OmniAICreator/Qiita-1.07M huggingface dataset:
CodiMDのDBまわりのTips

だいぶ前からDockerでCodiMDのコンテナをたてて適当にチームで使ってみているが、パスワードを忘れたり、そのせいでノートのURLがわからなくなって行方不明になったりしたのでその解決方法とかのTips

# DBへの接続

※以下に記載するコマンドはすべて、DBにPostgreSQLを使用している場合を前提とします。
コマンドを読み替えればMySQLの場合などでも同様に実行できるはずです。

`docker exec`などでDBのコンテナへ入り、`psql <db_name> <db_user_name>`と実行（docker-compose.yamlの環境変数を見ればわかる）
プロンプトが`hackmd=# `に変われば接続成功
`\d`と打てばテーブル一覧が表示される。

```
hackmd=# \d

              List of relations
 Schema |      Name      |   Type   | Owner  
--------+----------------+----------+--------
 public | Authors        | table    | hackmd
 public | Authors_id_seq | sequence | hackmd
 public | Notes          | table    | hackmd
 public | Revisions      | table    | hackmd
 public | SequelizeMeta  | table    | hackmd
 public | Sessions       | table    | hackmd
 public | Temp           | table    | hackmd
 public | Temps          | table    | hackmd
 public | Users          | table    | hackmd
```

## PostgreSQLの注意点

* 大文字が含まれているテーブル名やカラム名はダブルクォーテーションで括らないと正しく実行できません
* コマンドの最後にはセミコロンが必須です

# ノート一覧を取得したい

CodiMDには管理者ユーザのようなものはいないため、画面上からではDBに存在するノート一覧を確認する術が無く、DBを直接叩くしかない。
これを行うことで行方不明になってしまったノートを救うことができる。

```
hackmd=# SELECT title,shortid,alias FROM "Notes";

          title          |  shortid  |     alias     
-------------------------+-----------+---------------
 aaa                     | rJJMNlqVE | 
 Supported YAML metadata | HyLa2l5EV | yaml-metadata
 aaa                     | S1Ad7x9VV | 
 hoge                    | QxfVfPXMx | 
```

ノートのURLは`http://<CODIMD_HOST>:<PORT>/eGf30HxvQA-9iUqFgWe0ew`のようなやたら長いURLになっているが、おそらく後ろのハッシュみたいな文字列はDBに保存されていない。
ではどうすれば各ノートのURLがわかるのかと言うと、単純に`shortid`の値をハッシュみたいな文字列の代わりに打ち込めばよい。
例えば、上記の`hoge`には`http://<CODIMD_HOST>:<PORT>/QxfVfPXMx`でアクセスできる。
また、`content`というカラムの中にノートに記載した文章が入っており、これを使うことでなにかしらWebアプリケーションを作れそうな気もする。

# ユーザ一覧を取得したい

ノート一覧と同じく、ユーザ一覧もDBを直接叩く以外に確認方法は無い。
これを行うことで「ユーザ名忘れた」に対応することができる。

```
hackmd=# SELECT email FROM "Users";

       email       
-------------------
 user1@dummy.co.jp
```

実在しないメールアドレスでも登録できるため、誰がどのユーザ名なのかをリスト化したノートを作っておくと更に安心かもしれない。
本当にしっかり使うつもりならLDAPとかGitHubなどと連携するべきな気はする。

# 特定のユーザのパスワードを初期化したい

CodiMDでサインインできなくなるとそのユーザしか知らないノートが行方不明になったり、過去に共有したノートのURLを再度教えてもらう必要があるなど、割と致命的な事態になりかねない（一応上に記載した方法で迷子のノートの復旧は可能だが）
これは実はCodiMDのコンテナ（DBの方ではない）の中にツールが用意されており、それを実行すれば指定したユーザのパスワードを強制的に変更することができる。

`docker exec`などでCodiMDのコンテナへ入り、以下を実行

```
./bin/manage_users --reset <user_name>
```

例えば、`user1@dummy.co.jp`ユーザのパスワードを変更したい場合は以下のように実行する。

```
./bin/manage_users --reset user1@dummy.co.jp
```

変更したいパスワードの入力を求められるので、適当に入力すれば完了
これで「パスワード忘れた」にも対応することができる。
LaradockでLaravelの環境構築するもMysqlが立ち上がらない!

**開発環境**
---
- macOS Mojave 10.14.4
- Laravel6.(php7.3以上)
- Mysql8.

