OmniAICreator/Qiita-1.07M huggingface dataset:
わんたす　初めてのポートフォリオ[GO,PYTHON,GRPC,DOCKER]

# [wantas](https://www.wantas.net/search)

### **_ユーザーの"すぐ欲しい"実現アプリ_**

ユーザーが商品を検索し、位置情報から近くのコンビニを特定、商品を表示する。コンビニの商品は地域で差異があるためその点も考慮し、そのコンビニで販売されている商品のみを表示する。
横にグーグルマップも付けているのですぐ行くことができます。

### [github](https://github.com/biki-cloud/wantas)


# 使用技術

### プログラミング言語: バックエンド
* Python
* Go
* Shell
 
### プログラミング言語: フロントエンド
* Html
* Css
* Javascript

### データベース
* Sqlite

### CircleCI,GithubActions その他技術
* AWS
* Docker
* GRPC
* Jenkins
* Git
* Pytest

# 技術の選定理由
コンビニの位置情報や商品情報のスクレイピングをするためにPythonを選択しました。WebサーバーはGoのWebフレームワークであるGinで実装し、Goからメソッドを呼び出すようにpythonの機能を使用できるのでGRPCを選択しました。データベースは手軽に操作できるのでSqliteを使用しました。アプリ起動を簡単及び統括的にするためdockerを使用し、docker-composeで起動します。またCI/CDを実装するためにJenkins,AWSを使用しコード編集→git push→jenkinsが自動でテスト→テスト成功したらawsにデプロイという流れを実装しています。またほとんどの関数にテストコードを記述しています。
今後はログイン機能を追加し、SNSでシェアするような機能を付けていきたいと思っています。


# とりあえずローカルで使ってみる
### 起動方法
```shell
git clone git@github.com:biki-cloud/wantas.git
docker-compose up -d # 起動
```

### [**アクセス**](http://localhost:80/search)

### 終了方法
```shell
docker-compose down -v --rmi all # 終了
```

# テスト
```shell
# 起動
docker-compose up -d # 通常起動。エラーが出た場合は-dを--buildに変えてみる。

# pytestを使用してスクレイピング等が正常に動作しているか確認する
cd scrape_server
python3 util.py
# 上の出力をコマンドラインに貼り付け実行する。スクレイピングできるようにするために。
pytest -v -p no:warning

# webサーバとスクレイピングサーバを起動し、最終テストを行う。
# 別のセッション
cd scrape_server
python3 server.py

# 別のセッション
cd scrape_client
go run main.go

# 別のセッション
cd scrape_client
go test -v

# 終了
docker-compose down -v --rmi all # docker-compose終了時
```


# プロファイリング
### プロファイリングはcProfileを使用する。使用方法は以下の通り。
```python
import cProfile
import time

def f():
    print("start")
    time.sleep(2)
    print("end")

cProfile.run('f()')
```


### 出力結果
```text
start
end
         7 function calls in 2.003 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    2.003    2.003 <string>:1(<module>)
        1    0.000    0.000    2.003    2.003 time_test.py:5(f)
        1    0.000    0.000    2.003    2.003 {built-in method builtins.exec}
        2    0.000    0.000    0.000    0.000 {built-in method builtins.print}
        1    2.003    2.003    2.003    2.003 {built-in method time.sleep}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
```
MacからWindowsにSSH接続する方法

# はじめに

GPUを積んだWindows PCを購入したので、GPUを使ってディープラーニングやりたい。。。
けど、開発はショートカットキーに慣れているMacでやりたい。。。

ということで、MacからWindowsにSSH接続することにしました。

# 手順
ホスト(Windows)側の作業: H
ゲスト(Mac)側の作業: G

1. OpenSSHをインストール(H)
2. ssh時のデフォルトのターミナルをbashに変更(H)
3. sshd_configを編集(H)
4. sshキーを作成(G)
5. 公開鍵を登録(H)
6. 公開鍵の権限を変更する(H)
7. sshdを起動(H)
8. sshで接続(G)


# 1. OpenSSHをインストール(H)

`Windows + S` ボタンを押して、`optional features` と検索して、下図のような設定を開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241394/dc612a79-b4c3-a9d6-7105-f5e0a8d72046.png)


`Add a feature` を押して、`openssh`と検索すると、`OpenSSH Server`なるものが出てくると思うので、それをインストールする。


# 2. ssh時のデフォルトのターミナルをbashに変更(H)