---
**環境構築:参考記事**
[初心者でもLaradockでLaravelの環境構築をエラーなしで行おう！（Mac ver）](https://qiita.com/mukae_tech/items/24709085948a6d707da3)

---


初心者なりに環境構築で詰まったところを共有します。
記事を参考に進めていく途中

`docker-compose up -d nginx mysql phpmyadmin`

mysqlが立ち上がらない
Docker再起動しても立ち上がらない!
困った！

エラー内容

```
Recreating laradock_mysql_1            ... error
ERROR: for mysql  Cannot start service mysql:
driver failed programming external connectivity on endpoint 
laradock_mysql_1(aa02fd7a36f486c3c62720a98798b7625cd855e49b5c4ef25b26a425e1299e0c): 
Error starting userland proxy: listen tcp 0.0.0.0:3306: bind: address already in use
```


3306が既に使われているってことか?

ググる...


[Laradock公式](https://laradock.io/documentation/#change-the-mysql-version)にこんな記述が

<img width="936" alt="スクリーンショット 2020-01-19 23.37.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425644/e6be16d9-57fc-6de6-e63f-e861cc088cee.png">



docker-compose.ymlの中のports欄を消せばよいとのお告げ



```docker-compose.yml
### MySQL ################################################
    mysql:
      build:
        context: ./mysql
        args:
          - MYSQL_VERSION=${MYSQL_VERSION}
      environment:
        - MYSQL_DATABASE=${MYSQL_DATABASE}
        - MYSQL_USER=${MYSQL_USER}
        - MYSQL_PASSWORD=${MYSQL_PASSWORD}
        - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
        - TZ=${WORKSPACE_TIMEZONE}
      volumes:
        - ${DATA_PATH_HOST}/mysql:/var/lib/mysql
        - ${MYSQL_ENTRYPOINT_INITDB}:/docker-entrypoint-initdb.d
      ports:
        - "${MYSQL_PORT}:3306"
      networks:
        - backend
```

ports: - "${MYSQL_PORT}:3306"
これを削除してみる...


再度`docker-compose up -d nginx mysql phpmyadmin`

<img width="315" alt="スクリーンショット 2020-01-19 23.43.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425644/ac769381-8e83-34a4-0092-c97f8755ed0c.png">

できた！ありがとうLaradock公式！！
APIGateway+Lambda+ServerlessFramework（言語：Python&Golang）の色々。offlineモードとかバイナリアップロードとか

##はじめに
APIGateway＋Lambdaを使って、RESTfullなAPIによるPDF生成APIをデプロイメントする時に色々とハマったので、その時の備忘録を記事にしました。

基本はServerlessFrameworkを使ったデプロイメントの自動化を目指して作業をしていった内容をまとめています。

##APIGatewayでメディアタイプの追加
まず、APIGatewayのバイナリレスポンス対応がどういうフローになるかを説明しておきます。

- 【リクエスト】
ブラウザ → (jsonリクエスト） → APIGateway → Lambda

- 【レスポンス】
Lambda → （ファイルを文字列：Base64変換） → APIGateway → （文字列をデコード:Base64変換） → ブラウザ

上記のようにLambdaからはBase64変換されたバイナリデータの文字列が返却される事が期待されています。Lambdaで返却する時の処理として、ここがまずポイントになります。

###APIGatewayのバイナリサポート周辺
APIGatewayでPDFファイルを直接返す場合、APIGatewayでバイナリサポートを有効にする必要がある。

バイナリサポートは、GUIのAWS Consoleで言うと
![API_Gateway.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61212/d92d805a-ccbc-e722-ed43-45c9bca8f26b.png)

まず、ここにメディアタイプを追加する必要がある。
これをServerlessFrameworkのPluginで対応させる場合は以下のPluginを使って対応可能。

- [serverless-apigw-binary](https://serverless.com/plugins/serverless-apigw-binary/)

ちなみに、すごく紛らわしいが[serverless-apigwy-binary](https://serverless.com/plugins/serverless-apigwy-binary/)というPluginもある。これは後ほど説明するが、この↑バイナリメディアタイプを設定するものではないので、要注意。

## 統合リクエストの設定（英語名：Integration）
結構ハマった所。通常のServerlessFrameworkで生成されるテンプレートを使ってデプロイすると、統合リクエストは「Lambdaプロキシ統合の使用」にチェックが入った状態でデプロイされます。

![API_Gateway_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61212/596f1942-cf23-d69e-631d-9fd6b691b2fe.png)

ここを外すと、「統合レスポンス（英語名：IntegrationResponse）」が変更できるようになります。この時、統合リクエストのマッピングテンプレートは、マッピングテンプレートをどう使えば良いか習熟している場合は普通にそこを設定すれば良いのだろうけど、良くわからない場合は「テンプレートが定義されていない場合（推奨）」を選択しておきつつ、設定されてるマッピングテンプレートを全て削除しておくのが良いと思います。

##参考リンク
- [Lambda+API Gateway入門。画像のDL](https://xp-cloud.jp/blog/2018/07/12/3876)
ラズパイ＋VNCで外から監視するカメラシステム

###サマリー
#####ラズパイとVNCを使って簡単に監視カメラを構築します。RealVNCでアカウントを作成すれば、外からでもカメラ映像を確認できます！
ポート開放等、自宅のwifi設定をいじる必要もありません。但し、音は出ません（最近出る様になった、というウワサも聞きましたが未確認）
※3～4カ月前にやったので、抜けている内容あるかもです。
※拙宅wifi環境では問題なく動きましたが、利用wifi環境に依るかも知れません。

###使用したモノ
- Raspberry Pi 3B+ (OSはraspbian stretch 9.9)
- USBカメラ([バッファローBSWHD06MBK](https://www.amazon.co.jp/dp/B00GZAF72U/))
- ラズパイ用電源コード、(ラズパイ)ヒートシンク等
- Windows10 PC(外から画像を確認する機器)

###A.準備
####1.ラズパイ
#####(1)configuration
$sudo raspi-configで、VNCをON(yes)
#####(2)lxsession
```
$sudo apt-get install lxsession
```

####2.PC(windows10(64bits))
#####VNC Viewer
(a) [サイト](https://www.realvnc.com/en/connect/download/viewer)からダウンロード。
(b) 起動すると、最初の画面で「Send anonymous usage data to help improve VNC Server.」にチェックがあるので、もしイヤであれば外す。
(c) 画面上のバーに、ラズパイのIPアドレスを入力すると、__Authentication__の小窓が出るので、ラズパイのUsernameとパスワードを入力すればOK

####3.調整/追加機能
#####(1)ラズパイ出力画面サイズの調整
VNC Viewerでラズパイからの出力画面がはみ出てしまう等の場合、ラズパイで以下調整を実施。

- ラズパイの解像度を確認

```
$ tvservice -s
```
- ラズパイのconfig.txtを修正、__最後に__縦横の画面サイズを追加・設定

```
$ sudo nano /boot/config.txt
```
※次項のdirect capture modeを使用する場合は、アスペクト比を合わせて設定（例：1080x720)

```
framebuffer_width=1080
framebuffer_height=720
```
#####(2)Direct Capture Mode
(a) ラズパイのGUI画面の上部バーにある、VNCアイコンをクリックすると、VNC Serverウインドウが立ち上がるので、その右上のメニューから__Options__を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278241/916982d6-9e76-d26d-b76a-82f080110835.png)
(b) __troubleshooting__を選択し、「__Enable direct capture mode__」をチェック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278241/2ca76f71-ef6a-cbf0-ad99-2a12d9d6553e.png)

###B.Cheese
VNC経由の映像ストリーミングには、Cheeseを使用

```
$sudo apt-get install cheese
```
ラズパイのGUIのメニュー「__Sound&Video__」の中に出てくる__Cheese__をクリックすると、撮影画面(動画)が表示される。カメラの設定等は不要。自動的にUSBカメラからの画像が出てくる（はず）。
![cheese2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278241/16151882-d3e9-39b7-8996-2f6d98d62616.png)

###C.RealVNC cloud connection
####0.準備
- RealVNC accountの取得
- VNC接続： 設定はwifiで行なうので、まずは上述手順(A.2.)に従って、VNC Viewerに接続
(→ラズパイに直接ディスプレイを接続していれば不要です、多分）

####1.cloud connectionの設定
cloud connectionは、Home subscriptionで、5機まで登録(接続)が可能。
- ラズパイGUI画面上で、VNCアイコンを右クリックし、__Licensing__を選択
- __Sign in__画面で、RealVNC account情報を入力してサインイン
※ team指定は任意(指定した方がわかりやすいかと)
- 再度VNCアイコンを右クリックし、__Options__を選択
- __Connections__を選択し、「__Allow Cloud connections__」をチェック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278241/ac2ef7f7-1356-34e2-7fdb-4e35107367c2.png)

まずはそのまま同じwifi内で接続するか確認してみて下さい。
これで、外にPCを持ち出して、(ラズパイ+カメラが接続している)wifi以外のネットワークからもアクセスが可能になります。
スマホ(android)でもアプリ入れて見れます（外からアクセスできることを示すために、4Gで接続）
![Screenshot_20200121-234311 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278241/461656a1-7e52-736e-b7b4-d9042b65fbc7.png)

###あとがき
これから不定期にやっていきたいと思っていますが、素人に毛が生えた程度のエンジニア力ですので、間違っている点等ありましたら、優しく(笑)ご指摘下さい。改善・改良案は勿論welcomeです。

###参考サイト
[VNC Connect and Raspberry Pi](https://help.realvnc.com/hc/en-us/articles/360002249917-VNC-Connect-and-Raspberry-Pi#setting-up-your-raspberry-pi-0-0)
[ポートの開放することなく、Respberry Piに外からアクセスする](https://qiita.com/moritalous/items/6ddd36f895ca656eac80)
nuxtでSSRをする際にfirebasehostingとnetlify、herokuどれがいいのか？

# この記事の目的

nuxtでSSRをするサービスのプロジェクトでどこのインフラ使おうかなーと調べてメリット・デメリット並べてみた。

# SSRとは

実は告白すると僕SSRって静的なファイルを事前に作っておくことでgenerateすることと同義だと思っていました。だけど調べていくとgenerateは本当に静的なHTMLなので動的な部分があると向いてないです。

* SSRはサーバーサイドが都度レンダリングしてページを返す
* SPAはクライアントサイドでレンダリングしている
* generateはサーバーに置かれている時点ですでにHTML化されてて静的な状態になっている

って感じ。

# firebasehosting

firebase functionsを使うことになります。

https://www.memory-lovers.blog/entry/2019/08/07/150000

twitterに対してはheadタグ内のogpだけ返して人に対してはページを返すみたいなfunctionsの動き。

# netlify

prerendering
https://docs.netlify.com/site-deploys/post-processing/prerendering/#how-it-works

まだbetaだけどnetlifyにはprerindering機能があり、OGPや検索エンジンへレンダリングして返すというSSRと同じ動きをしてくれる。

# heroku

まあherokuだよね。そうだよね。herokuだとnode.jsで動かす感じになる。

https://ja.nuxtjs.org/faq/heroku-deployment/

「Nuxt + Herokuで簡単にデプロイする」
https://qiita.com/pentla/items/de3e6b50f84fcc188fa5
Shibuya.apk#38 参加メモ

# はじめに
2020/1/17(金)の [shibuya.apk #38 - report from KotlinConf 2019](https://shibuya-apk.connpass.com/event/159535/) に参加してきました。
slackにメモを残しても忘れてしまうので、どんな発表があったのか、
どんな資料が紹介されていたのかまとめました。
（自分用のメモです）

# Java Bytecode Vertical Tasting 
### punchdrunkerさん
https://speakerdeck.com/punchdrunker/java-bytecode-vertical-tasting
途中参加だったので、話の概要を掴めず...

> JavaのLTSがついていないのはすぐにサポートが切れる。今の本命バージョンは11

このあたりが要点？かも。
あと、

```
innerクラスからフィールドにアクセスすんな
```

コンパイラで余計なクラスが増えるかららしい。

# KotlinConf Overviewと2021年のアプリ開発トレンド
### mhidakaさん
```
・春にはKotlin1.4が出るが、Updateはちょっと大変らしい。
・今後、宣言的UIは流行する
・lonaの紹介
・lonaは大規模開発時にデザインシステムをつくるためのOSS、概念的なもの
・成長するアプリが抱える課題、複雑なコードに対して今まで以上に開発スピードを上げる
・サーバ連携してレイアウト組み立てられると良い
```
https://github.com/airbnb/Lona

# Repository with Store4
### wasabeef_jpさん
https://speakerdeck.com/wasabeef/repository-with-store4-ja

```
・store4の紹介
・なんで4かというと、製作者が転職ごとにリポジトリを移動しているかららしい
・RxよりFlowの方がDisposableの扱いがない分、フールプルーフにならない
```
https://github.com/dropbox/Store

# kotlinx.serialization について
### red_fat_darumaさん
https://speakerdeck.com/jmatsu/serialization-in-kotlin-world

集中切れ...

# LT
### How to use multi camera in Android Kurunさん
https://t.co/eVuB2yUeZh?amp=1

### Capture view and register to gallery うめちゃんさん
https://docs.google.com/presentation/d/1EsB-7X2zFtkIwyQXzlI1FWdiRdof1QMeUogsvEAUOhw/edit#slide=id.p1

### Asynchronous Data Streams with Kotlin Flow を要約したかった　sobayaさん
https://docs.google.com/presentation/d/1Z8eVQede3MFSO2wEy0d_pUPPLh0nYwlg74m1Be_E4yk/edit?usp=sharing

### WorkManagerを使って指定した時間に音量をゼロにするアプリを作った話　kikuchiさん

# まとめ
話の内容が刺激的で良い刺激を受けました。
全然別の話ですが、Qiitaにspeakerdeck埋め込めるといいんだけどなぁ...
Mac の VSCode に PlantUML を導入

# Overview
プライベートの開発タスクの依存関係を可視化するために、PlantUML を導入する。

# インストール
brew コマンドを使って必要なパッケージを導入

```sh
$ brew install graphviz
$ brew install adoptopenjdk --cask
$ brew install plantuml
```

brew コマンド自体がない場合は、以下でインストールしてから再実行する。

```sh
# コマンドがない！
$ brew install XXX
bash: brew: command not found

# インストール
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

# インストール確認
$ brew -v
Homebrew 2.2.3
Homebrew/homebrew-core (git revision a0d66; last commit 2020-01-19)
Homebrew/homebrew-cask (git revision 4bb1; last commit 2020-01-19)
```

plantuml は Java が必要とのことで、入っていない場合は以下のエラーが表示される。

```sh

$ brew install plantuml
plantuml: Java is required to install this formula.
Install AdoptOpenJDK with Homebrew Cask:
  brew cask install adoptopenjdk
Error: An unsatisfied requirement failed this build.
```

# VSCode で利用する
PlantUML 用の拡張をインストールする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38277/536e8441-570c-b861-a574-f8cd7acaacc0.png)
インストールが完了したら VSCode を再起動。

`*.pu` 形式のファイルを作成し、以下の様に記入してみる。

```
@startuml
Class01 <|-- Class02
Class03 *-- Class04
Class05 o-- Class06
Class07 .. Class08
Class09 -- Class10
@enduml
```
その後、`⌥(option) + D` を入力すると。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38277/19aa446b-47cb-835b-3c0c-cebd33073847.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38277/2ed5b5b4-139f-f565-5c3d-2d4786ef103a.png)
少し時間をかけた後に上記の図が描写された！

一度プレビューを表示すれば、後はリアルタイムで更新が反映されていくので確認しつつ修正ができる。これは便利。

# 画像として保存
`⌘(command) + ⇧(shift)+ P` でコマンドパレットを開き、 **Export Current File Diagrams** を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38277/622a3f99-ef80-6635-53de-932c5bc08121.png)
その後ファイル形式を聞かれるので、好きな形式を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38277/a1d6c049-2011-07c6-31da-7cfce4c2022f.png)
少し時間が経った後、 以下の通知が出れば作成完了。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38277/76e35aaa-ba26-afb7-606c-e4f94ba5d633.png)

今回の場合、`test.um` から `/out/uml/test/test.png` を生成した。
生成ファイルのディレクトリが独自に作られるので、最初は少し迷うかもしれない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38277/ae2dc9cc-9469-7490-09c4-6f4e9777c351.png)


# GitHub 上で管理・閲覧する。
[ PlantUML Viewer](https://chrome.google.com/webstore/detail/plantuml-viewer/legbfeljfbjgfifnkmpoajgpgejojooj?hl=ja)を使うことで、GitHub 上のテキスト形式の UML図 を描写して確認することが可能。
確認する際は、そのファイルをGitHub上の**Raw**指定で表示する必要あり。

# 詳しい書き方
* [PlantUML Cheat Sheet
](https://qiita.com/ogomr/items/0b5c4de7f38fd1482a48)を参照


# Reference
* [PlantUMLをMac OS Xで使う](https://qiita.com/kohashi/items/1d2c6e859eeac72ed926)
* [PlantUMLを使ってみる(MacOS) ~Visual Studio Codeでもっと使いやすく~](https://qiita.com/Takaichi00/items/8e03258c3c956d4848c4)
Tera Term導入

##記載内容
Tera Termインストール後に実施しておいたほうが良い設定を記載します。
##環境
OS：Windows 10 Home 1903
Tera Term：v4.105

##初期設定
Tera Termインストール後に、まず初めにしておくべき設定を紹介します。

1. ログ保管設定
    1. ログ保管ディレクトリ作成  
       適当な場所にログ保管用のディレクトリを作成します。  
今回は C:\logs\teraterm を作成しました。
    2. ログ保管設定  
標準ログファイル名：%Y%m%d_%H%M%S.log  
標準のログ保存先フォルダ：1.1.で作成したログ保管用のフォルダを指定  
自動的にログ採取を開始する：チェックを入れる  
オプション > タイムスタンプ：チェックを入れる  
<img width="356" alt="2020-01-19 (3).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299203/ffa9cde2-7d0d-c28e-fca0-2e4727489869.png">

2. ブレークコマンド無効化
全般 > "ブレーク信号のアクセラレーターキーを無効にする"にチェックを入れる。
<img width="356" alt="2020-01-19 (5).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299203/5daf82ad-1b84-1d08-7326-0d487e626ab4.png">

今はあまり無いかと思いますが、SunOSにコンソール接続してAlt+Bを押すとブレーク信号が送信され**サーバが停止します。**
何が危険かというとTeraTermではペーストをAlt+Vで行うので、コピー&ペーストするつもりが、キーを横に一つ打ち間違えることによってコピー&ブレークになってしまうことです。昨今の業務でSunOSを使うことはあまりないと思いますが、無効化しておく方が無難かと思います。  
3. ウインドウサイズ変更時の画面クリア
全般 > ウィンドウサイズ変更時に表示内容をクリアする のチェックを外す
<img width="356" alt="2020-01-19 (6).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299203/e8c1c904-0980-c1da-9ba5-09fb87fc11ba.png">
デフォルトではウインドウのサイズを変更すると画面表示がクリアされてしまうので、それを回避します。

以上の手順が完了したら、設定 > 設定の保存 で変更を保存します。

##参考：テキストベースでの設定方法
今回はGUIでの設定方法を紹介しましたが、テキストでの変更も可能です。
Tera Termは以下の優先度で、設定ファイルを読み込みます。
1. Tera Term実行時に設定ファイルを引数として渡す。
2. My Document配下のTERATERM.INIファイル
3. 実行ファイル(ttermpro.exe)と同じディレクトリ上のTERATERM.INI
    (C:\Program Files (x86)\teraterm配下)    
  
デフォルトではC:\Program Files (x86)\teraterm\TERATERM.INIが読み込まれているので、該当ファイルを上書き、もしくは該当ファイルをコピー→編集して1., 2.の方法で読み込ませることで修正可能です。  
以下、編集する箇所です。
**1. ログ保管設定**
変更前：
LogTimestamp=off
LogDefaultName=teraterm.log
LogDefaultPath=
LogAutoStart=off
変更後：
LogTimestamp=on
LogDefaultName=%Y%m%d_%H%M%S.log
LogDefaultPath=C:\logs\teraterm
LogAutoStart=on
**2. ブレークコマンド無効化**
変更前：
DisableAcceleratorSendBreak=on
変更後：
DisableAcceleratorSendBreak=off
**3. ウインドウサイズ変更時の画面クリア**
変更前：
ClearOnResize=on
変更後：
ClearOnResize=off  

基本的にはGUIでの変更がメインになりますが、研修の準備等で複数のPCに設定する場合などは設定ファイルを配布したほうが早いと思います。  
以上です。
その他に推奨される設定をご存じの方はコメントで教えていただけると嬉しいです。
ライブラリやツールなどを導入するにあたっての選定の仕方

# はじめに

現在、現場ではスクラム開発をやっているのですが、チームで進めていくにあたって新しいライブラリやツールの導入の進め方をきちんを明文化したほうがよいと感じたので、記事化してみました。

# 1.導入目的を明らかにする

「ただなんとなく導入したら良さげだな」とか、「これが今流行ってるから」とか、あいまいな理由で導入するのは返って効率が悪くなったり、導入する意味が全くなく時間の無駄になってしまいます。
まずは「今明確に解決したほうがよい問題はないか？」を明らかにしましょう。
また、その問題を解決する上で、「選定基準」も合わせて洗い出せるとなおよしです。

例えば、「手動でUIテストをやっているために時間がかかっており、本来は全画面で確認するべきなのにできておらず、デグレ検知できない」という問題があったとします。
その問題を解決するために、「UIテストを自動化し、CIで実行することで定期的にUIテストを実行することで、テストという毎回同じ単純作業を効率化し、かつデグレ検知できるようにする」という解決案に決まったとします。
そして解決案に対して、

- 各種OSに対応しているか
- 言語は何か
- スクリーンショット撮影ができるか
- 各種OSでワンソースでいけるか
- CIツールとの相性はどうか
- 環境構築は容易か

上記のように選定基準も洗い出せる範囲で洗い出します。

# 2.目的を達成できそうなものを調べ、複数候補を出す

次に、1.で明らかにした内容を元に、ネットなどでツールを調べ、合致しそうなものをどんどん洗い出していきます。
また、調べていく中で「こんなこともできるんだ！」という発見があり、選定基準として必要そうであれば、適宜追加していきます。

# 3.マトリクスを作成する

次に、2.で洗い出したものと1.で作成した選定基準をもとに、以下のような「マトリクス」を作成し、比較していきます。

| 選定基準 | ツールA | ツールB | ツールC |
|:-----------------|:------------------|:-------------------|:-------------------|
| 各種OSに対応しているか | ○（iOS、Android） | ○（iOS、Android） | ○（iOS、Android） |
| 言語は何か | JavaScript | Kotlin | Python |
| スクリーンショット撮影ができるか | × | ○ | × |
| 各種OSでワンソースでいけるか | ○ | × | △（ネットの情報が曖昧なため要検証） |
| CIツールとの相性はどうか | ○（Circle CI、Bitrise、他） | ○（Azure pipelines、GitHub Actions） | △（Jenkinsのみ） |
| 環境構築は容易か | ○ | ○ | △（メンバ内に有識者がいない） |

あとは一番良さげなものを採用して進めていくだけです。

# おわりに

今回はライブラリやツールなどを導入するにあたっての選定の仕方をまとめました。
これはあくまでも一例であり絶対的なものではありません。
もし、「こういう進め方もあるよ」などあれば教えていただけると嬉しいですm(__)m
Delphi タッチアニメーションを無効化する

#はじめに
Delphiでモバイル開発をしていると、コントロールをタッチした際に波紋のようなアニメーションが発生することに気がつきます。
ユーザーが操作可能なオブジェクトであることが分かりやすい一方、微妙にダサいというか・・頼んでないというか・・そういう気分になることもありますね！
![touchAnimation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33684/3f1ea9c7-35c2-22c7-6bec-cb41b2871e34.png)（意外と撮るのに苦労するスクリーンショット）

しかしながら、無効化の方法については、ドキュメントを見つけることができませんでした。
なので、この記事は「方法が正しいかは解らないけどこれで無効化できたよ！」という内容になります。

Delphi 10.3 CommunityEditionで確認しています。
※iOSについては手元に環境が無いため、確認できていません。

##やり方
TButtonを例に説明します。

- マルチデバイスアプリケーションのプロジェクトを作成し、スタイルを「Android」に設定します。
- FormにTButtonを配置し、右クリックし「カスタムスタイルの編集」を選択します。
- スタイルデザイナが開いたら（<code>Button1Style1</code>というスタイルが表示されています）、「名前を付けて保存」で適当なファイル（Button.style）に保存します。
- テキストエディタで Button.style を開きます。おおよそ次のような中身になっていると思います。

```
object TStyleContainer
  object TLayout
    StyleName = 'Button1Style1'
    Align = Center
    Size.Width = 297.000000000000000000
    Size.Height = 233.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 0
    object TTintedButtonStyleObject
      StyleName = 'background'
      Align = Contents
      SourceLookup = 'AndroidL Lightstyle.png'
      Size.Width = 297.000000000000000000
      Size.Height = 233.000000000000000000
      Size.PlatformDefault = False
（中略）
      TouchAnimation.Link = <
        item
          SourceRect.Left = 336.000000000000000000
          SourceRect.Top = 233.000000000000000000
          SourceRect.Right = 396.000000000000000000
          SourceRect.Bottom = 293.000000000000000000
        end
（略）
        item
          Scale = 3.000000000000000000
          SourceRect.Left = 1009.000000000000000000
          SourceRect.Top = 704.000000000000000000
          SourceRect.Right = 1189.000000000000000000
          SourceRect.Bottom = 884.000000000000000000
        end>
      TouchAnimation.Padding.Left = 1.000000000000000000
      TouchAnimation.Padding.Top = 1.000000000000000000
      TouchAnimation.Padding.Right = 1.000000000000000000
      TouchAnimation.Padding.Bottom = 2.000000000000000000
      HotTint.Shadow = <>
      HotTint.Mask = <
（後略）

```
この<code>TouchAnimetion.Link</code> 要素が、波紋アニメーションを発生させる際の当たり判定と考えて良さそうです。
よって、<code>TouchAnimation.Link</code>の中身をバッサリ削除します。<code>TouchAnimation.Padding.*</code>も削除します。

```
      TouchAnimation.Link = <>　//中身を削除
      HotTint.Shadow = <> //その他は前のまま
```
- スタイルファイルを保存します。（変更前にスタイルファイルのバックアップを取った方が安全）

- スタイルデザイナに戻り、<code>Button1Style1</code>を削除します。そして、「開く」アイコンをクリックし、先ほど修正したスタイル ファイルを読み込みます。
- スタイルを適用し、デバッグ実行で動作を確認します。


##原理など
タッチアニメーションの発生・描画は、<code>FMX.Style.Object.pas</code>ファイル内の、<code>TTouchAnimationAdapter</code>クラスが担っており、このクラスを<code>TActiveStyleObject</code>などの、スタイル定義クラスが所持しています。

先ほどの<code>Button1style1</code>の場合は、<code>backgroud</code>という<code>TTintedButtonStyleObject</code>クラスのオブジェクトが、<code>TTouchAnimationAdapter</code>を持っています。（スタイルデザイナのオブジェクトインスペクタ上でも確認できます。デザイナ上で設定を変更できればよかったのですが、出来なかった為、ファイルを直接編集して対処しました。）

スタイルの定義に<code>TActiveStyleObject</code>等を用いなければ、タッチアニメーションも発生しない、ということでもあります。

逆に、スタイルを触ってたらタッチアニメーションが発生しなくなってしまった！という場合は、<code>TouchAnimation.Link</code>の情報が消えてしまっていることが考えられるので、スタイルファイルを確認してみてください。

#おわりに
正直「こんな手間なことってある？」と半信半疑なのですが・・。なにかもっと良い方法というか、正しいやり方がありましたら、情報頂ければと思います！
初学者のレスポンシブデザイン

# はじめに
就活で使用しているポートフォリオにレスポンシブなデザインを取り入れたので書きます。
（私は現在就活中の大学生です）

# レスポンシブとは
PC、タブレット、スマホなど異なる画面サイズの幅に合わせて柔軟にデザインを調整し、見やすい最適な表示にすることです。
私の大学のホームページにもつい最近導入されました。（うちの大学は遅れてるのかな？

# レスポンシブの導入
レスポンシブなデザインを始める前に、まずHTMLのヘッダーに以下を追記します。

```
<meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no">
```

これによりWebページを閲覧している端末の画面サイズに合わせて表示域を調整してくれます。
これをしないとレスポンシブなデザインを作っても、うまく動作しません。（経験済み）

# ブレイクポイントを決める
ブレイクポイントとは、CSSを切り替える画面サイズのことです。
以下が端末の一般的なサイズ表です。

|端末 |サイズ |
|--- |---|
|PC |1024px以上|
|タブレット|768px以上|
|スマホ|480px未満|

# CSSに指定する
上の表を使ってブレークポイントを決めると
以下のようになります。

```
@media screen and (min-width: 1024px) {
    1024px以上(PC)のCSS
}
@media screen and (min-width: 768px) {
    768px以上(タブレット)のCSS
}
@media screen and (max-width: 480px) {
    480px未満(スマホ)のCSS
}
```

# まとめ
今回の記事では、簡単にですがレスポンシブなデザインを作る方法を書きました。

レスポンシブなデザインはUIの向上やSEO対策などのメリットがある反面、表示が重くなったり単純に作るのが大変だったりします。
しかし、私がユーザー側に立った時には、やはりレスポンシブなデザインのWebページの方が使いやすいと感じます。大学のホームページでとても感じました。
なので、これから私が作るWebページはレスポンシブを考えたものにしたいと思います。
JavaScript勉強の記録その22:setTimeoutとclearTimeoutを使って関数を呼び出す

#setTimeoutを使って関数を呼び出す
setInterval(関数名,ミリ秒)とすることで、特定のメソッドを指定したミリ秒間隔で呼び続けることができますが、setTimeoutはsetTimeout(関数名,ミリ秒)とすることで、特定のメソッドをミリ秒後に1度だけ呼ぶことができます。

例えば以下の例では、ボタンがクリックされてから、2秒後にDateオブジェクトが生成されてコンソールに表示されています。

```index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Javascript Practice</title>
</head>
<body>
  <button type="click" id="button">showDatePerSecondを呼ぶ</button>
  <script src="main.js"></script>
</body>
</html>
```

```index.js
function showDateTimePerSecond() {
   console.log(new Date());
};

document.getElementById('button').onclick = function(){
  setTimeout(showDateTimePerSecond,2000);
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271600/2847823c-09fa-8bb0-38d3-70dac827aa09.png)


#setTimeout(関数名,ミリ秒)で繰り返し関数を呼ぶ
setTimeoutもsetIntervalと似たような使い方ができます。
以下の例では、showDateTimePerSecondの関数内でsetTimeoutを呼び、引数にshowDateTimePerSecondを取ることで繰り返しの処理をしています。

```index.js
function showDateTimePerSecond() {
  console.log(new Date());
  setTimeout(showDateTimePerSecond, 1000);
};

showDateTimePerSecond();
```

#clearTimeout()で処理を止める
setTimeoutの繰り返しを止めるには、clearTimeoutというメソッドが用意されています。
以下の例では変数iが３になった時点でclearTimeoutを呼んで処理を止めています。

```index.js
let i = 0;

function showDateTimePerSecond() {
  console.log(new Date());
  const timeoutId = setTimeout(showDateTimePerSecond, 1000);
  i ++;
  if (i > 2) {
    clearTimeout(timeoutId);
  }
};

showDateTimePerSecond();
```

#setIntervalとsetTimeoutの違い

setIntervalで繰り返し処理を行うのと、setTimeoutで繰り返し処理を行うのではどのような違いがあるでしょう？

以下のサイトがとてもわかりやすいです。要は、

setInterval　←　呼び出しを行なった関数の処理終了は関係なく、指定された時間になったら問答無用で引数で渡されている関数を呼ぶ。
setTimeoutで繰り返し処理　←　呼び出しを行なった関数の処理が終わった後、指令された時間を置いて、引数で渡されている関数を呼ぶ。

https://ja.javascript.info/settimeout-setinterval
Node.js install for mac

何からNode.jsを入手したか忘れないためのメモ。


- [公式](https://nodejs.org/ja/download/)からLTS版mac用のinstallerをダンロードする
- installerを起動し、各設問をデフォルトのまま進めてinstallする
    - <img width="615" alt="スクリーンショット 2020-01-19 23.05.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110482/9d70e984-8bd5-ef05-a38f-5be6639a44d5.png">
- nodeのバージョンを確認する

```
$ node -v
v12.14.1
```

- npm(パッケージマネージャ)のバージョンを確認する

```
$ npm -v
6.13.4
```

以上です。

参考:
https://jsprimer.net/use-case/setup-local-env/
Jupyterhubで教室を作る

## Jupyterhubのことがようやく少しわかってきました。

皆様はJupyterhubという最強のPython実行環境を使っているでしょうか?

Kaggleなどもありますが自分が持っているサーバーインスタンス上でJupyterhubを動かすことができれば結構便利です。

将来的にはトレーニングなどで使えないかなと思いJupyterhubでの運用方法を模索していました。

その中で特に**ユーザーの作り方**と**ユーザーがログインしたときのホームディレクトリ**を設定する方法がわからなかったので共有します。

### まずはJupyterhubのインストール

``` bash
$ pip install jupyterhub
```

### Jupyterhubのデーモン化(起動しっぱなしにする)

``` bash
#外部からアクセスできるようにホストも設定
$ nohup jupyterhub --ip=0.0.0.0 >> jupyterhub.log 2>&1 &
```

### CONFIGをつくる

***jupyterhub_config.py***というファイルが作られます。

``` bash
$ jupyterhub --generate-config
```

### ユーザーのホワイトリストの設定

ホワイトリストのユーザー名しかうけつけないようにします。

``` jupyterhub_config.py
c.Authenticator.whitelist = {'【ユーザー名】'}
```

### ユーザーごとのホームディレクトリの設定

**{username}**でログインしたユーザーネームで可変になるように設定します。ユーザーごとの最上位のディレクトリを設定します。Jupyterhubではユーザーのディレクトリを作ってくれないので自分で作っておくことをお忘れなく。

``` jupyterhub_config.py
c.Spawner.notebook_dir = '/home/【ユーザー名】/jupyterhub/{username}'
```


### 更新履歴

- 2020/01/19 新規作成
Python 3 エンジニア認定基礎試験を受験した感想

# はじめに
Python 3 エンジニア認定基礎試験を受験してきましたので、簡単に感想を記録しておきます。
個人的に受験前に知りたかった情報などが書ければと思います。

# Python 3 エンジニア認定基礎試験とは
簡単に。

|項目||
|---|---|
|制限時間|60分|
|問題数|40問|
|合格ライン|70%(700/1000点)|

# 筆者について
都内にある中規模の会社に入社して2年目のSEです。
今までにシェルスクリプト、C、C#の開発を経験しました。
Pythonについては半年前までは一度も触ったことがありませんでしたが、流行の言語である(?)との情報を聞き受験に至りました。
あと、弊社では、この類いの試験に合格するとなんと会社からお小遣いがもらえます(あるあるだと思いますが)。

業務では、社内で使用するツールをPythonで1ヶ月弱かけて作成したほどの経験しかありません。

# 学習方法
* 「Progate」で2ヶ月課金して学習。  
https://prog-8.com/languages/python
* 「DIVE INTO EXAM」の模擬試験を3週間ほどみっちり行う。
最終的には95%くらいの得点率まで到達しました。  
https://exam.diveintocode.jp/exam
* 書籍は購入しましたが、ほぼ読みませんでした。

# 受験当日
予約方法などは他の方が丁寧に書いてくださっているので割愛します。
以下に記載するのは私の場合であり、受験会場によって異なる場合はありますのでご了承ください。

## 持ち物
受験票メールに記載されています。

* 受験票  
印刷して持参しろとありますが、氏名を伝えれば良いのでなくても問題ありません。
* 顔写真付きの身分証明書  
学生の場合は学生証が必要なようです。
* Odyssey ID・パスワードを控えたメモ用紙  
パスワードを紙に記載するのには抵抗があったので持参しませんでした。
受験時に必要になりますが、失念した場合確認する術がないようです。
私は直前に確認して完全に記憶しました。

## 受付
自分の場合は受付時刻が14:15～14:25でした。
遅刻をすると受験できないようですが、自分は30分前に到着したため完全に時間をもてあましました。
14:15頃に受付に行き氏名を伝えて受付完了です。

携帯電話、腕時計、筆記用具は持ち込み不可です。
ロッカーに荷物をしまいますが、自分はPCとカメラを持参していたので詰め込むのに苦労しました。荷物は軽くしておきましょう。

## 受験時
一人ずつPCが置いてある席に通されます。席に座るとOdyssey ID・パスワードを入力してログインします。
合図があると各々のタイミングで試験を開始します。
受験した方の記録を見ると15分や20分で回答が完了したとのことでしたが、私は30分ほどで見直しまで完了しました。
他の試験の受験者も含め一番最初の退出者でした。

## 受験後
回答完了後にアンケート(8問)に回答すると結果が表示されます。
点数と合否のみでどの問題を間違えたかなどは見えないようでした。
私は800/1000点で合格でした。

# 受験した感想
受験した経験のある先輩や、こちらの記事などを見ると「模擬試験で勉強するだけで十分だ」、「試験問題は簡単だ」とありますが、Pythonを業務で使用したことがなくコーディング時も文法をGoogle先生にいちいち聞いているような人間としては言うほど簡単ではなかった印象です。
確かに模擬試験が100%であれば合格はすると思いますが、いくらか初見の問題がありますので実際にコーディングしておくとより安心して受験できると思います。

# さいごに
取得した資格は「基本情報技術者試験」「応用情報技術者試験」に次ぐ3つめの資格です。多い方ではないと思います。

よく「勉強をする目的が資格取得になってしまっては意味がない」と言われることがありますが、私は「勉強をするきっかけが資格取得であっても良い」と考えています。
今回はPythonを学習するするきっかけになって良かったと思っています。

基本情報や応用情報と比べたらよっぽど楽に取れました。もっと早く受験していればなと若干後悔しているほどです。
今年は「資格取得Year」にしようと思います。

稚拙な文章で申し訳ありません。ご覧いただきありがとうございました。
Windows10 HomeでのDockerを使ったpython環境構築手順メモ

# はじめに
普段の業務での開発環境はUbuntuなのですが、わけあって私物のwin10 Homeで開発をする機会ができたので、そのときの環境構築手順のメモです。
普段業務で散々お世話になっているので、いい機会だと思い初投稿。

# やりたいこと
- Pythonの開発環境を作りたいが、Anacondaは結構な頻度でハマるポイントに出会うので使いたくない
- 普段使う開発環境(Ubuntu 18.04 LTS)になるべく近付けたい

ということで、Docker入れてubuntuのコンテナをpythonのバージョン毎に個別に建てるのが何も考えずに出来てきっと楽。
pythonのライブラリはrequirement.txt作って使いまわし、python自体のバージョンはDockerfileの時点で書き換えるつもり。


## 0. 実行時の環境 
作業開始時点で環境構築に影響がありそうなのは以下くらい。

- Windows10 Home
- Git for Windows(2.18.0.windows.1)

基本的にGit bash使って作業しています。



## 1. Dockerのインストール
Docker for WindowsはHomeだと使えないので、Docker Toolboxをインストールする。
[https://github.com/docker/toolbox/releases](https://github.com/docker/toolbox/releases)から最新版のインストーラをダウンロードし実行。記事作成時は19.03.1でした。
終了後、以下の3つのアイコンがデスクトップに作成されているはずなので、一番右のDocker Quickstart Terminalを実行。
![01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546406/6c105012-2fa1-ffbb-d35b-4a80420f8d2e.png)  
ターミナルが立ち上がって初期設定が始まるので、しばらく待つ。DockerのクジラのAAが無事出れば多分無事成功

## 2. Docker周りの準備
無事Dockerのインストールが終了したので、ubuntuのイメージを持ってくる

```bash

$ docker pull ubuntu:18.04
```
無事pull出来ていれば、以下のコマンドで確認できる。

```bash

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              18.04               ccc6e87d482b        3 days ago          64.2MB
```

## 3. Python環境の準備
上記imageをベースにpythonの開発環境が整ったimageを作成するため、適当なディレクトリを作り、Dockerfileを用意。
よく使うDockerfileの中身はこんな感じ。RUNたくさん使うとレイヤーが増えて良くないとか見た気もするけど、自分で使うだけなので気にしない方針。
python3.8とかにしたい場合は、Dockerfileの中身のpythonの部分だけ書き換えれば多分問題ないです。

```dockerfile
FROM ubuntu:18.04

RUN apt update && apt upgrade -y
RUN apt install vim python3.7 python3.7-distutils curl -y
RUN ln -s /usr/bin/python3.7 /usr/bin/python
RUN curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" && python get-pip.py
RUN apt autoremove
```
上記を元にimageを作成。

```bash

# imageの作成
$ docker build -t <image name> .

# 作成したimageの確認(今回はu18_py37という名前で作成)
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
u18_py37            latest              86bc6cf8e1a9        7 hours ago         214MB
ubuntu              18.04               ccc6e87d482b        3 days ago          64.2MB
```


## 4. 実行
上記imageを使ってコンテナをたてる。

```bash

$ docker run --net host --name test -v //c/Users/<User Name>/docker:/wrk -it u18_py37 bash
```

# おわりに
とりあえず環境構築自体はこんな感じでしょうか。
ここのやり方良くない等、ご指摘あればよろしくお願いします。
作った環境で初めてwebAPI叩くコード書いたりするのですが、それはまたそのうち別記事で。
GitHub Actions で３項演算子を実現する

## はじめに

[演算子](https://help.github.com/ja/actions/automating-your-workflow-with-github-actions/contexts-and-expression-syntax-for-github-actions#operators)には今のところ３項演算子はありませんが３項演算子のようなことは容易に実現可能です。

## 対象
GitHub Actionsを利用している開発者

## 実装
AND 演算子と OR 演算子を使用して以下のように指定します。

```
(条件 && 条件がtrueの場合の値) || 条件がfalseの場合の値
```

### 実装例

以下のような実装でイベントごとに異なる変数をアクションに渡すことができます。

```yaml:.github/workflows/test.yml
on:
  pull_request:
  push:

name: Test
jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Test
        uses: ./
        with:
          test: ${{ (github.event_name == 'pull_request' && 'test1') || 'test2' }}
```

```yaml:action.yml
name: Test
description: Test
author: technote-space
inputs:
  test:
    description: test
    required: true
runs:
  using: node12
  main: lib/main.js
```

```js:lib/main.js
console.log('eventName: ', process.env.GITHUB_EVENT_NAME);
console.log('process.env.INPUT_TEST: ', process.env.INPUT_TEST);
```

### 動作

![202001192227.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264159/e0abbd1a-9aec-f318-cb6c-eb68ee6a4d95.png)
![202001192230.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264159/bf64bf36-2d70-c934-1c17-f8764c5eaada.png)

## 注意

条件がtrueの場合の値に空の文字列を渡したい場合、条件を逆にする必要があります。

```
(!条件 && 条件がfalseの場合の値) || ''
```
Nuxt.js開発環境構築

# Node.jsをインストール
以下記事を参考にインストール。
https://qiita.com/Masayuki-M/items/840a997a824e18f576d8

#VScodeをインストール
以下記事を参考にインストール。

・VSCodeインストール
https://qiita.com/psychoroid/items/7d85ae6bade4a67aedb1
・日本語化
https://qiita.com/psychoroid/items/8f37d3b32509d428bab8

#Nuxt.jsを動かしてみる
コマンドプロンプトを管理者で起動する、
またはVScodeのターミナル>新しいターミナルを開く。
プロジェクトを作成したいディレクトリ配下で、下記のコマンドを実行。

```ruby:指定したディレクトリに移動
>cd c:\work
```

```ruby:Nuxt.jsプロジェクト作成
>npx create-nuxt-app PROJECT_NAME
```
今回企画のプロジェクト環境設定は、以下画像の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479885/c20d5f0a-2f59-9cfe-0e5c-6d0c556be60e.png)

作成したプロジェクトのディレクトリに移動し、実行。

```ruby:プロジェクトのディレクトリに移動
>cd demo
```
```ruby:実行
>npm dev
```

http\://localhost:3000/を表示し、Nuxt.jsのデフォルトTOP画面が表示されれば完了。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479885/9987b10c-4739-b408-8446-4e5d508778d8.png)

#(おまけ)Nuxt.jsをVSCode上でデバックする方法
以下記事を参考に設定可能。
https://qiita.com/idani/items/ac638d5a0786aaefb8cf
https://qiita.com/keiichi-hikita/items/e4b788a1d39aa88b46e3
http://nullpoint.hatenablog.com/entry/2019/10/14/210107
午前0時に前日のファイルアクセスログをメールで自動送信する

本稿は[【ファイルサーバのアクセスログをPowerShellで取得する】](https://qiita.com/mindwood/items/da5932845548a64a7946)の続編。
今回はコードのみ。監査ログの設定方法については、先の記事を参照して欲しい。

次のPowerShellスクリプトをWindowsファイルサーバのタスクスケジューラに登録した。
午前0時になると、前日のファイルアクセスログをZIPファイルに固め、速やかにメールで転送する仕掛けだ。

```powershell:audit_mail.ps1
$endTime = Get-Date -Hour 0 -Minute 0 -Second 0
$startTime = Get-Date $endTime.AddDays(-1)

$baseFile = Join-Path 'E:\log\audit_' $startTime.ToString('yyyyMMdd')
$csvFile = $baseFile + '.csv'
$zipFile = $baseFile + '.zip'
if (Test-Path $csvFile) { Remove-Item $csvFile }
Write-Host "$startTime - $endTime -> $csvFile"

echo '日時,アクセス,ユーザ名,ファイル／フォルダ名' | Out-File -Append -Encoding default -FilePath $csvFile
# イベントログの取得
$AccessMaskHash = @{ '0x1' = '読込'; '0x2' = '書込'; '0x4' = '追加'; '0x10000' = '削除'}
try {
    Get-WinEvent -FilterHashtable @{
        LogName = 'Security'
        ProviderName = 'Microsoft-Windows-Security-Auditing'
        ID = 4663
        StartTime = $startTime
        EndTime = $endTime
    } -Oldest -ErrorAction Stop | %{
        $xml = [XML]$_.ToXml()   # EventDataにアクセスするためにXML化

        $FileName = ($xml.Event.EventData.Data | ?{$_.Name -eq 'ObjectName'}).'#text'
        if (! $FileName.StartsWith('E:\SHARED\')) { return }
        $AccessMaskCode = ($xml.Event.EventData.Data | ?{$_.Name -eq 'AccessMask'}).'#text'
        $AccessMaskName = $AccessMaskHash[$AccessMaskCode]
        $TimeCreated = ([DateTime]$xml.Event.System.TimeCreated.SystemTime).ToString('yyyy/MM/dd HH:mm:ss.fff')
        $UserName = ($xml.Event.EventData.Data | ?{$_.Name -eq 'SubjectUserName'}).'#text'
        if ("$AccessMaskName,$UserName,$FileName" -eq $uniq) { return }
        $uniq = "$AccessMaskName,$UserName,$FileName"

        echo "$TimeCreated,$AccessMaskName,$UserName,$FileName"
    } | Out-File -Append -Encoding default -FilePath $csvFile
    # ZIPアーカイブ作成
    Compress-Archive -Force -Path $csvFile -DestinationPath $zipFile
    # メール送信
    $password = ConvertTo-SecureString 'PASSWORD' -AsPlainText -Force
    $credential = New-Object System.Management.Automation.PSCredential example@gmail.com, $password
    $subject = $startTime.ToString('【自動送信】yyyy年M月d日(ddd) 監査ログ送付')
    $body = @"
このメールはシステムから自動送信しています。
昨日のファイルアクセスログを送付します。
"@
    Send-MailMessage -From 'システム管理者 <example@gmail.com>' -To user@example.com `
               -SmtpServer smtp.gmail.com -Port 587 -UseSsl `
               -Credential $credential -Encoding UTF8 `
              -Attachments $zipFile `
                  -Subject $subject `
                     -Body $body
    if (Test-Path $zipFile) { Remove-Item $zipFile }
} catch [Exception] {
    Write-Host('エラー:' + $_.Exception)
}
```

他システムに転送することで、**システム管理者による意図的もしくは無意識の改竄を防止する**ソリューションになる。
今回はZIPファイルをそのまま転送しているが、改竄防止目的なら、**ZIPファイルのMD5ハッシュ値**のみメールで知らせても良い。それだけでも抑止力になる。

なお、Gmailの場合、初回に次のような警告を出して異常終了するが、「はい」と回答すれば次回からは正常に送信される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/2917d69a-2900-5f3c-d429-6fe7b1724114.png)
以上、お金を掛けずにできる実効性あるセキュリティ対策の一例として紹介した。簡単だが参考になれば幸いである。
PythonでABC152のA~C問題を解く

### はじめに
[前回](https://qiita.com/taxfree_python/items/80246826ff9d93c23520)からレートが少しあがって、今は104です。
今回は、A~C解くことができました。

###A問題
[問題](https://atcoder.jp/contests/abc152/tasks/abc152_a)

**考えたこと**
n == mの時だけACなので簡単なif文を書けばよい。

```python
n, m = map(int,input().split())

if n == m:
    print('Yes')
else:
    print('No')
```
'Yes''No'の大文字に注意!

###B問題
[問題](https://atcoder.jp/contests/abc152/tasks/abc152_b)

**考えたこと**
aとbの各桁の大小の比較の実装に苦労した。最終的に、intをlistに変換してindexを引き出して比較した。
for文でindexを指定して、それで大小を比較した。

```python
a, b = map(int,input().split())
a_l = list(str(a)*b)
b_l = list(str(b)*a)
c = 0
for i in range(0,min(len(a_l),len(b_l))):
    if c == len(a_l)-1:
        print(int(str(b)*a))
        break
    if a_l[i] == b_l[i]:
        c += 1
        continue
    if a_l[i] > b_l[i]:
        print(int(str(b)*a))
        break
    if a_l[i] < b_l[i]:
        print(int(str(a)*b))
        break
```

###C問題
[問題](https://atcoder.jp/contests/abc152/tasks/abc152_c)

**考えたこと**
最初は、二重ループで回していたけど明らかにTLEするだろうと思っていた。

```python
n = int(input())
p = list(map(int,input().split()))

ans = 0
for i in range(0,n):
    for j in range(0,i+1):
        if p[i] > p[j]:
            break
        elif j == i:
            ans += 1
print(ans)
```
案の定TLEしたので、forを減らせるところを考えた。
$p[i]$と$p[j]$の大小関係がどうなっているか考えた。それで思ったのは、$p[i]$と$p[j]$の大小を考えた時。

```
$p[i]$よりも前の$p$の要素が全て$p[i]$よりも大きければよい → $p[i]$がi番目までで最小であればよい
```
ということが分かったので、for文を一つ減らせた。

```python

n = int(input())
p = list(map(int,input().split()))

ans = 0
n_min = 0
for i in range(0,n):
    if i == 0:
        ans += 1
        n_min = p[i]
        continue
    if p[i] <= n_min:
        ans += 1
        n_min = p[i]
print(ans)
```

###まとめ
今回は、A~Cを解けたうれしかった。競プロに慣れてきたので今後はアルゴリズムや計算量を減らせるような実装をできるように勉強します。Dは、少し考えたけど分からなかった。サンプルにはACでるけど、TLEしたり、WAしてしまった。
javaでAtCoder Beginner Contest 152を解く

AtCoder Beginner Contest 152お疲れ様でした！
[公式ページ](https://atcoder.jp/contests/abc152)

今回の自分の書いたコードは[こちら](https://github.com/ko-flavor/atcoder-java/tree/master/atcoder/src/abc/abc152)
結果はA-DまでAC、EがWAでした。

以下簡単に解説します。

##[問題A](https://atcoder.jp/contests/abc152/tasks/abc152_a)

引数のNとMを比較する問題。
同じ場合は`Yes`、違う場合は`No`を出力すればOK

##[問題B](https://atcoder.jp/contests/abc152/tasks/abc152_b)

aをb回繰り返した文字列とbをa回繰り返した文字列のどちらが**辞書順**ではやいかを出力する問題。
辞書順なので、aとbのうち小さいほうが先です。

aとbの小さい方をaとbの大きい方ぶん繰り返した文字列を出力できたらACです。

##[問題C](https://atcoder.jp/contests/abc152/tasks/abc152_c)

説明文はちょっと難しく書いてありますが、結局数列で最小値が何回更新されたかを答えればOKです。
**4**, 5, **2**, 3, **1**
上記だと太字になっている数字の数3が正解になります。

##[問題D](https://atcoder.jp/contests/abc152/tasks/abc152_d)

N以下の数字の中で、

- Aの先頭の数字 = Bの末尾の数字
- Bの先頭の数字 = Aの末尾の数字

のどちらも満たす数字の組み合わせが何通りあるかを答える問題です。
Nの数字の数的に二重ループしていると間に合わないと思ったので、

- 最初にループして先頭の数字と末尾の数字のMapをつくる
- 二回目のループで条件にマッチする数をとってきて足し合わせる

としました。計算量的に間に合わないから方針変えなきゃって気づけたのは個人的にうれしいポイントでした。

##[問題E](https://atcoder.jp/contests/abc152/tasks/abc152_e)

どのような i,j についても AiBi=AjBjが成り立つ。
つまり、最小公倍数を求めてAiで割ってBiを求める問題。

答えはさらにそれを足し合わせて出力しなさいとのこと。

方針を決める部分までは分かり、最近勉強した逆元使えばAiで割れるやん！というところまではよかったが、
大きい数の場合にうまく動かず。原因まで特定もできました。

最小公倍数を計算する際、大きい数になり1000000007で一度割ってしまうと最小公倍数を正しく求められなくなるというものでした。
色々考えた結果解消できず。。

解説にもある最小公倍数を素因数分解した形で保持するというのも考えたのですが、実力不足でコードにできませんでした。
(ちょっとまだどうすればいいかイメージも出来ていないので、平日仕事終わりにまた考えてみます。。)

##[問題F](https://atcoder.jp/contests/abc152/tasks/abc152_f)

問題Eがもうちょっとでできる気がしたので手を付けれていないです。。


----

レーティングは897→956。最高更新です！

D問題までは25分とサクサクできて、成長も感じれたのですが
E問題の逆元使うところまでは思いつけたのに、あと一歩及ばずでとても悔しいです。。

次こそは学びをきちんと形にしたいです・・・:sunny:
Agile開発に必要なJIRAチケットの書き方

先週よりチームメンバーも増え、そろそろJIRAを運用してしっかりとタスク管理をしなければ思ってチケットを書き始めた時のこと。

## ざっくりと書いてみるパターン
まず最初に書いたのはこんな感じ

```
サブスクリプション課金システムを実装する
```

一人でガリガリコードを書いていた時は実際にこういうタスクをメモ帳に積んでいたので取り敢えず書いてみる。
しかしなんとなく違和感…
1. ツールや実装方法は具体的に書かなくて良いんだろうか？
2. 渡された人の「サブスクリプション課金」の実装に必要な要素の認識は私と同じだろうか？

自分の為だけに書いてあるのであればこれでいいのだが、「ロケットで月に行く」みたいな書き方に少し違和感があったのでもう少し詳しくなるように書き直してみた。

## 詳細を書くパターン
というわけで、実装方法やDB設計までをそれぞれのチケットに落としてみた

```
1. XXX（Stripeとか課金プラットフォーム）に開発用アカウントを作成する
2. サブスクリプション課金システムをフロントエンドから叩くAPIを実装
3. サブスクリプションのフロントエンドUIを実装
4. サブスクリプション課金成功時にフロントエンドからバックエンドへ通信するPost Subscription APIを実装
5. サブスクリプション課金成功時にフロントエンドからバックエンドへ通信した際にバックエンドへサービスへ課金確認をするロジックを実装
6. サブスクリプション課金成功時にバックエンドに課金ログを残すようにDB（subcription_logs）を実装
7. ユーザーの現在のサブスクリプション状況を確認できるようにDBを改変（userテーブルにis_subscribed:boolを追加）
8. サブスクリプション課金動作のテストケースを実装する（フロントエンド）
9. サブスクリプション課金動作のテストケースを…
```
とこの辺まで書いて気づくのが、これ全部書いてたらきりがない。

新規サービスの開発なのでもちろんサブスクリプションだけを作るわけじゃないし、実装内容をいちいち全部書いていたらチケットを書いているだけで1週間ぐらいかかってしまうと。
そしてもうひとつ書いていて思ったのが「これが本当に正しい実装方法なのか、**自分の知らないもっといい方法**があるのではないのか」ということ。このチケットは当然自分の知識がベースになっているので、必ずしも最良ではない可能性があるということに気づいてしまった。

実際、昔開発していた現場でも「チケットにこう書いてあるからそのまま実装する」という状態で、あとから別のもっといい開発方法を見つけることが何度かあった。

## 結局どうしたか
今までのチケットの書き方の問題点は
1. 開発者としっかりと意思疎通できる内容であるか
2. チケット内容が開発者の自主性やポテンシャルを阻害する要因となっていないか（マネージメント層の知識が組織のスキルキャップになっていないか）

と少々悩み、自分が以前Scrum Masterとしてチケットを書いていた方法で書き直してみる。それがこちら

```
1. ユーザーとして、サブスクリプション制の課金システムで課金ができるにする。
2. 運営として、各サブスクリプションの履歴を参照できるようにする。
3. 開発者として、サブスクリプションシステムが正常に動作しているか定期的にチェックしたい。
```
やや独特の書き方であるが書いてみてこれがやっぱり一番しっくりときた。
チケットに実際の実装方法を書かず、アウトプットの価値（とそれが誰に対してか）を記しているので、開発者に対して制限を設けず、かつその人の自主性を重んじる内容となる。このあと、アサインされた開発者はPull Requestベースで作業方針を示していく流れとなる。（その他必要条件がある場合は事前に1on1でフォローする）

## Manifesto for Agile Software Developmentを振り返る
もちろんこの書き方の前提条件には、開発チームの全員がある程度のスキルレベルに達している必要がある。ジュニアレベルのメンバーや外部連携ではもっと詳細を書く必要があると思う。

ただ、少数精鋭の開発チームであればこれはアジャイル開発手法として有効なチケットの書き方だと思う。
アジャイルソフトウェアの根底にある考えは以下だ（Agile開発に携わったことのある人は耳にタコができるほど聞いたことがあるだろうけど…

- プロセスやツールよりも**個人と対話**を、
- 包括的なドキュメントよりも**動くソフトウェア**を、
- 契約交渉よりも**顧客との協調**を、
- 計画に従うことよりも**変化への対応**を、

　　　　　　　　　　　　　　　　　　　　　　　　　　　　([アジャイルソフトウェア開発宣言より](https://agilemanifesto.org/iso/ja/manifesto.html))

上のチケットの書き方はScrum Master認定の講習会でも説明を受けるものだが、開発においてのこの4つの考えを守るのに最適なものだと考えている。


Agile開発をすでにしている方には当たり前の話かもしれないですが、久しぶりにAgile開発に戻ってきての嬉しい気付きでした。
AtCoder Beginner Contest 152 参戦記

# AtCoder Beginner Contest 152 参戦記

## [ABC152A - AC or WA](https://atcoder.jp/contests/abc152/tasks/abc152_a)

1分半で突破. 書くだけ.

```python
N, M = map(int, input().split())

if N == M:
    print('Yes')
else:
    print('No')
```

## [ABC152B - Comparing Strings](https://atcoder.jp/contests/abc152/tasks/abc152_b)

2分で突破. 書くだけ.

```python
a, b = map(int, input().split())

if a < b:
    print(str(a) * b)
else:
    print(str(b) * a)
```

## [ABC152C - Low Elements](https://atcoder.jp/contests/abc152/tasks/abc152_c)

5分で突破. 流石に二重ループは TLE なので、現時点の最小値を持ち回す必要あり. 題意を理解するのに少し時間を使った.

```python
N = int(input())
P = list(map(int, input().split()))

result = 0
m = P[0]
for i in range(N):
    if P[i] <= m:
        result += 1
        m = P[i]
print(result)
```

## [ABC152D - Handstand 2](https://atcoder.jp/contests/abc152/tasks/abc152_d)

26分で突破. N回ループ回しても大丈夫だと見切れればさして難しくはない. 先頭と末尾の組み合わせで個数を集計すれば一発.

```python
N = int(input())

t = [[0] * 10 for _ in range(10)]
for i in range(1, N + 1):
    s = str(i)
    t[int(s[0])][int(s[-1])] += 1

result = 0
for i in range(1, 10):
    for j in range(1, 10):
        result += t[i][j] * t[j][i]
print(result)
```

## [ABC152E - Flatten](https://atcoder.jp/contests/abc152/tasks/abc152_e)

敗退. 最小公倍数を求めて集計するナイーブな実装では TLE だった.

追記: 解説通り実装した.

```go
package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

func mpow(x int, n int) int {
	result := 1
	for n != 0 {
		if n&1 == 1 {
			result *= x
			result %= 1000000007
		}
		x *= x
		x %= 1000000007
		n >>= 1
	}
	return result
}

func main() {
	maxA := 1000000

	N := readInt()
	A := make([]int, N)
	for i := 0; i < N; i++ {
		A[i] = readInt()
	}

	sieve := make([]int, maxA+1)
	sieve[0] = -1
	sieve[1] = -1
	for i := 2; i <= maxA; i++ {
		if sieve[i] != 0 {
			continue
		}
		sieve[i] = i
		for j := i * i; j <= maxA; j += i {
			if sieve[j] == 0 {
				sieve[j] = i
			}
		}
	}

	lcmFactors := map[int]int{}
	for i := 0; i < N; i++ {
		t := map[int]int{}
		a := A[i]
		for a != 1 {
			t[sieve[a]]++
			a /= sieve[a]
		}
		for k, v := range t {
			if lcmFactors[k] < v {
				lcmFactors[k] = v
			}
		}
	}

	lcm := 1
	for k, v := range lcmFactors {
		for i := 0; i < v; i++ {
			lcm *= k
			lcm %= 1000000007
		}
	}

	result := 0
	for i := 0; i < N; i++ {
		result += lcm * mpow(A[i], 1000000007-2)
		result %= 1000000007
	}
	fmt.Println(result)
}

const (
	ioBufferSize = 1 * 1024 * 1024 // 1 MB
)

var stdinScanner = func() *bufio.Scanner {
	result := bufio.NewScanner(os.Stdin)
	result.Buffer(make([]byte, ioBufferSize), ioBufferSize)
	result.Split(bufio.ScanWords)
	return result
}()

func readString() string {
	stdinScanner.Scan()
	return stdinScanner.Text()
}

func readInt() int {
	result, err := strconv.Atoi(readString())
	if err != nil {
		panic(err)
	}
	return result
}
```
count(*) するときに気をつけたい NULL


```sql

create table contents (id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY)
comment='コンテンツ';
insert into contents values (), (), (), (), (), ();
create table view_logs (id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, content_id BIGINT NOT NULL)
comment='コンテンツに対する閲覧ログ';
insert into view_logs (content_id) values (1), (2), (3);
select * from contents;
select * from view_logs;
```
今回は簡便さを優先して、外部キー制約は付けていません。

|コンテンツ | 閲覧ログ |
| --- | --- |
| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38967/8c9c96b6-cedf-3250-d885-a31749a761da.png) | ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38967/521f4f23-0136-396a-a3c9-3ffaf95cf2ae.png) |
| 6レコード | 3レコード。コンテンツIDが4,5,6は閲覧ログがありません。 |

# 各コンテンツの閲覧回数は何回でしょうか？
次のようになるはずです。

|コンテンツID | 閲覧回数 |
| --- | --- |
| 1 | 1 | 
| 2 | 1 | 
| 3 | 1 | 
| 4 | 0 | 
| 5 | 0 | 
| 6 | 0 | 

次にSQLを書いてみます。

# 失敗例
## 内部結合を使ってカウント
INNER JOINで閲覧ログとコンテンツを結合してしまってはいけません。閲覧ログの中にすべてのコンテンツIDが現れないためです。

閲覧ログにあるコンテンツIDしか閲覧数をカウントすることができません。

```sql

SELECT
	ct.id AS content_id,
	count(*) AS view_count
FROM
	contents AS ct
	JOIN view_logs AS vl ON ct.id = vl.content_id
GROUP BY
	content_id
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38967/b9240ddf-d4d2-4444-81ca-71fe35637a92.png)

## 外部結合を使ってカウント
閲覧ログとコンテンツIDを内部結合すると、閲覧ログに存在しないコンテンツIDがカウントできませんでした。

それなら、コンテンツIDを基準に外部結合をすれば、すべてのコンテンツIDが網羅できます。
しかし、安直に `count(*)` を使うと罠にハマります。

```sql

SELECT
	ct.id as content_id
	,count(*) as view_count
FROM
	contents AS ct
	LEFT OUTER JOIN view_logs AS vl ON ct.id = vl.content_id
group by
	ct.id
```

**閲覧ログがないはずのコンテンツID(4,5,6)が閲覧回数が1になっています。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38967/ecbb4732-350f-3f6c-31a3-5ee8b4188edd.png)

これは、 `count(*)` によって行単位でカウントされてしまっていることが原因です。

> COUNT(*) は行数をカウントするため、ペットの数をカウントするクエリーは次のようになります。

出典：https://dev.mysql.com/doc/refman/5.6/ja/counting-rows.html

> COUNT 関数には NULL を件数に含めるために特別な アスタリスク（*）を使用する構文が用意されており NULL を含めて行数を取得することができる。

出典：https://www.shift-the-oracle.com/sql/aggregate-functions/count.html

### count する前のレコードを観察する
countする前のレコードを見ると、レコードの数は6で、content_idが4,5,6にはlog_idがNULLになっています。

```sql

SELECT
	ct.id AS content_id,
	vl.id AS log_id
FROM
	contents AS ct
	LEFT OUTER JOIN view_logs AS vl ON ct.id = vl.content_id
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38967/5d91c9a7-2bcb-3d23-6654-497167529f38.png)

`count(*)` はレコードが存在すればカウントするため、すべてのコンテンツIDについて **1** とカウントします。

# 成功例
## カウントしたいカラムを指定して外部結合を使う
view_logs の content_id カラムか id カラムを `count()` に与えれば、求めていた結果が得られます。

`count()` には式が入るようになっていて、 `NULL` を与えた場合 **0 とカウント** するため、 `*` ではなく `NULL` になりうるカラム名を与えれば、正しくカウントできます。

```sql

SELECT
	ct.id as content_id,
	count(vl.content_id) as view_count
FROM
	contents AS ct
	LEFT OUTER JOIN view_logs AS vl ON ct.id = vl.content_id
group by
	ct.id
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38967/e9a4e09c-63d5-3938-e57e-e82a71aabc89.png)

# 実験後の後始末
不要になったテーブルはサクッと消して終了です。

```
drop table view_logs;
drop table contents;
```