管理者権限でWindowsPowerShellを開いて、以下のコマンドでデフォルトのターミナルをbashに変更する。必要なければやらなくても良い。
wslのインストールが済んでない場合は、[wslのインストール](https://docs.microsoft.com/en-us/windows/wsl/install-win10)を先に行う。

```powershell:デフォルトのターミナルの変更
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\bash.exe" -PropertyType String -Force
```


# 3. sshd_configを編集(H)

`C:\ProgramData\ssh\sshd_config`を好きなエディタで開いて、最後の2行をコメントアウトしておく。
これがあると、administratorsのグループのアカウントは`__PROGRAMDATA__/ssh/administrators_authorized_keys`を参照してしまう。

```config:sshd_config
# Match Group administrators
#        AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
```


# 4. sshキーを作成(G)

ゲスト側でターミナルを開いて、以下のコマンドでsshキーを作成する。
sshキーの作成方法の詳細については巷にあふれているので割愛。

```bash
ssh-keygen -t rsa -b 4096 -o -a 100
```

デフォルトの名前なら、`~/.ssh`内に秘密鍵`id_rsa`と公開鍵`id_rsa.pub`が作成される。


# 5. 公開鍵を登録(H)

ゲストで作成した公開鍵をホスト側に登録する。
その際、`id_rsa.pub`という名前を、`authorized_keys`に変更する。
`authorized_keys`の設置場所は、`C:\Users\{user_name}\.ssh`の中。


# 6. 公開鍵の権限を変更する(H)

公開鍵は、初期状態の権限だとssh接続ができないので、権限を変更する必要がある。
権限の変更は以下の手順で行う。

1. `authorized_keys`を右クリックして、`Property -> Security -> Advanced`という順に進む。
2. `Disable inheritance`をクリックする。
3. `Convert inherited permissions into explicit permissions on this object" when prompted`をクリックする。
4. SYSTEMとユーザーの権限を外す。




# 7. sshdを起動(H)

管理者権限でWindowsPowerShellを開いて、以下のコマンドを入力する。

```PowerShell
Start-Service sshd
```

起動状態の確認は、以下のコマンドでできる。


```PowerShell
Get-Service sshd
```


# 8. sshで接続(G)

ゲスト側でターミナルを開いて、以下のコマンドでssh接続を行う。

```bash
ssh user_name@host_ip_address
```

IPアドレスは、ネットワークのプロパティから確認できる。


# 追記: SSHがTimeoutする場合
SSH接続がタイムアウトする場合は、Windows側のポートが開いていない可能性があります。
ローカルのみで良いので、Firewallの設定から22番ポートを開けるようにして下さい。


# 参考

[How to SSH into Windows 10?](https://theitbros.com/ssh-into-windows/)
[Setting up OpenSSH for Windows using public key authentication](https://stackoverflow.com/questions/16212816/setting-up-openssh-for-windows-using-public-key-authentication)
[OpenSSH Server Configuration for Windows 10 1809 and Windows Server 2019](https://docs.microsoft.com/en-us/windows-server/administration/openssh/openssh_server_configuration)
Docker HubにPushする方法

# はじめに
似たようなコンテナを毎回作成するという非効率なことを行っていたので、Docker HubにPushしてみようと思い立った。

# 手順
手探りでやってみたところ、以下の手順でできた。
1. Docker Hubでリポジトリを作成
2. コンテナを作成
3. 作成したコンテナをコミット
4. docker tag
5. docker login
6. docker push

# 1. Docker Hubでリポジトリを作成
[Docker Hub](https://hub.docker.com/)で、**Create Repository**を押して、リポジトリを作成する。


# 2. コンテナを作成
Dockerfileを用意して好きなコンテナを作成する。
ここでは、docker-compose.ymlを使用してコンテナをビルドした。
ビルドに使用したファイルはGitHubで管理している。

# 3. 作成したコンテナをコミット
`docker ps`で起動中のコンテナの*CONTAINER ID*がわかる。
その*CONTAINER ID*を使用して、以下の文でコミットする。
`:<tag>`は入れても入れなくても良い。デフォルトだと、`:latest`となる。

```bash

docker container commit <container-id> <hub-user>/<repo-name>[:<tag>]

```

# 4. docker tag
下記コマンドを入力して、Imageにリポジトリ名をタグ付けする。

```bash

docker tag <image-id> <repo-name>

```

# 5. docker login

下記コマンドを入力後、ユーザー名とパスワードを入力してログインする。

```bash

docker login

```

ログイン成功したら、`Login Succeeded`と表示される。

# 6. docker push

下記コマンドを入力して、Docker Hubにpushする。

```bash

docker push <hub-user>/<repo-name>[:<tag>]

```

# 参考
https://docs.docker.com/docker-hub/repos/#pushing-a-docker-container-image-to-docker-hub
派遣企業のマージン率(ピンハネ率)を見ると、プログラマやWeb制作者ならフリーランスの方が良いと思う事について

####派遣会社は、原則としてマージン率などの情報を公開しなければならない

人材派遣会社は、法律（労働者派遣法第23条第5項）によって、事業所ごとにマージン率（ピンハネ、手数料）を関係者に情報提供しなければならないと決められています。ですので、ネット上でマージン率を公開している派遣会社が多いです。

・[マージン率等の情報提供について](https://www.mhlw.go.jp/content/11600000/000634367.pdf)

上の資料によると、マージン率の他に、派遣労働者の人数、派遣料金平均額、派遣労働者の賃金平均額などの情報も公開しなければいけないようです。

ちなみにマージン率は、以下の計算式です。

（派遣料金平均額 - 派遣労働者の賃金平均額） / 派遣料金平均額


####マージン率の平均は、大体30%台前半ぐらいか

それで公開されているなら、マージン率（ピンハネ率）はどんなもんかと気になってネットで調べてみたんですが、派遣企業によってバラつきがありますが、大体20%半ばから30%後半って感じですね。

「企業名　マージン率　公開」、「企業名　労働者派遣法第23条第5項」みたいなキーワードで検索すれば、各派遣企業が公開している情報が出てきます。

例えば、大手の人材派遣企業のパソナやパーソルだと、以下のページに事業所ごとにマージン率などの情報を公開しています。

#####参考サイト

・[パソナ、労働派遣事業に関わる情報提供](https://www.pasona.co.jp/company/haken-notice.html)
・[パーソル、労働者派遣法に基づく派遣事業の情報公開について](https://www.persol-pt.co.jp/news/2021/08/13/5260/)

####プログラマやWebデザイナなら、フリーランスの方が良いのではと思う

派遣労働者は様々な業界や職種にいます。大きな分類で言うと、肉体労働、軽作業などのブルーカラーや、オフィス業務のホワイトカラーなどですね。

そして、プログラマやWeb制作を行うWebデザイナなどの高度スキルを持った派遣社員もいます。

プログラマの派遣社員の時給は、大手人材派遣のサイトで見ると、大体１０００円台後半から２０００円台ぐらいです。

２０００円台という時給は派遣社員の中では高いかもしれませんが、プログラマとしてはかなり安いと思います。

そしてこれだったら、エージェント企業に営業活動を頼んで、フリーランスのプログラマをやった方が、全然高い収入が得られると思います。

フリーランスなんで、雑務を自分でやったり、交通費などの経費は自分もちだろうけど、正直、プログラマの経費なんてあまりかからないだろうし、勉強するためのプログラミング本とかは自分で経費精算できるなどのメリットもありますしね。

なにより、派遣企業の30%のマージン率と比較したら、フリーランスのエージェント企業のマージン率はかなり低いと思うので、やっぱり収入面を考慮したら、フリーランスの方が圧倒的に良いと思います。

もちろん、派遣企業のマージンからは福利厚生とかも支払われているんだろうけど、それを差し引いてもフリーランスの方が全然良いよねと。

あと、派遣社員のプログラマよりも、フリーランスのプログラマの方が高いスキルを求められるのはあると思います。これは、フリーランスがそれだけ高収入をもらっているんだから当然ですね。

それでも、派遣社員のプログラマやWebデザイナなどのWeb制作をやっていて、ある程度のスキルと実務経験があるなら、フリーランスでも十分いけると思うんですよね。それだったら、フリーランスをやらない手はないとも思います。

スキルが少し足りない派遣の人も、少し努力してスキルアップしてフリーランスになれば、大きく収入アップする可能性があると思うので、頑張ってみる価値はあると思うのです。

もしもプログラマやWebデザイナをやっている派遣社員の人で、フリーランスという選択肢を考えていなかったら、一度チャレンジしてみるのもいいのではと個人的には思います。ちゃんとしたスキルを持っている人なら、あまり自分のスキルを安売りしないでねって感じなのです。


#####参考サイト

・[派遣エンジニアからフリーランスのITエンジニアに？両者の違いなど](https://it-jog.com/itfreelance/dispatchtofreelance)
・[どれだけの実務経験があれば、フリーランスのITエンジニアとして働けるか？](https://it-jog.com/itfreelance/howlongworke)
・[フリーランスのITエンジニアの初めてのエージェント選び](https://it-jog.com/itfreelance/agentselect)
XAMLが難しいと思ったのでXAMLとは何かについて調べた

# XAMLとはなにか
この記事は[こちらのXAML Tutorial]()を翻訳・要約・補足した内容となります。
他記事からの引用も有るため多少原文にはない内容がありますので、
原文を当たりたい方はリンクまでお願いします。原文も平易な英語で読みやすいです。

リンクのチュートリアルはプラットフォームに依存しないXAMLの解説を狙いとしていますが、
本記事はWPFアプリケーション開発を前提としたXAMLの解説としている点ご理解ください。

Xamarin、UWPはわかりません。

# 対象読者
XAMLに少し触ったがいまいち理解が深まらない初学者。


# XAMLとは何か？
### XAMLとは「宣言型マークアップ言語」
* 宣言型：
    * SQLなどのように「何を得たいか」を定義するプログラム構成。
『SQLのクエリは「どのようなデータが欲しいか」を宣言し、どのテーブルから先に見るかなどといった「いかにしてデータベースにアクセスするか」という命令・手続きには関与しない。』
([Wikipedia-宣言型プログラミング](https://ja.wikipedia.org/wiki/宣言型プログラミング)引用)

### XAMLはインスタンスを階層的に表現する言語
* 『XAMLはオブジェクトのインスタンスを組み立てるための言語』

私はHTMLのような感覚で書いて本質が理解できずつまづきましたが、XAMLの本質はC#コード(のようなもの)で
インスタンスを初期化してプロパティ値を入れる作業に過ぎません。
実際、XAMLをC#コードで表現することもできます。

ですからXAMLは『UI特化言語ではない』のです。([XAML入門](https://www.slideshare.net/okazuki0130/xaml-71503072)より引用)

```csharp
//下記２つの記述は同じ結果となる。
//XAML
<StackPanel> 
	<TextBlock Text = "Welcome to XAML"/>
	<Button Content = "Ok" Height = "20"/> 
</StackPanel> 

//C#コード
         //StackPanelインスタンス作成
         StackPanel stackPanel = new StackPanel();
         this.Content = stackPanel; 
			
         //TextBlockインスタンス作成
         TextBlock textBlock = new TextBlock(); 
         textBlock.Text = "Welcome to XAML Tutorial"; 
         stackPanel.Children.Add(textBlock);  
			
         //Buttonインスタンス作成
         Button button = new Button(); 
         button.Content = "OK"; 
         button.Height = 20; 
         stackPanel.Children.Add(button); 
```
(XAML vs C#Code)[https://www.tutorialspoint.com/xaml/xaml_vs_csharp_code.htm]

# XAMLの利点
初期のGUIフレームワークでは、アプリケーションの見た目と振る舞いを分けて考えることができませんでした。
C#やVB.netなど、GUIと振る舞いを同じ言語で作成していたため、UIとそれに付随する動作の両方を実装するには開発者の手間がかかっていました。
![xaml_framework.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634043/f393d61e-2152-3129-6a3f-c9486e56dc4a.jpeg)
XAMLでは、デザイナーのコードからビヘイビアを分離することが非常に簡単です。
そのため、XAMLプログラマーとデザイナーは並行して作業することができます。XAMLのコードは非常に読みやすく、理解しやすい。
![gui_frameworks.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634043/11e6d16f-1177-9974-bed5-ccec4e3d3b6c.jpeg)

# XAMLはどうやって動いているのか？
前述の通り、XAMLはXAMLプロセッサによってコードにコンパイルされ、C#コード同様.NETコンパイラーがアプリを構築します。
![xaml_works.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634043/f89d6e93-3c79-b584-41d5-100d44778a7d.jpeg)


# まとめ
今回はXAMLとは何かという概要だけ説明しました。
要点としては「インスタンスを階層で表している」に尽きると思います。

これを理解していれば究極MSDNを参照しながら開発してゆけることでしょう。究極は、ね………。
いちばんシンプルなdat.gui.jsの使い方

dat.guiは基本的には、データを動的に変更する為に使う。

リロードして、っていうのが面倒な場合に使える。使い方がややこしいのが多いので、シンプルに作る

#cdnで設置

```html:html.html
<!-- CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.1/dat.gui.min.js" type="text/javascript"></script>
```

#読み込み

```js:html.js
window.onload = function() {
  const gui = new dat.GUI();
};
```

これで右上になんか矩形が表示されるはず。

#デバッグボタンを作ってみる

```js:html.js
window.onload = function() {
  const gui = new dat.GUI();
  gui.add( params, 'debug' ).onChange( function() {
  	if(params.debug){
  		console.log("DEBUG MODE")
  	}else{
  		console.log("NORMAL MODE")
  	}
  });
};

let params = {
    debug: true
};
```

これで矩形にチェックボックスが入っているのが表示されるはず
実は上記が一番かきたかった事、使い方を説明するのに、変数モリモリのサンプル提示してんの多すぎて、ちょっとカロリーが必要そうだった。

#数値の変更のサンプルを作る

```js:html.js
window.onload = function() {
    const gui = new dat.GUI();
    gui.add(params, 'debug').onChange(function() {
        if (params.debug) {
            console.log("DEBUG MODE")
        } else {
            console.log("NORMAL MODE")
        }
    });
    gui.add(params, 'x', -100, 100).onChange(function() {

    });
    gui.add(params, 'y', -100, 100).onChange(function() {

    });
    gui.add(params, 'z', -100, 100).onChange(function() {

    });
};

let params = {
    debug: true,
    x: 0,
    y: 0,
    z: 0
};
```

-100とか100って書いてる部分は最小値と最大値で設定しておくと、指定の範囲だけ動かせるUIもついてくるので制限なくても適当な値を入れておくと、簡易に変更しやすい。
設定ファイル（postgresql.conf）の内容が消えてしまった（全行"~"になった）→ディレクトリに注意！

# 環境
|||バージョン|
|:--|:--|:--|
|Host OS|Windows 10 Home|21H1(OSビルド:19043.1165)|
|Virtual Machine|WSL2|-|
|Remote OS|Ubuntu| 20.04.2 LTS (GNU/Linux 5.4.72-microsoft-standard-WSL2 x86_64)|
|Database|PostgreSQL|12.7 (Ubuntu 12.7-0ubuntu0.20.04.1)|
|Database GUI|無し|-|
|Docker|無し|-|

#何があった？
WSL2・DBeaverでPostgreSQLを使えるようにすべくPostgreSQLの設定ファイル(postgresql.conf)の書き換えを試みたところ、下スクショのような状態に書き換わってしまった。
<img width="700" alt="1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498057/725db7fe-401d-95bb-cc29-685e98e202c4.png">

本来であれば「listen_address='local host'」を含むコード（？）が何行も表示されるはずが、全て「~」に書き換わってしまった。

#何をしたらこうなった？
設定ファイル「/etc/postgresql/12/main/postgresql.conf」をテキストエディタviで開き
「listen_addresses = 'localhost'」を「listen_addresses = '*'」に変更しようと試みた。

【やったこと】
WSL2のターミナルで
下記を実行
umesan@MyComputer:~$ **cd** /etc/postgresql/12/main
↓
下記を実行
umesan@MyComputer:/etc/postgresql/12/main$ **vi** postgresql.conf
（viでpostgresql.confファイルを開く、の意）
↓
（WSL2ターミナルにpostgresql.confテキストファイルが展開される）
「/listen」+Enerで listen_addressを検索
↓
iキーを押下し、コマンドモードから編集モードに変更。
↓
「localhost」を「*」に書き換えようとするも違う箇所の文字を変えてしまい
取り急ぎ [こちら](https://docs.oracle.com/cd/E19253-01/816-3946/editorvi-12/index.html) を参考に「ファイルの変更を保存しないでviを終了」を実行しようと、「:q!」を実行した。
↓
再び「vi postgresql.conf」で設定ファイルを開くと、上記画面のように「~」が羅列されている状態になっていた。

#原因
再度「vi postgresql.conf」で開いた際のディレクトリが「umesan@MyComputer:~$」になっていた。
「umesan@MyComputer:/etc/postgresql/12/main$ 」に移動しないといけない！

#対処方法
WSL2ターミナルで「umesan@MyComputer:~$ **cd** /etc/postgresql/12/main」を実行してから
「umesan@MyComputer:/etc/postgresql/12/main$ **vi** postgresql.conf」を実行したら
目的のファイルを正しく開くことができた。

#おわりに
今どのディレクトリにいるのかという位置を、きちんと確認・意識しよう。

#未解決事項
ディレクトリ「umesan@MyComputer:~$」で「vi postgresql.conf」を実行したことで
このディレクトリ内に上記スクショのファイル（画面左下にNew Fileとある）を「postgresql.conf」として新規作成してしまった状態、になっている？？
であれば、そのファイルを削除する必要がある？？
Djangoで指定したhostにリダイレクトさせるmiddlewareを書いた(middleware入門)

## TL;DR
タイトル通り、Djangoで指定したhostにリダイレクトさせるmiddlewareを書こうとした。
[見つかった記事](https://qiita.com/shinsaka/items/ed8527cb1c83519d8f43)が5年以上前のものと古く、Django v1.10以降のmiddlewareの書き方に対応していなかったので対応する書き方で書き直した。
middlewareで呼ばれる関数については[こちら](https://qiita.com/shirakiya/items/1503eaffe81f91af5b9d)がわかりやすかった。

## 0.問題設定
例えばDjangoアプリケーションをherokuでデプロイし(`example.herokuapp.com`)、お名前.comでドメイン(`example.com`)を取得したとする。
この場合、`example.com`からアクセスできるようになったとしても、
`example.herokuapp.com`でも同様にアクセスできる。
これをリダイレクトするために、middlewareを作って対応した。

## 1.middleware作成
今回、プロジェクト直下に`/lib/middleware.py` という名前でmiddlewareを作成。
クラス名は`RedirectCorrectHostname` とする。
これを`/{プロジェクト名}/settings.py`に追加する。

``` settings.py
MIDDLEWARE = [
    ...
    'lib.middleware.RedirectCorrectHostname',
    ...
]
```

ここがハマりポイント1で、v1.10より、`MIDDLEWARE_CLASSES`から`MIDDLEWARE`に変わった。([参考](https://qiita.com/Brokenumbrella/items/cd0ce6c36ec70f9dcf43#2-settingspy-%E3%81%AE-middleware_classes-%E3%82%92-middleware-%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%BE%E3%81%99))

## 2.リダイレクト先のhostを指定

``` settings.py
CORRECT_HOST = '127.0.0.1:8000'
```

これを`settings.CORRECT_HOST` として参照できる。

## 3.リダイレクトのmiddlewareを定義

``` middleware.py
from django.http import HttpResponsePermanentRedirect
from django.conf import settings


class RedirectCorrectHostname(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        return response

    def process_view(self, request, view_func, view_args, view_kwargs):
        # settingsにCORRECT_HOSTがあるか
        if not getattr(settings, 'CORRECT_HOST', None):
            return None
        # settings.CORRECT_HOSTとアクセスしているhostが一致するか
        if request.get_host() == settings.CORRECT_HOST:
            return None

        # 一致しなかった場合、settings.CORRECT_HOSTにリダイレクト
        return HttpResponsePermanentRedirect(
            '{scheme}://{host}{path}'.format(scheme=request.scheme,
                                             host=settings.CORRECT_HOST,
                                             path=request.get_full_path()))

```

詳しい説明は[こちら](https://qiita.com/shirakiya/items/1503eaffe81f91af5b9d)を参照してほしいのだが、各メソッドについて軽く説明。

### `__init__`
サーバ起動時に1度だけ呼び出される。初期化。必須。

### `__call__`
リクエスト毎に呼び出されるメソッド。必須。
`self.get_response()` 実行前に処理を記述することで、view関数適用前に実行する共通処理を定義することができる。

### `process_view`
view関数を呼び出す直前にhookされるメソッド。
- 実行されるview関数
- そのview関数への引数（例えばurlsで定義したパラメータ）

を知っている。
返り値として
- None
- HttpResponse

のどちらかを返す必要があり、ここにリダイレクト処理を定義した。

## おわりに
リダイレクト処理なんて何人もやってそうなのに意外と記事がなかった......

## 参考
[元記事](https://qiita.com/shinsaka/items/ed8527cb1c83519d8f43)
[middlewareの関数](https://qiita.com/shirakiya/items/1503eaffe81f91af5b9d)
[MIDDLEWARE_CLASSESからMIDDLEWAREへ](https://qiita.com/Brokenumbrella/items/cd0ce6c36ec70f9dcf43#2-settingspy-%E3%81%AE-middleware_classes-%E3%82%92-middleware-%E3%81%AB%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%BE%E3%81%99)
【GooglePlayアプリ内課金】テスト

# 概要

google play アプリ内課金のテストをする方法についてまとめました。

# テストの方法

アプリ内課金をお金を使わずにテストをする方法ですが、ライセンステスター登録することで利用することができます。
使用する google アカウントをアプリ毎に設定することでライセンステスターになります。

https://developer.android.com/google/play/billing/test

# 通常の決済とテストの決済で異なること

支払方法は、「テストカード、常に承認」と「テストカード、常に不承認」のみとなる

* 承認とは、クレジットカードのオーソリゼーションのことだと思われ、決済が成功する/失敗するカードになります
    * 初回購入時や継続時の決済失敗時の挙動を確認することができます
* 通常の決済手段は使用することはできなくなります

定期購入の更新間隔が変わります

* 迅速にテストできるように更新間隔が変わります
    * 更新以外にも、アカウント保留期間や猶予期間、無料試用期間なども短縮されます
* 特定回数更新後に自動で解約されます
* https://developer.android.com/google/play/billing/test#subs

一時停止などの設定が適用されない

* 一時停止や再購入を利用するかの商品ごとの設定が適用されない
    * ライセンスアカウントでは購入に関する全ての設定が有効となる
    * ただし、この仕様は明記されていないため、変更されるかも（設定可能になるかも）

# 購入時の挙動

1. 購入時の実装には特に特殊なものは必要にはなりません
1. 商品選択後の OS の支払方法選択画面でテスト用の支払い手段を選択します
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224207/0ce7f3e4-1f2f-0180-066b-515b77a96910.png)
1. 支払方法を変更するには、「テストカード、常に承認」をタップします
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224207/61229f46-06fb-c361-f517-13d7392dae61.png)
1. 解約は、通常の操作と同様です。ただし、前述したとおり、手動で解約しなくても自動で解約されます。


# その他ポイント

* テスト支払かどうかは、purchaseType で判断する
* ライセンステスターとなる google アカウントは、インストールアカウントに対してかかる
    * 変更したい場合、他のアカウントを全てログアウトすることで切り替えられる
アジャイル開発（スクラム）の概要

「スクラム開発って知ってる？」と質問されたときに、エンジニアであれば概要くらいは答えたいですね。

自身の備忘録のためにもメモしておきます。

スクラムとは、アジャイル開発手法の1つであり、ユーザーに価値のあるプロダクトを素早く生み出すための軽量なフレームワークです。

1990年初頭にKen Schwaber(ケン シュエイバー)とJeff Sutherland(ジェフ サザーランド)により生み出されました。また、スクラム開発におけるチームメンバーの役割、イベント、作成物などのルールは「スクラムガイド」で定義されています。

![スクリーンショット 2021-08-31 0.50.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1955975/e4229e35-6f86-c498-0293-30a1a92155ea.png)
参考画像：https://scrumguides.org/docs/scrumguide/v2020/2020-Scrum-Guide-Japanese.pdf

「スクラムガイド」に記載されているルールはそれほど多くはありません。
簡単にまとめると「3つの役割、5つのイベント、3つの作成物」これだけです。

![スクリーンショット 2021-08-28 23.17.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1955975/2e782b45-4b15-68f1-c879-388c6ff81958.png)
参考資料：https://eh-career.com/engineerhub/entry/2020/05/12/103000

※ アジャイル開発の第一人者「吉羽龍太郎さん」と対談している「エンジニアHub」の記事を参考に、上記図は作成しています。

スクラムチームは以下3つの役割（ロール）で構成されます。

・プロダクトオーナー（PO）
　プロダクトの価値を最大化することに責任を持つ。

・スクラムマスター（SM）
　スクラムの考え方とノウハウを全員に理解してもらうことに責任を持つ。

・開発者
　プロダクトに関するあらゆる側面（品質、計画、専門性）に責任を持つ。

スクラム開発は「スプリント」と呼ぶ一定期間の中で、以下5つのイベントを実施します。

![スクリーンショット 2021-08-29 0.06.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1955975/5631d441-a337-6b83-47ae-2ba44fd9ac95.png)

スクラムガイドでは以下3つ作成物が定義されています。

・プロダクトバックログ
　プロダクトの開発や改善に必要なタスクが優先度順に並べられた一覧。

・スプリントバックログ
　スプリントで実施するプロダクトバックログの項目を実行可能なタスクに詳細化したもの。

・インクリメント
　プロダクトゴールへ向けた具体的な成果物のこと。



ざっくりと概要をまとめておくと、上記のような感じです。

スクラム開発に関して、別のブログで初心者向けに、簡単に解説しています。

⬇︎⬇︎⬇︎⬇︎【初心者向け】はじめてのスクラム開発⬇︎⬇︎⬇︎⬇︎
![5分でわかる スクラム概要-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1955975/e32fa3db-d065-9930-771c-11d928206c03.png)

https://do-scrum.com

お時間ある時に是非ご確認のください。エンジニアとして必ず1アップしていただけると思います。

今日はこのへんで！
SQLインジェクションの脅威と対策(安全なウェブサイトの作り方)

#はじめに
本記事は[安全なウェブサイトの作り方](https://www.ipa.go.jp/security/vuln/websecurity.html)(IPA)の第一章「ウェブアプリケーションのセキュリティ実装」に解説を加えた記事です。
長いので分割して投稿しています。
画像はすべて上記サイトから引用してます。


#目次
1.[概要](#概要)
2.[脅威](#脅威)
3.[根本的解決](#根本的解決)
4.[保険的対策](#保険的対策)

#SQLインジェクション
##概要
データベースに対してSQL文を送信できるwebアプリケーションに対して、不正なSQL文を組み立てて、本来得られない情報を入手・改ざんする攻撃
![000083692.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/686089/15b0f1b0-8025-0e6b-dbe5-c8d8db85a385.png)


##脅威
・非公開情報の閲覧
・DB内情報の改ざん・消去
・不正ログイン
・ストアドプロシージャを利用したOSコマンドの実行

##対象
DBを利用するすべてのwebアプリケーション
特に個人情報を扱うDBを利用するwebサイト

##根本的解決
###(1)プレースホルダの利用
変数を入れたい場所にプレースホルダと呼ばれる仮の記号を入れておきます。後で、実際の変数とプレースホルダを紐づけます(バインド)。これによって、SQL命令に関わるような「特殊文字」は無効化されます。

<details><summary><font color="red">具体的な手順</font></summary><div>
下記の様なSQLを実行するPHPプログラムがあったとします。```$name```はユーザーが入力する値です。

```php
$sql = "SELECT * FROM user WHERE name='$name'";
```
ここに、不正な文字を入力すると、不正な命令が実行されてしまいます。
下の例では、「';DELETE FROM user--」という値を入力することで、userテーブルの値がすべて削除されてしまう不正なSQL文になってしまいました。

```php
$sql = "SELECT * FROM user WHERE name='';DELETE FROM user--";
```
これを防ぐために、```$name```をプレースホルダと呼ばれる文字列(```?```や```:name```や```@name```など)で仮置きしておきます。
※OracleのSQLやPL/SQLではコロン（:）、他の言語ではクエスチョンマーク(?)等を使用します。[[2](#参考文献)]
下の例では、```:name```を使います。

```php
$sql = "SELECT * FROM user WHERE name=:name";
```
最後に、プレースホルダと実際の変数をバインドします。
バインドに用いる関数は言語ごとに存在しますが、今回は```PHP```の```bindValue()```関数を使います。

```php
bindValue(':name', $name, PDO::PARAM_STR);
```
これで、```:name```と``$name``がバインドされました。

参考文献：[[1](#参考文献)]　
－－－－－－－－－－－－－－－－－－－－－－－－－－－－
</div></details>

###(2)エスケープ処理の実装
前述のプレースホルダを用いずに、文字列連結によりSQL文の組み立てを行いたい場合は、可変の値を定数(リテラル)として埋め込むことが推奨されます。
具体的には、入力値が文字列型の場合は特別な意味を持つ記号をエスケープ処理する(たとえば、「'」→「''」、「\」→「\\」等)、入力値が数値型であれば、数値型へのキャストを行って、数値リテラルであることを確実にするといった対処が挙げられます。
###(3)ウェブアプリケーションに渡されるパラメータにSQL文を直接指定しない
HTTPのhiddenパラメータ等からSQL文を直接受け取るようになっているといくらでも改ざんが可能です。やめましょう。
##保険的対策
###(1)エラーメッセージを隠す
エラーメッセージは開発者・利用者にとっては有用な情報である一方、攻撃者にとっても有用な情報になります。また、エラーメッセージに漏洩してはいけない情報が表示されてしまうこともあるため、エラーメッセージは書き換えて表示する・そもそも表示させないことが推奨されます。
###(2)DBに適切な権限の設定
想定されるSQL文に必要な権限のみ与えましょう。

#リンク
1.SQLインジェクション←本記事
2.OSコマンド・インジェクション
3.パス名パラメータの未チェック/ディレクトリ・トラバーサル
4.セッション管理の不備
5.XSS(クロスサイトスクリプティング)
6.CSRF(クロスサイト・リクエスト・フォージェリ)
7.HTTPヘッダ・インジェクション
8.メールヘッダ・インジェクション
9.クリックジャッキング
10.バッファオーバーフロー
11.アクセス制御や認可制御の欠落


#参考文献
[1]フルスタックエンジニアのノウハウ『[プレースホルダとは何か？SQLインジェクション攻撃を回避せよ！](https://blog.senseshare.jp/placeholder.html)』
[2]@kamihork『[Oracleにおけるバインド変数の利用](https://qiita.com/kamihork/items/948f451761a484ddfb27)』
power automate desktopを使ってFTPファイルを監視(無料で定期実行)

#定期実行方法
多分、一番気になっているであろう定期実行から説明します。
無料プランのpower automate desktop単体だと定期実行ができません...
(有料だとできるとか、ちょっと調べきれてません)

なので、PadFlowRunというコマンドからpower automateの処理を起動できるソフトを使い対応します。
こちらを作成された「YOSHIO」さんに感謝です！

※使い方とダウンロードは下記を参照
https://yoshio-akz.blogspot.com/2021/03/stream-deck-power-automate-desktop.html

こちらのコマンドをタスクスケジューラーに登録することで自動化ができます。





#作成したもの
前回はWordpressを題材にしましたが、今回もWordpressです。
Wordpress一番上の階層にあるindex.phpがありますが、こちらのファイルは非常にハッキングの対象になりやすいことは既に使ったことがある方ならご存知かと思います。
こちら監視して変更があれば管理者にメールを送信します。
(index.phpを更新することは、ほぼ無いため決め打ち)

##最初にPCに作成して置くフォルダ
![folder_pc.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280639/6d023c5c-5a37-9134-3419-5feeb7075f72.jpeg)
このフォルダの監視用.phpとダウンロードしてきたindex.phpが違えばハッキングされている可能性があるのでメールを送信します。


##処理の流れ
1. 起動時にbackupフォルダにある監視用のPHPを読み取りテキストを変数に格納する
2. 現在の日付を取得してきて、backupフォルダに日付の更新フォルダを作成する
3. FTPに接続してきてindex.phpやテーマファイルをダウンロードする
4. ダウンロードしてきたindex.phpのテキストを変数に格納する
5. ①と④で読み取ったindex.phpを比較して違ったら管理者宛にメールを送信する。

![main.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280639/5988ae10-d26c-3f2a-e35d-84b723c0beea.jpeg)

![folder.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280639/d66944bf-8ad9-c492-8e7b-c61ab06a2e2c.jpeg)


![ftp.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280639/e7bfee70-e298-4a54-6662-1cf317836727.jpeg)

#感想
今回はテーマファイルのバックアップを取るなどはしていませんが日付フォルダの中にテーマファイルもダウンロードすれば、より実用性が上がる気がします！
定期実行ができるようになるだけで一気に便利になるのでぜひ、使ってみてください。
bootStrap で使われる言葉の説明

## 最初に来る短縮文字の候補
|省略名|正式名(推測)|説明|使用例|
| --- | --- | --- | --- |
|d-|display||d-flex|
|g[ ]-|gutter|ガター(カラムの左右の空白)|g-1|
|h-|height|高さ|h-25|
|m[ ]-|margin|マージン|m-1|
|p[ ]-|padding|パディング|p-1|
|w-|width|幅|w-25|
|bg-|back ground|背景カラー|bg-primary|
|fs-|font size|フォントの大きさ|fs-1|
|fst-|font style type|フォントのタイプ|fts-italic|
|fw-|font weight|フォントのウェイト|fw-bold|
|lh-|line height|行間|lh-1|

g, m, pに関しては、以下のオプションを付け加えることができる

|短縮名|正式名(推測)|説明|使用例|
| --- | --- | --- | --- |
|(空白)||全方位|m-1|
|s|start|左|ms-1|
|t|top|上|mt-1|
|e|end|右|md-1|
|b|bottom|下|mb-1|

## 配置用語の説明
|言葉|説明|
| --- | --- |
|none|非表示|
|inline|要素が平ぺったく横に並んでいく|
|inline-block|blockとinlineの中間|
|block|要素が横までいっぱいに広がり、縦に並んでいく|
|table|
|table-cell|
|table-row|
|flex|要素のサイズから、自動で並べる(引用参照)|
|inline-flex|
> bootStrap5 document : Display property
> https://getbootstrap.jp/docs/4.4/utilities/display/
>【CSS】displayの使い方を総まとめ！inlineやblockの違いは？
> https://saruwakakun.com/html-css/basic/display
> Bootstrap4に用意されているクラス【flex編】
> https://webnetamemo.com/coding/bootstrap4/201906138320
> Bootstrap Flexの使い方を紹介！
> https://www.fenet.jp/dotnet/column/language/6541/
Windows11のタスクバーに好きなショートカットを置くには？

Windows 11 InsiderPreview Dev build 22000.132をISOからインストールしています。

以前と変わり、好きなショートカットをタスクバーに置けるというわけではなく、実行ファイルじゃないとだめになりました。
例えば、ThisPCをタスクバーに置けなくなるなど(動作としてはエクスプローラーなので。)
これは友人からの相談で、自分としてはエクスプローラー左枠のThisPCを右クリックで新しいウィンドウとして開けばええやん って感じなのですが彼なりの利便性があるらしいので検証。
(このようにショートカットではPin to taskbarが存在しない)
![Screenshot 2021-08-30 165704.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242735/dfce088c-1f2f-abe6-f3f8-2f5b965e298b.png)


結局のところ、エクスプローラーにIDを渡せばThisPCは開かれますから、それだけをするものをアプリ枠で認識させればいいわけですね。
windows10でもアプリケーション扱いでしたので察していましたが、cmd.exeに対するコマンドショートカットを自分で書いたbatは以下のようにタスクバーにピン止め可能です。(画像のはbatteryreport)
これをexplorer.exeに対して作ればいいだけですね。
今回はPCを開きたいだけ(加えて、エクスプローラーのデフォルトをPCにしてある)なので、固有IDの指定も必要なく....   C:\Windows\System32\cmd.exe /c explorer file: 
をショートカットに作ればいいだけです。
アイコン変更、管理者権限で起動(必須?)、最小状態で起動などのオプションをお好みで付与すればOK。
(そうして作ったショートカット、もといパッチファイルではPin to taskbarが存在しますね。)
![Screenshot 2021-08-30 165737.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242735/2a80141d-de48-90ea-643e-06894b62d3e2.png)
もう少し手間がかかると思いましたが、大したことせずにできてしまいました。
これを応用すれば仕様変更で追加できなくなったものは大抵追加できると思うので覚えておいて損はないでしょうね。
Azure Functions でキューをトリガーに関数を動かして、結果を別のキューに積む(JavaScript)

# ベースの関数と設定

```ts:src/FunctionName/index.ts
import { AzureFunction, Context } from '@azure/functions';

export const functionName: AzureFunction = async (context: Context, inputMessage: any): Promise<void> => {
    context.log(`function name ran with ${inputMessage}`);

    const data = doSomething();
    context.bindings.outputMessage = {data: data};
};
```

```json:src/FunctionName/function.json
{
  "bindings": [
    {
      "name": "inputMessage",
      "type": "queueTrigger",
      "direction": "in",
      "queueName": "input-queue-name",
      "connection": "AzureWebJobsStorage"
    },
    {
      "name": "outputMessage",
      "type": "queue",
      "direction": "out",
      "queueName": "output-queue-name",
      "connection": "AzureWebJobsStorage"
    }
  ],
  "scriptFile": "../dist/FunctionName/index.js"
}
```

- トリガー側のキューの `name` は何でも良いはず。
- トリガー側のキューの `direction`　は `in` を指定する。
- バインド側のキューの `name` は `context.bindings.<name>` のようにバインドするときのプロパティ名になる
- バインド側のキューの `direction`　は `out` を指定する。
- `connection` の `AzureWebJobsStorage` はおまじないだと思っている。 `AzureWebJobsStorage` は環境変数で定義されているべきもの。- `connection` を指定しない場合でも、`AzureWebJobsStorage` を探す仕様らしい。
ちなみに、ローカル環境では `local.settings.json` に環境変数を定義するようドキュメントに記載があるが、なぜか読み込めなかったため、 `docker-compose.yml` の `enveronment` に定義した。


# テスト

テストするとき、関数の第1引数 `Context` は以下のようにモジュール化することができます。(jest を使っている前提です。)
[参考](https://tobim-dev.medium.com/setup-and-write-jest-unit-tests-for-azure-functions-in-typescript-ea6e72db9277)

```ts:tests/modules/defaultContext.ts
import { Context } from '@azure/functions';

export const context =  ({ log: jest.fn() } as unknown) as Context;
```

# Azure Queue Storage の CLI

キューを作ったり削除したりするコマンドは、 `az storage queue ...`
https://docs.microsoft.com/ja-jp/cli/azure/storage/queue?view=azure-cli-latest

キューに値を入れたり削除したりするのは、 `az storage message ...`
https://docs.microsoft.com/ja-jp/cli/azure/storage/message?view=azure-cli-latest#az_storage_message_delete

```zsh:sample
// message は base64 にエンコードする必要がある

az storage message put --content $(echo -n "test"|base64) -q input-queue-name --connection-string=""
```
話をわかりやすく伝えるために意識するべき4のポイント

話をわかりやすく伝えるための４つのポイントは以下の通りです。

1. 結論から話す
1. 専門用語を使わない
1. 聞き手の反応を見る
1. あいまいな言葉を使わない


上記のポイントを一つずつ説明して行きたいと思います

### 結論から話す
結論を最初に話すことで、聞き手は「最終的なゴールはそこなのか」ということを念頭に説明を聞くことができるので、説明の内容が頭に入ってきやすくなります。
うまく人に伝えることができない人の多くは、頭の中に思いついた順に話をする傾向にあります。そのため最後に結論を持ってくる場合がほとんどです。こういった話し方にならないよう気をつけましょう。


###専門用語を使わない
話す相手がその専門用語を理解しているのであれば使用しても良いですが、その用語を知らない場合は聞き手の頭の中で「？？？？」という状態になるでしょう。
誰にでもわかる言葉で話すと聞き手はスムーズに話を理解することができるでしょう。

###聞き手の反応を見る

聞き手が話についていけてない状態にも関わらず話をドンドンと進めてしまうと、相手に伝えたいことの２割程度しか理解してもらえず一時間ほど話した機会が無駄になってしまうこともあります。そういった状態を避けるためにも、相手が今自分の話を理解できているのか？相槌や話の間などを聞き反応を伺いましょう。

###あいまいな言葉を使わない
曖昧な言葉は人によって捉え方や定義がことなるため、作業を進めていく中でお互いの認識にズレが生じる場合があります。そういった危険を回避するためにも、曖昧な表現で会話を進めることはやめましょう。できる限り具体的な数字や認識のずれが起きづらい具体的な表現で話をしましょう。
縦持ちデータをグループ化して横持ちデータに変更

わけあって下記のようにgroupをグループ化して、指定した列をユニーク数分新しいカラムを用意して横持ちに変換する必要がでてきました。

![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428239/592a9f64-35f6-a1f7-097a-210f12c2fdb8.png)

今回の例でいう新しいカラムを作成するcol1,col2以外はグループ毎に同じデータが入っているという条件です。

メソッドでさくっとできるかなと考えていたのですが、
メソッドではできなさそうなので、自分で処理を作成しました。
同じコードを書く必要が出てきたときに備えてメモを残しておきます。


```py:データ用意
import pandas as pd
data = {
    'group': ['A', 'A', 'A', 'B', 'B', 'C', 'C', 'C', 'C' ],
    'other_col': ['AA', 'AA', 'AA', 'BB', 'BB', 'CC', 'CC', 'CC', 'CC' ],
    'col1': list(range(1, 10)),
    'col2': list(range(11, 20))
    }

df = pd.DataFrame(data)
df
```

この状態でグループ化をすると下記のようなに
グループ名とindexesの辞書を作成してくれることを利用して今回はデータ整理をしました。

```py:groups
df.groupby('group').groups
# {'A': [0, 1, 2], 'B': [3, 4], 'C': [5, 6, 7, 8]}
```

```py:変更するコード
groups = df.groupby('group').groups
new_df = pd.DataFrame()

# 項目+Numberで項目を作成したいカラム
target_columns = ['col1', 'col2']

# それ以外のカラムを作成
other_column = [x for x in df.columns.tolist() if x not in target_columns]
max_length = 0

for group, indexes in groups.items():
    
    # 最後に並び替えをさせるのに最大作成された項目数を記録
    length = len(indexes)+1
    if length > max_length:
        max_length = length

    columns = other_column.copy()
    values = df[other_column].iloc[indexes[0], :].values.tolist()
    
    for target_column in target_columns:
        columns += [f'{target_column}_{x}' for x in range(1, length)]
        values += df.iloc[indexes, :][target_column].T.tolist()
        
    record_df = pd.DataFrame([values], columns=columns)
    new_df = pd.concat([new_df, record_df], axis=0)

# ここまでで必要な情報はそろっているが並びがよくないので以下で整理
sorted_columns = other_column.copy()
for target_column in target_columns:
    sorted_columns += [f'{target_column}_{x}' for x in range(1, max_length)]

sorted_df = new_df[sorted_columns]
sorted_df
```
SNS認証導入時に検討したこと【OAuth】

「メールアドレスによる会員登録」と「SNS認証による会員登録」を両方実装する場合に、
要件や設計で検討する点がいくつかあったのでメモ。

## アイコン（アバター）についての検討事項

SNS認証による会員登録時、アイコン画像もSNSから自動取得して保存ケースが多いと思う。その場合、

■当該SNS上でユーザーがアイコンを変更した場合、自サイトのアイコンも自動変更するのか（ログイン時に毎回照合をかける等して）
■会員登録時のみSNSから取得し、その後はユーザーによる手動での変更のみ可能とするのか
■qiitaのように「更新ボタン」を設けて、そのボタンを押すと当該SNS上のアイコンと同期するようにするのか

などの検討が必要。↓下記画像はqiitaのアイコン変更画面
![スクリーンショット 2022-01-21 164058.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985396/1cf66a67-eb6e-231d-e2ee-76aab3bd89de.jpeg)

## パスワードについての検討事項

SNS認証による会員登録の場合、通常はパスワードを登録させない。
（パスワードの入力を省けることがSNS認証の大きなメリットの１つなので）
しかしメールアドレスによる会員登録の場合、基本的にパスワード入力は必須。
そのため、「パスワードを持っている会員」と「パスワードを持っていない会員」が存在することになる。

そしてこの場合、ログイン画面での「パスワードをお忘れの方はこちら」が問題となる。
SNS認証による会員登録をしたユーザーが、自分がSNS認証で登録したということを忘れ
パスワードを持っていると勘違いして「パスワードをお忘れの方はこちら」を選択する可能性があるからだ。

ユーザーはログイン前なので、ユーザーによって画面を出し分けるようなことは当然できない。
そのため、

■メールアドレスを入力させて、パスワードの登録が無いこと、もしくはSNS認証での登録であることをエラーメッセージ等で伝えるのか
■そのままパスワードを登録させ、「SNS認証による会員だがパスワードも持っている」状態にするのか

などの検討が必要。


## ユーザー名をunique（重複不可）にする場合の検討事項
メールアドレスによる会員登録しかないサイト（SNS認証無し）であれば
ユーザー名は単純にuniqueにしておけば問題ないと思う。

しかしSNS認証を導入する場合、ユーザー名をuniqueにすると
「Aさんのtwitterのユーザー名」と「Bさんのgoogleでのユーザー名」が一致してしまい
BさんがSNS認証での会員登録が出来なかった、といったケースもありうる。

かといってuniqueにしなければ同じ名前のユーザーが存在することになってしまうので、

■twitterのように「uniqueではないユーザー名＋uniqueな識別番号」といった表示形式にするのか
■SNS認証での会員登録時に、ユーザー名を変更できるようにするのか

などの検討が必要。


## emailをuniqueにする場合の検討事項

emailをuniqueにしない場合、１つのメールアドレスでアカウントがいくつでも作成できてしまう。

emailをuniqueにした場合、
例えば、Aさんがメールアドレスによる会員登録でGmailアドレスを入力して登録。
後日、メールアドレスで会員登録したことを忘れ、google認証（上記と同じGmailアドレス)で会員登録しようとしたら
「そのメールアドレスは既に登録されています」とエラーになってしまう。

→これは仕方ないので、エラーメッセージの内容だけ検討すればいいかもしれない。
【Laravel】ユーザー専用ページのURLを直接入力された場合への対処【認可】

環境：Laravel 6.2
※※初学者が書いています。おかしな部分など有りましたら教えて頂けると嬉しいです。※※※


## ケース１：ログインユーザー専用のページを非ログインユーザーに見られたくない

### 状況

ログインユーザー専用のページ「ページA」があるとする。
ページAは非ログインユーザーには見られたくない。

ページAへのリンクを`@auth` と `@endauth`　で囲めば
リンクはログインユーザーにしか表示されなくなる。
しかし、ページAのURLをブラウザのアドレスバーに直接入力すれば
非ログインユーザーでもアクセスできてしまう。

### 対処方法

ルーティングでmiddleware('auth') を使用。

```php:
Route::get('/hoge', 'FugaController@hoge')->name('fuga.hoge')->middleware('auth');
```

こうすれば、非ログインユーザーがexample.com/hogeを直接入力すると自動でログイン画面へ遷移する。
ログインが完了すると自動でexample.com/hogeへ遷移する。


## ケース２：特定のログインユーザー用のページを他のログインユーザーに見られたくない
### 状況

会員Bさんのマイページ「マイページB（hoge.com/users/ユーザーID）」があるとする。
マイページBは、非ログインユーザーにも、Bさん以外のログインユーザーにもには見られたくない。

ページの構成にもよるが、
マイページBへのリンクを`@if( Auth::id() == $user->id )` と `@endif`で囲むなどして
Bさん以外にはボタンが表示されないようにすることはblade上の記述で可能。
（該当ページにユーザーモデルのインスタンス$userを渡している前提です。）
そしてケース１のようにmiddleware('auth') を使えば
非ログインユーザーが直接アドレスを入力した場合への対処もできる。

しかし、別のログインユーザーが直接アドレス（hoge.com/users/ユーザーID）を入力した場合は表示ができてしまう。

### 対処方法

下記の１か２の対処方法が考えられる。

##### 1. Laravelの認可機能（PolicyやGate）を使用する。
参考サイト様↓

https://reffect.co.jp/laravel/laravel-gate-policy-understand


##### 2. 編集画面を表示させるためのコントローラー内にユーザー照合の処理を記述する。
```php:web.php
Route::get('users/{user}', 'UserController@edit');
```
```php:UserController
public function edit(User $user)
    {
         if (Auth::id() != $user->id) {
            return abort('404', 'not found');
        }else{
            return view('users.edit', [
            'user' => $user,
            ]);
        }
    }
```

小規模サイトであればどちらを使っても問題なさそう。
中規模以上のサイトなら認可はまとめて管理したほうが保守や運用上において好ましいので
PolicyやGate、もしくはそれに代わるオリジナルのクラス等を使ったほうがいいかもしれない。
React 入門道場　~特殊なJSXの構文~

# はじめに
Reactを学習していてJSXについて理解が浅かったので今回簡単ににまとめてみました。
今回は特殊なJSXの構文についてまとめてみました。


# 特殊なJSXの構文

自分自身、下記の３つのコードを説明するほど理解していなかったので要点をまとめることにします:point_up:

①

```jsx
return (
   <p>サンプルコードです</p>
   <p>特殊なJSXの構文の学習</p>
)
```
②

```jsx
return (
  <React.Fragment>
   <p>サンプルコードです</p>
   <p>特殊なJSXの構文の学習</p>
  </React.Fragment>
)
```

③

```jsx
return (
  <>
   <p>サンプルコードです</p>
   <p>特殊なJSXの構文の学習</p>
  </>
)
```

# 前提知識

1.JSXは必ず階層構造でなければならない！
最上位のコンポーネント或いはタグを書く必要がある。

これを踏まえた上で、先程の①では最上位のコンポーネント或いはタグの記載がありません。

__Error__

```jsx
return (
   <p>サンプルコードです</p>
   <p>特殊なJSXの構文の学習</p>
)
```
かと言って、別の方法としてdivタグで囲ってしまうのいけません！HTMLではdivタグはなんの意味もなく、必要ないものにはdivタグを使用しない方が良い:point_up:
理由として、HTMLで出力した際にCSSの崩れが無くなるからである。

```jsx
   return (
　　　　　　　 <div>
      <p>{caption}</p>
      <img src={samplePath} alt={'sample写真'} />
    </div>
  )
}

```

# divタグを使わない方法　-React.Fragment-

```jsx
return (
  <React.Fragment>
   <p>サンプルコードです</p>
   <p>特殊なJSXの構文の学習</p>
  </React.Fragment>
)
```

先程、divタグは使いたくない時の対処法としてReact.Fragmentを使います！
先程挙げた条件としての、JSXは必ず階層構造でなければならない！っていうのもクリアしてますね:smiley:

最終的にHTMLに出力した際は、React.Fragmentがなかった事として出力される。

```jsx
return (
   <p>サンプルコードです</p>
   <p>特殊なJSXの構文の学習</p>
)
```

__並列で使用する際はReact.Fragmentを使いましょう__

# 　-React.Fragmentは省略可能-


```jsx
return (
  <React.Fragment>
   <p>サンプルコードです</p>
   <p>特殊なJSXの構文の学習</p>
  </React.Fragment>
)
```

```jsx
return (
  <>
   <p>サンプルコードです</p>
   <p>特殊なJSXの構文の学習</p>
  </>
)
```

# よくある間違い

```jsx
return (
  <>
   <p>サンプルコードです</p>
  </>
)
```

よく間違った使い方として、タグが一つしかないのにReact.Fragmentをしてしているケースがある。これだと意味をなさない。pタグひとつで良い。React.Fragmenは二つ以上で並べる時、階層構造のルールあるので上位のタグを使用する際に使用する:hugging:

>[日本一わかりやすいReact入門【基礎編】JSXの記法](https://www.youtube.com/watch?v=gLbTluYSb_U&t=699s)
JOINの簡単な例

###目的
JOINがどうも感覚的につかめ無い。

理由は、「テーブルを結合する」という漠然とした内容しか頭に無いから。

つまり、LEFT JOINとRIGHT JOINの区別がつかない。等価結合もわからなくなる。

なんでそうなるか。理由は簡単。

自分で結合しねぇからだよ！！

エンジニアとして経験が浅いものは、ちょっとした時間でできる実験を怠ってしまう。ベテランさんにしたら数分でできることが調べながらで数時間かかることもあるから。いや、場合によっては数日かかることもある。（これほんま）

ということで、とりあえずすごーく簡単だけど、自分で実験した結果を記録し、記憶の定着を図る。

###概要

```
生徒テーブル
ID、名前、性別コード
```

```
性別テーブル
ID、性別名
```

生徒テーブル.性別コードと性別テーブル.IDをくっつけて、


```
名前、性別名
例）
山田花子 女
```

の結果を出力したい。


「nullの場合は・・・」って説明がテキストにはあるので、生徒テーブルと性別テーブルいずれにもnullのレコードを準備することにする。後は読んで理解するよりも「自分で」やって見たほうが体感的に理解できる。


###テーブル作成とデータの挿入

####生徒テーブルの作成とデータ挿入
```sql
create table students (id int not null primary key, name varchar(10) not null, sex int );

// 秒で間違えたので修正
alter table students modify id not null auto_increment primary key;

// データ挿入
insert into students (name, sex) values ('山田花子', 1),('北川景子', null), ('内村照子', 1), ('田中角栄', 2);

mysql> select * from students;
+----+----------+------+
| id | name     | sex  |
+----+----------+------+
|  1 | 山田花子 |    1 |
|  2 | 北川景子 | NULL |
|  3 | 内村照子 |    1 |
|  4 | 田中角栄 |    2 |
+----+----------+------+
4 rows in set (0.01 sec)
```

####性別テーブルの作成とデータ挿入

```sql
mysql> create table sex (id int not null auto_increment primary key, sex varchar(10) );
Query OK, 0 rows affected (0.06 sec)

mysql> insert into sex (sex) values ('男'),('女'),('不明');
Query OK, 3 rows affected (0.02 sec)

mysql> select * from students;
+----+----------+------+
| id | name     | sex  |
+----+----------+------+
|  1 | 山田花子 |    1 |
|  2 | 北川景子 | NULL |
|  3 | 内村照子 |    1 |
|  4 | 田中角栄 |    2 |
+----+----------+------+
3 rows in set (0.00 sec)

```

####中身の確認と、なんとなく結果をイメージする

```sql

mysql> select * from students;
+----+----------+------+
| id | name     | sex  |
+----+----------+------+
|  1 | 山田花子 |    1 |
|  2 | 北川景子 | NULL |
|  3 | 内村照子 |    1 |
|  4 | 田中角栄 |    2 |
+----+----------+------+
mysql> select * from students;
+----+----------+------+
| id | name     | sex  |
+----+----------+------+
|  1 | 山田花子 |    1 |
|  2 | 北川景子 | NULL |
|  3 | 内村照子 |    1 |
|  4 | 田中角栄 |    2 |
+----+----------+------+

```

この2つを見比べて、どんなJOINをしたらどんな結果になるかを、「間違っていてもいいので」想像する。ここが一番大事ですな。間違っててもええねん。そんなん、やってみりゃええねん。そしたらちょっとずつわかってくんねんって！


###結果はっぴょ～

```sql
mysql> select a.name, b.sex from students a left join sex b on a.sex = b.id;
+----------+------+
| name     | sex  |
+----------+------+
| 山田花子 | 男   |
| 北川景子 | NULL |
| 内村照子 | 男   |
| 田中角栄 | 女   |
+----------+------+
4 rows in set (0.00 sec)

mysql> select a.name, b.sex from students a right join sex b on a.sex = b.id;
+----------+------+
| name     | sex  |
+----------+------+
| 山田花子 | 男   |
| 内村照子 | 男   |
| 田中角栄 | 女   |
| NULL     | 不明 |
+----------+------+
4 rows in set (0.00 sec)

mysql> select a.name, b.sex from students a, sex b where a.sex = b.id;
+----------+------+
| name     | sex  |
+----------+------+
| 山田花子 | 男   |
| 内村照子 | 男   |
| 田中角栄 | 女   |
+----------+------+
3 rows in set (0.00 sec)

```


###自分解説
LEFT JOIN は左テーブル優先。
RIGHT JOINは右テーブル優先。
等価結合はinner JOINとかいうやつ。よくわからんけど、nullはでてこん。

上の説明で後は試したほうがわかりやすいですね。
Firestoreから取得するデータの上限を決める。（.limit(to: Int)）

#今回の内容
* Firestoreから大量にデータを取得しない様にする為に調べた内容について

#コードと簡単解説

* 取得するデータの上限を決めるときは、`.limit(to: Int)`を使用します。

* 下のコードだと、最大で50個までデータを取得することになります。

```Swift:
db.collection(uid).order(by: "省略", descending: true).limit(to: 50).addSnapshotListener {

          ~~~~~~~~~省略~~~~~~~~~~

 }
```

#終わり
ご指摘、ご質問などありましたら、コメントまでお願い致します。
https-portalの自動更新が失敗する件の対応

■結論
https-portalの永続化ディレクトリの[該当ドメイン]/production/中身を全部DEL（一応中身はローカルに退避したが私のケースでは不要だった）ってコンテナ再起動で直った。

永続化ディレクトリは
https://qiita.com/github0013@github/items/71c44d7bf4faf63c1956#ssl-certificates%E3%82%92docker%E3%83%9B%E3%82%B9%E3%83%88%E4%B8%8A%E3%81%AB%E4%BF%9D%E5%AD%98
これです。

■以下自分用メモ

お客さんからサイト接続時に警告が出ていると連絡があってサイトを確認した。
確かにブラウザで第三者が～～という警告が出ているのを確認。
調べたところSSL証明書の期限が切れているのが原因だと判明。
はて、https-portalを使用して自動更新されていたはずだが何が起こった？

```
docker-compose ps
```
でコンテナの状態を確認したらコンテナのステータス自体はUPになっていて正常。

自作した証明書の自動更新スクリプトを手動で実行するも変化なし。
ホストから

```
wget http://[webコンテナのIPアドレス]
```

するとindex.htmlが正常に取得出来た。WebServerには問題は無いようだ。
一旦全コンテナを落として状態を確認する事にする。

```
docker-compose down
```

問題無くコンテナを終了できた。
再度コンテナを立ちあげる

```
docker-compose up 
```


http-portalのコンテナ起動時に永続化したディレクトリ内に「chained.crt」が無いとエラーが出ていた。
自動更新スクリプトを何度叩いても同じ。
確認すると指定ディレクトリにこのファイルは無かった。

https-portalの最新のdocker imageをdocker pull

```
docker pull steveltn/https-portal:1
```

この状態でdocker-composeの再起動

```
docker-compose down
docker-compose build
docker-compose up
```

するも変化なし。

永続化したディレクトリ内から[ドメイン名]/production/内に残っていたファイルをローカルに退避して全削除。
その後https-portalのコンテナを含むdocker-composeを再起動。

無事「chained.crt」が再作成されて起動にhttps-portalのコンテナが正常に起動し、SSLでのサービスも再開された。
転職の面接で残業が少ない会社を判断する方法【IT/Web系職種】

昔と違って残業まみれの会社は少なくなり働きやすい企業が増えたように感じていますが、それでもたまに残業が、、、という話を未だに聞きます。

求人票に書いてあることと実際の面接で違うじゃんということは昔から多々あることですが、ある程度面接の中で残業がどのぐらいありそうか、多そうか、少なそうかは判断が可能かと思います。

今回はそれを備忘としてまとめておきます。

なお、こちらの内容は<a href="https://jc2.xsrv.jp/article/57475/">残業が少ない会社、どう見極める？【エンジニア・クリエイターの転職事情】</a>を一部参考引用しておりますので予めご了承ください。

なお、エンジニア派遣系企業に勤務することは想定しておりませんのでご了承ください。

<h2>残業時間の計測方法を確認する</h2>

最近は求人票に平均残業時間が記載されていることが大半かと思います。
転職エージェントさん経由であろうと求人媒体経由であろうと必ず（多分）求人票に書いてあるはずです。
例えば、月平均残業時間20時間と書いてあったとして、その残業時間の計測方法がどのようなものかは確認しましょう。

会社全体（残業の無い管理部門等も含まれているケースがある）の残業時間だけが記載されているケースもあり、実際に働く部署とは大きく異なるケースもあるため、必ず当該部門の残業時間なのかどうか確認しましょう。

<h2>なぜ残業が少なく済むのかを聞いておく</h2>
仮に求人票に記載してある残業時間が本当だったとして、それが少ないものであったとしたならば、今度はなんで残業が少なく済むようにできたのかというその背景を確認しましょう。

質問の際に、どういった取り組みを行って残業が減らせたのか（あるいは少なく済んでいるのか）というその理由を聞くことで、どういった体制を取っているのか、働くということに対するスタンスはどういった感じの企業なのか、そういったことまでわかるので、残業体質かどうかはそれだけである程度判断ができます。

実際問題開発の現場で残業を減らそうと思ったら、うまく仕組みにしてまわせている状況だと思うので、聞いておいて損は無いでしょう。

<h2>残業のことを転職面接で聞いて問題無いのか？</h2>

たまに残業のことを面接中に聞くのは大丈夫なのかと思う方もいるようなのですが、聞き方さえ間違えなければ問題ありません。

あからさまに働きたくない感が出ているケースでは論外となりますが、メリハリをつけて働きたい方が大半だと思うので、そういった趣旨で質問すれば大丈夫でしょう。

勉強する時間も確保したいですしね。

とりあえず今どきは残業で苦しむことも少ないかと思いますが、参考まで過去やってたことを記載させていただきました。
【PostgreSQL】n個以上紐付きのあるデータをSQLで抽出する【window関数】

2つのテーブル間に1:多のリレーションシップあり、その中でn個以上紐付きのあるデータを洗い出したいことがあった。
その時にwindow関数を使用したので、忘れないように記録しておく。

## やりたいこと

複数のデータの紐付きがあるものを抽出する。
今回は注文を複数している顧客のidを抽出してみる。

## テーブル構成

- 顧客のデータを保存するusersテーブル
- 注文データを保存するordersテーブル
- usersとordersは1:多の関係（userは複数のorderを持つことができる）

## SQL

集約関数だと複数の行が一つにまとまってしまうため、window関数を使用して対象のデータを洗い出す。

```sql
SELECT
  u.id
FROM
  users AS u
  INNER JOIN (
    SELECT
      count(id) OVER (PARTITION BY user_id) AS counter,
      *
    FROM
      orders
  ) AS o ON u.id = o.user_id
  AND o.counter > 2;
```

## 参考

https://lets.postgresql.jp/documents/technical/window_functions/1
【pandas】DataFrameであるカラムを改行文字で分割して行番号をつける

## 改行（またはその他の区切り文字）で行を分割したい
元となるDataFrameは

```
   id     name
0   1  あい\nうえお
1   2    かきくけこ
2   3    サシスセソ
```
このようなもので、このnameの値を改行コードで分割する。

```
   id   name  line_number
0   1     あい            1
1   1    うえお            2
2   2  かきくけこ            1
3   3  サシスセソ            1
```

このように`id=1`の行を2つに分割し、先頭から1,2... と行番号をつけていく処理をしたかった。

## コード

```python:split.py
import pandas as pd
import numpy as np
import re

df = pd.DataFrame(
    data={
        'id': [1, 2, 3],
        'name': ['あい\nうえお', 'かきくけこ', 'サシスセソ']
    }
)

df["name"] = df['name'].str.split(pat="\n") # 改行コードでレコードを配列化
df = df.explode('name', ignore_index=True) # explodeで配列を行に分割
df['group_id'] = df.groupby(['id']).ngroup() # グループ毎にIDを降る
df['original_index'] = df.index # 一行ずつにindexを設定
df["line_number"] = df.groupby(["id"])["original_index"].rank(ascending=True) #グループ内で上の業からrank付けをしていく
df['line_number'] = df['line_number'].astype(np.int64) #float型で入っているのでintに直す

df = df.drop('original_index', axis=1) # 生成過程の不要な列を削除
df = df.drop('group_id', axis=1) # 生成過程の不要な列を削除
print(df)
```

処理は各行にコメントを付けた通り。
ただ結構回りくどい処理をしているのでもっとスッキリできるやり方があれば教えていただきたいです。
【Unity】AssemblyDefinition.asmdefをスクリプトから作成する

# はじめに

スクリプトからAssemblyDefinitionを生成する機会があったのですが`AssemblyDefinitionAsset`は`sealed`になっていますしコンストラクタもなくて`AssetDatabase.CreateAsset`で生成できませんでした。

```cs
 AssetDatabase.CreateAsset(new AssemblyDefinitionAsset());
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175167/4db6b0bd-d703-2d6e-f92d-ed0323b45847.png)

# jsonから生成する

幸い`AssemblyDefinitionAsset`の中身はjsonなので`File.WriteAllText`から生成することにしました。


```asmdefの中身.json
{
    "name": "SampleAssemblyDefinition",
    "rootNamespace": "",
    "references": [],
    "includePlatforms": [],
    "excludePlatforms": [],
    "allowUnsafeCode": false,
    "overrideReferences": false,
    "precompiledReferences": [],
    "autoReferenced": true,
    "defineConstraints": [],
    "versionDefines": [],
    "noEngineReferences": false
}
```

```JsonAssemblyDefinition.cs
public class JsonAssemblyDefinition
{
    public string name;
    public string rootNamespace;
    public object[] references;
    public object[] includePlatforms;
    public object[] excludePlatforms;
    public bool allowUnsafeCode;
    public bool overrideReferences;
    public object[] precompiledReferences;
    public bool autoReferenced;
    public object[] defineConstraints;
    public object[] versionDefines;
    public bool noEngineReferences;
}
```

スクリプトから生成します。

```EditorCreateAssemblyDefinition.cs
using System.IO;
using UnityEditor;
using UnityEngine;

public class EditorCreateAssemblyDefinition : Editor
{

    [MenuItem("Qiita/CreateAssemblyReference")]
    private static void CheckSelectObject()
    {
        JsonAssemblyDefinition json = new JsonAssemblyDefinition()
        {
            name = "JsonAssemblyDefinition",
        };

        string text = JsonUtility.ToJson(json);
        string path = Path.Combine(Application.dataPath, "Scenes/EditorCreateAssemblyReference/JsonAssemblyDefinition.asmdef");

        File.WriteAllText(path, text);
        AssetDatabase.ImportAsset("Assets/Scenes/EditorCreateAssemblyReference/JsonAssemblyDefinition.asmdef", ImportAssetOptions.ForceUpdate);
    }
}

```

無事生成できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175167/06921642-7313-5288-c509-65af2c5c3803.png)

# おわりに

正式にアセットを作成できる方法があるのかもしれませんが、この方法だとほかのクラスのアセットでもスクリプトから作成できると思います。

# 参考

[AssemblyDefinitionAsset](https://stephenhodgson.github.io/UnityCsReference/api/UnityEditorInternal.AssemblyDefinitionAsset.html)
AtCoderログ：0097 - ABC 216 に参加しました

この記事は 2021年08月29日(日)21:00-22:40 に開催された [AtCoder Beginner Contest 216 (ABC 216)](https://atcoder.jp/contests/abc216) に参加した記録です。

https://atcoder.jp/contests/abc216

# 結果
- 得点：600 (=100+200+300)
- 時間：73:51 (= 53:51 + 05:00 × 4)
- 順位：4773 / 7377
- パフォーマンス：311
- レーティング：379 → 368 (-11)
- 段級位：9級

![2021-08-30_abc216.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1589302/9184e789-01bd-7f0b-574f-15a599ae946e.png)

# 詳細
|問題|得点|時間|提出コード|復習|
|:--:|:--:|:--:|:--:|:--:|
|[ABC 216 A](https://atcoder.jp/contests/abc216/tasks/abc216_a)|100|14:28(2)|[○](https://atcoder.jp/contests/abc216/submissions/25416922)|[AtCoderログ：0098](https://qiita.com/RoadLynton/items/a42013a870778a8f84f9)|
|[ABC 216 B](https://atcoder.jp/contests/abc216/tasks/abc216_b)|200|18:18|[○](https://atcoder.jp/contests/abc216/submissions/25419443)|[AtCoderログ：0099](https://qiita.com/RoadLynton/items/faa58e3479f57cd5536a)|
|[ABC 216 C](https://atcoder.jp/contests/abc216/tasks/abc216_c)|300|53:51(2)|[○](https://atcoder.jp/contests/abc216/submissions/25433704)|[AtCoderログ：0101](https://qiita.com/RoadLynton/items/b31ccf2806e239c34014)|
|[ABC 216 D](https://atcoder.jp/contests/abc216/tasks/abc216_d)|400|(2)|---|---|
|[ABC 216 E](https://atcoder.jp/contests/abc216/tasks/abc216_e)|500|---|---|---|
|[ABC 216 F](https://atcoder.jp/contests/abc216/tasks/abc216_f)|600|---|---|---|
|[ABC 216 G](https://atcoder.jp/contests/abc216/tasks/abc216_g)|600|---|---|---|
|[ABC 216 H](https://atcoder.jp/contests/abc216/tasks/abc216_h)|600|---|---|---|

# 感想
- A, B, C まで完答できました。
- A の float の扱いにつまづいてしまい、WA を2回も出してしまいました。ひどい。
- B は素直に回答して正解。
- C の解法はすぐに思いついたものの、ちょっとした勘違いから WA を2回も出してしまいました。
- レーティングははじめてのダウンでした。ちょっと残念ですが、こんなに WA を出していればもっともですね...気を引き締めなくては
 
# リンク
## 前後の記事
- 前の記事 → [AtCoderログ：0096 - AGC 014 A](https://qiita.com/RoadLynton/items/82e9b4ceb9e9384130f5)
- 次の記事 → [AtCoderログ：0098 - ABC 216 A](https://qiita.com/RoadLynton/items/a42013a870778a8f84f9)