OmniAICreator/Qiita-1.07M huggingface dataset:
Rails6 エラーメッセージの日本語化

Rails6.0のエラーメッセージを日本語にする方法を忘れないように記事にします。
間違いなどありましたらご指摘いただければ幸いです。


## 準備

```Ruby:app/models/post.rb
class Post < ApplicationRecord
 validates :text, :image, presence: true
end
```

上記は例です。
モデルにバリデーションを設定します。

```Ruby:app/controllers/posts_controller.rb
def create
  @post = Post.new(post_params)
  if @post.valid?
    @post.save
    redirect_to root_path
  else
    render 'new'
  end
end
```

上記は例です。
コントローラーで保存処理の部分に条件分岐を記載します。
保存できればルートにリダイレクトして、保存できなければ投稿画面に戻る等です。

次にエラーメッセージ用のviewファイルを作ります。
`view/layouta`配下に`_error.html.erb`のような名前でファイルを作成します。
部分テンプレートで使いまわせるように`_(アンダーバー)`を忘れずに。

```Ruby:app/views/layouts/_error.html.erb
<% if model.errors.any? %>
<div class="error">
  <ul>
    <% model.errors.full_messages.each do |message| %>
    <li class='error-message'><%= message %></li>
    <% end %>
  </ul>
</div>
<% end %>
```

上記のようにどのモデルでも対応できるように`<% if model.errors.any? %>と記述します。エラーメッセージを`each do`で`message`に入れて表示させます。

```Ruby:×××.html.erb
<%= render 'layouts/error', model: form.object %>
```

エラーメッセージを表示させたい部分には上記のように記述します。
うまく保存の処理ができなかった場合にrenderメソッドで`_error.html.erb`を呼び出し表示させます。


## 言語設定

```Ruby:config/application.rb
module <アプリ名>
 class Application < Rails::Application
   # Initialize configuration defaults for originally generated Rails version.
   config.load_defaults 6.0

   # 以下を追加
   config.i18n.default_locale = :ja
　　# 省略
 end
end
```

上記のように`config/application.rb`を`config.i18n.default_locale = :ja`と編集します。

## Gemの導入
日本語に対応しているlocaleファイルである`rails-i18n`というGemを導入します。
[rails-i18n](https://github.com/svenfuchs/rails-i18n)
[rails-i18nに登録されている日本語](https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml)

```Ruby:Gemfile
gem 'rails-i18n'
```

Gemfileの一番下に`gem 'rails-i18n'`と記述します。

```Ruby:ターミナル
bundle install
```

忘れず`bundle install`してください。


ここまで実施するとエラーメッセージが「Textを入力してください」みたいに中途半端な日本語で表示されます。
ここから完全に日本語にしていきます。

## 日本語用localeファイル作成

localeファイルは`config/locale`配下にあります。
デフォルトでは`en.yml`というファイルがあり、
deviseを導入していれば`devise.en.yml`というファイルが存在していると思います。

`config/locales`配下に`devise.ja.yml`というファイルを作成します。
[devise-i18n]8https://github.com/tigrish/devise-i18n/blob/master/rails/locales/ja.yml)
上記のサイトのコードを全てコピーして`devise.ja.yml`に貼り付けます。
少しでもインデントが変わるとエラーになるため気をつけてください。

このままだと自分で作成した`Nickname`とか`Image`とかのカラムはまだ日本語にならないので
新たに`ja.yml`というファイルを`config/locale`配下に作成します。

```Ruby:config/locale/ja.yml
ja:
 activerecord:
   attributes:
     user:
       nickname: ニックネーム
     tweet:
       text: テキスト
       image: 画像
```

上記のように自分で作ったカラムをどう表示させたいか記述します。
こちらも少しでもインデントが変わるとエラーになるので注意です。

これで全て日本語化したエラーメッセージできました。

## deviseのエラーメッセージ日本語化

ちなみにdeviseでユーザー認証を取り入れている場合は新規登録は元々用意されているもので特に変更しなくてOKですが
ログインについては少し変更が必要みたいです。

```Ruby:app/views/devise/sessions/new.html.erb
<div class="contents row">
  <div class="container">
    <div class="new-head">
      <h2>ログイン</h2>
    </div>

    # 以下を追加
    <div class='login-flash-message'>
      <%= flash[:notice] %>
      <%= flash[:alert] %>
    </div>
    <%= form_with model: @user, url: user_session_path, id: 'new_user', class: 'new_user', local: true do |f| %>
    # ここまで追加
  # 省略
```

上記のようにviewを変更することで対応できます。

以上です。
【LOBE入門1日目 AIが画像分類をする】インストールと分類デモ

ノンコードで画像を判別するAI（分類器）を作る

作業時間は5～10分です。
※Surface pro7(corei5,メモリ8GB, Win10)

【Step1】　Lobeのインストール
①こちらからファイルをダウンロードして実行する
https://lobe.ai/
必要な情報を入力して、download
![lobe00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/84798734-1793-b881-31e4-8462781eb576.png)

②Next、Finishを連打。（インストールってAgreeを強いられている気がする）

![lobe01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/8aa0ba48-5311-65db-9c61-fb5f7ee02058.png)
【この間、約2分】
![lobe03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/2549100a-093e-847e-0d77-9a80e47e19a5.png)
Finishを押して、LOBEを起動
![lobe04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/24dbdbc3-e0e4-0b6c-3809-a52313b09184.png)
（強いられている）
![lobe05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/1fa1b7d9-6033-9aa3-9b17-058f1f2f649b.png)
（まだ強いられている）
![lobe06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/20cf8cbc-ec1f-6f9f-6a7e-62c7ca9b82d1.png)

【Step2】　教師データの登録
プロジェクト名は左上のバーで変更できますが。
LOBEは基本的に①教師データの登録、②学習、③データの投入、の３Stepです
では、右上の【import】からデータを登録しましょう
![lobe07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/e5e8a7c9-3e22-7a1e-f212-ba4ef528e75a.png)
【Camera】を押すと、端末のカメラで撮影できますが
今回は登録されている【Images】から登録しましょう
スクレイピングで収集した鬼のデータをまとめて選択
![lobe09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/4284eed6-e0c1-de03-469e-75f42d424606.png)
登録すると画像の左下に、分類名を登録できるので【oni】で登録する
![lobe10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/25007826-423d-7229-50d9-8b8b6d31e4f9.png)
続いて、コントロール群のデータを登録
![lobe11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/d8e02ccc-b281-0877-79c9-d80d153ea2f7.png)
こちらは【hito】と名前を付けましょう
これで2分類の基盤ができました
![lobe12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/5ba8abfa-2f3f-2e66-1846-8140830d92e2.png)

【Step3】学習
実はLobeは勝手に学習が始まりますが
分類結果を途中で見ると、学習途中なのでとんでもなく精度が悪いです
気長に待ちましょう
![lobe12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/97839a2f-2ef0-7e23-7c90-71fac4c5a35d.png)
分類精度は左側のバーにも出ていますが、赤いバーは失敗の度合いなので
【バーが赤い】＝残念なAiとなります!
精度は95％。炭野郎は痣が出ているし、他の鬼は顔の模様が薄い・・・
そこらへんが失敗の原因でしょうか
![lobe08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/19c92ebf-2673-6d43-847e-402a2c44f863.png)
富師匠・・・なぜお前がミスる
※多分、顔の面積が小さく湯呑や服の模様に引っ張られている

【Step4】データの投入
右上の【Camera】を押すと、端末のカメラを使えます
人間の顔はほとんど【hito】になりますが、
側面からライトを当てる→顔に影ができて、濃淡差がでる
マスクをつける→実はマスクって、影ができやすいので濃淡差がでる
そんな理由であえて、失敗した画像
![lobe13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/8866c153-dc29-32a9-3bf6-6162a437fab9.png)
右上の【Images】を押して、判別したい画像をドラッグすると判別できます
ためしにいらすとやの鬼を投入
![lobe14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167993/5aa9f715-7199-4cce-2295-faaeed18694b.png)
本当に鬼になった・・・


こんな感じで10分程度で、簡単な画像判別をしてみました!
画像データもどこかで公開したいな

![Something went wrong]()
Unityを始める - オブジェクトの操作 -

# はじめに

Unity初心者が開発を始める場合に必要な事をまとめまています。
前回は、「[Unityの画面構成](https://qiita.com/nekoharuyuki/items/629ec2e07fb28a930e62)」についてまとめました。

Unityでゲーム開発を進める中で、知っておく必要があるオブジェクトの操作についてまとめます。

## 参考になる本
* [Unityの教科書 Unity 2020完全対応版　2D＆3Dスマートフォンゲーム入門講座](https://amzn.to/3owf0Fq)
* [UnityではじめるC#　基礎編](https://amzn.to/36Qfe4p)
* [作って学べる Unity本格入門](https://amzn.to/3mSD19t)

## 参考になるサイト
* [Unity User Manual 2020.2](https://docs.unity3d.com/ja/2020.2/Manual/UnityManual.html)

## 事前準備
### Unityのプロジェクトの作成

* UnityHubの起動
* 新しいプロジェクトの作成

詳細は「[Unityを始める - Unityインストールからプロジェクト作成 -](https://qiita.com/nekoharuyuki/items/cd845d58ecbac9ea4768)」にまとめました。

# オブジェクトの操作
ゲームオブジェクトの Transform コンポーネントを変更するには、マウスを使って軸を操作するか、インスペクターの Transform コンポーネントの数値フィールドに直接値を入力します。

## Sceneビューでのオブジェクトの操作
![MoveRotateScaleTransform.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/229633/b5495428-fc22-68e2-2eb9-3e0e66dc0dd1.png)

* 移動
平面上でオブジェクトを移動するには、オブジェクトの中心で、シフトキーを押したままクリックやドラッグを行います。オブジェクトをシーンビューのカメラが向いている方向に紐づけられた平面上で移動できます。

* 回転
オブジェクトの周りに表示されたワイヤーフレーム状の軸をクリックアンドドラッグすることにより、オブジェクトを回転できます。

* 拡大縮小
すべての軸に対して均等にオブジェクトを拡大縮小することができます。また、それぞれの軸を別々に拡大縮小することもできます。

## Inspectorビューでのオブジェクトの操作
![スクリーンショット 2021-02-05 23.11.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/229633/ae588e3e-38bf-1e3b-6c0b-c00c965a655c.png)

* 移動
 * transformのPositionを変更して移動させる
* 回転
 * transformのRotationを変更して回転させる
* 拡大縮小
 * transformのScaleを変更して拡大縮小させる

# まとめ
今回はUnityのオブジェクトの操作についてまとめました。
使い方を知っておく事で、今後のUnityによるゲーム開発のスピードや精度を一気に高める事ができます。
複数変数の正規化処理：独立にfitしてtransformしてinverse_transformする

# やりたいこと

StandardScalerやMinMaxScalerで正規化処理をするときに、ある変数だけinverse_transformしたいときなどがあります。しかし、n変数まとめてfitしてしまっていると、特定の変数だけを逆変換するわけにはいきません。

非常にナイーブな実装ですが、n変数に対してn個のscaler()を用意して、リストに格納してぶん回します。
これだとあまりに記事にするほどの価値がないので、このリストに入ったScalerをsave/loadして使いまわす方法も紹介しておきます。


# やったこと

変換対象の配列を用意します。ここでは3変数とします。

```python
ar1 = np.arange(10)
ar2 = np.arange(10)+10
ar3 = np.arange(10)+20
arrs = np.block([[ar1], [ar2], [ar3]])
arrs = arrs.T

print(arrs)
# array([[ 0, 10, 20],
#        [ 1, 11, 21],
#        [ 2, 12, 22],
#        [ 3, 13, 23],
#        [ 4, 14, 24],
#        [ 5, 15, 25],
#        [ 6, 16, 26],
#        [ 7, 17, 27],
#        [ 8, 18, 28],
#        [ 9, 19, 29]])
```

Scalerをn個(この例では3つ)用意します。

```python
from sklearn.preprocessing import StandardScaler, MinMaxScaler

n = arrs.shape[1] # 変数の数, ここでは3
scaler_list = [StandardScaler() for i in range(n)]
```

for文でぶん回しながらfitします

```python
for i in range(3):
    scaler_list[i].fit(arrs[:,i].reshape(-1, 1))
```

各列について`transform`していきます。
格納先として乱数の入った配列`trans`を用意しておきます。
ポイントは、２つ。
1. 一次元の配列は(m, 1)次元にしないと受け付けてもらえないのでreshape(-1,1)しているところ
2. それを配列`trans`に格納するときには逆に(m, )というshapeに戻しておかないといけないので`np.ndarray.squeeze()`しているところ

```python
trans= np.random.random(arrs.shape)
for i in range(n):
    trans[:,i] = scaler_list[i].transform(arrs[:,i].reshape(-1, 1)).squeeze()

print(trans)
# array([[-1.5666989 , -1.5666989 , -1.5666989 ],
#        [-1.21854359, -1.21854359, -1.21854359],
#        [-0.87038828, -0.87038828, -0.87038828],
#        [-0.52223297, -0.52223297, -0.52223297],
#        [-0.17407766, -0.17407766, -0.17407766],
#        [ 0.17407766,  0.17407766,  0.17407766],
#        [ 0.52223297,  0.52223297,  0.52223297],
#        [ 0.87038828,  0.87038828,  0.87038828],
#        [ 1.21854359,  1.21854359,  1.21854359],
#        [ 1.5666989 ,  1.5666989 ,  1.5666989 ]])
```

これを`scaler.inverse_transform()`で戻します。さっきのコードをちょっとだけいじれば終わりですね。ここでも格納先に`inv`という配列を用意しておきます。

```python
inv = np.random.random(arrs.shape)
for i in range(n):
    inv[:,i] = scaler_list[i].inverse_transform(res[:,i].reshape(-1, 1)).squeeze()

print(inv)
# array([[ 0, 10, 20],
#        [ 1, 11, 21],
#        [ 2, 12, 22],
#        [ 3, 13, 23],
#        [ 4, 14, 24],
#        [ 5, 15, 25],
#        [ 6, 16, 26],
#        [ 7, 17, 27],
#        [ 8, 18, 28],
#        [ 9, 19, 29]])
```

無事に元の配列`arrs`に戻っていることが確認できました。成功です。今回はすべてnumpy.ndarrayに閉じていますが、pandas.DataFrameに格納しておけば必要な時に特定のカラムだけ逆変換して戻すといったことができそうです。そのときは`scaler_list`の何番目がどのカラムに対応しているのか、という対応表を辞書で持っておくと安全でしょうか。

Scalerでこういうことができるということは、sklearn.preprocessingに入っているメソッド(のオブジェクト)なら同じことができると思います。
Next.js + Webpack5 + Dockerでファイル変更時にリロードをできるようにしたメモ

## 概要
next.jsで開発サーバでの検証中、ファイルを保存したときに、自動でブラウザが更新されるようにしたかった。
ホットモジュールリロードとか、Next.js 9.4以降ではFast Refresh とか言われてるやつ。
Dockerで動かすときは、`CHOKIDAR_USEPOLLING=true`の環境変数を設定すればよかった([*](https://create-react-app.dev/docs/troubleshooting/#npm-start-doesnt-detect-changes))が、webpack5を利用するようにしたら動かなくなった。
リロードができるように調査を行う。

## 結論
* next.config.jsの設定でポーリングを指定で解決。
* `CHOKIDAR_USEPOLLING`の環境変数設定は不要になった。

```js:next.config.js
/* eslint-disable @typescript-eslint/no-var-requires */
const nextConfig = {
  future: { webpack5: true },
  webpack: (config) => {
    config.watchOptions = {
      aggregateTimeout: 200,
      poll: 1000,
      ignored: /node_modules/,
    }
    return config
  },
}

module.exports = nextConfig
```


[参考用ソース](https://github.com/hibohiboo/kotatool/tree/0e7c886927d8012769aa7bbaa57dffa9f99fbe46)



## 調査


* webpack5のリリースを読むと、chokidarを利用しなくなっている。[（参考：webpack news)](https://webpack.js.org/blog/2020-10-10-webpack-5-release/#new-watching)

* webpack方式でpollingの設定を行う。[（参考：webpack configuration)](https://webpack.js.org/configuration/watch/#watchoptions)
`npm run dev` ... 以下のエラーが出るようになった。

```
Watchpack Error (initial scan): Error: ENOTDIR: not a directory, scandir '/app/node_modules/next/dist/next-server/lib/router/utils/resolve-rewrites.js'
```

* ignoredに/node_modules/を追加することで解決。
ifで空配列を渡すと実行しない

## 動機

空配列, false もしくはデータが入った配列
これを判断し、空配列とfalseの場合、即時falseを返したい。

そのため、以下の書き方をした。

```php
$array = false, もしくは [], もしくは ["a" => "b"];
if(!$array || count($array) == 0) return false;
```

`!$array`が先に来ないと,`count(false)`してしまいエラーが出る。

その際に、`!$array`って空配列の場合どのような挙動をするのか気になった。

## ifで空配列を渡すと実行しない

```php
if([]) echo "not show";
if(["a" => "b"]) echo "show";
```

ifで空配列を渡してあげると実行しないことがわかった。

つまり以下の方法でも良い

```php
if(!$array) return false;
```
この記法だと後でバグフィックスするのは骨が折れそう。

## 個人プロジェクトでよく使う手法

`if()`の挙動として、以下のようになる

- `if("0") echo "not show";`
- `if(0) echo "not show";`
- `if("文字列") echo "show";`
- `if(1) echo "show";`

これを使えば、トリッキーだが例外処理等を使用せずに、関数やオブジェクトの戻り値の条件分岐が楽になる。

```php
function ret($i = 0){
  if($i == 0) return "moji";
  return false;
}
if(ret()) echo "show"; // show
echo (ret()) ?: 'not show this string'; //moji
```

ただ、php7やphp8で`==`や`empty`,`isset`等の挙動が変わっている。
現状、ifでは文字列や配列を突っ込んでも動いてくれるが、将来の仕様変更を考えるとあまり褒められた方法ではない。

実際に処理している内容は以下のような物と考えるとわかりやすい

```php
if($string == true)
```

## もっと良い解決方法

```php
$array = false, もしくは [], もしくは ["a" => "b"];
if(empty($array)) return false;
```

そもそも、`empty`という関数があるんだからそれ使えよ。と遅まきながら気づいた。

---
追記2021/02/16

今回紹介したのは`if`に直接文字列や配列を入れて判断するものだが、`if(`内部`)`で判断する場合には更にシビアになる。

```php
PHP ~7
if(0 == "abc") echo "show";
PHP 8~
if(0 == "abc") echo "not show";
```
これは以下のように処理されていると考えるとわかりやすい

```php
php ~7
if(0 == (int) "abc") echo "show";
```

まとめると、バージョンの違いにによっては動かなくなる可能性もあるので、
自分でメンテナンスできる環境以外では使わないようにすることを勧める。
MacBook Pro M1に買い替えたときの移行メモ（TimeMachine）

# 方針
移行アシスタントというのを使うと手作業でファイルコピーする手間を省けます。
Mac、TimeMachineバックアップ、起動ディスクといったいくつかの種類がありますが、
移行中も旧Macを使える**TimeMachineバックアップ**を選択しました。


# 用途
主にWEBサイトの開発環境として使用します。
よく使うソフトは以下です。
Chrome, Docker, ターミナル, Vim, Dropbox, SourceTree, FileZilla,
AdobeXD, Photoshop, Illustrator, Acrobat
Excel, Word, PowerPoint, Keynote,
Slack, Skype


# 環境
・買い替え前
MacBook Pro (13-inch, 2016, Two Thunderbolt 3 ports)
macOS Mojave 10.14.5
メモリ 16GB
ストレージ 256GB

・買い替え後
MacBook Pro M1チップ
メモリ 16GB
ストレージ 2TB



# 移行方法
公式ドキュメントに詳しくのっているので、下手に僕の記事を見るより公式を参考にするのを推奨します。
わからなければチャットサポートを利用すれば色々教えてくれるはずです。
[バックアップから Mac を復元する](https://support.apple.com/ja-jp/HT203981)

・移行開始時刻
2/4 20:50
・移行完了時刻
2/4 22:00

もっと色々設定に苦労するかと思ったのですが、割とすぐ終わりました。
とはいえM1チップは今までのMacと違い、うまく動作しないソフトが出てくる可能性があります。
使ってみて何かあれば追記しようかと思います。


# 移行後の状況
## トラブル系
### キーボードが入力できない
Macのログイン時にはキーボードが入力できるのに、その他の入力ができなかったです。
Google日本語入力が原因。
キーボードの入力設定から「日本語（ローマ字入力）」と「英語」を追加すればOK。
以下のような感じで設定しました。
![スクリーンショット 2021-02-04 22.44.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118677/0d54dea8-07ce-d13d-7605-3336fd813891.png)


## 各ツールの使用感
### Docker
まだ未確認

### node（特にnvmでのバージョン管理）
特に問題なさそう。
そのまま使えました。

### Apache
特に問題なさそう。
そのまま使えました。
色々とApacheの設定ファイルをいじっていたのですが、特に何事もなく使えて感動。
以前OSをアップデートしたときは設定に苦労したんだけどな・・

### MySQL
特に問題なさそう。
そのまま使えました。
MacにMySQLをインストールすると色々と設定やらバージョンやらbrewやら何やらでトラブルがあったものですが、特に何事もなく使えました。感動。

### git
ターミナル上で使うとエラー

```
xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
```

以下コマンドで解決

```
xcode-select --install
```

### MacVim
起動するとなんか点滅する。
[こちらのMacVim](https://macvim-dev.github.io/macvim/)を入れたらとりあえずは解決。
ただ、細かい設定はし直さないとだめそう。
KaoriYaさんのvimを愛用されている方は注意。
[こちらの記事](https://qiita.com/watagashi/items/8f3c01cb37ecf9eb5ce0)を参考にすればいけるのかな・・？まだ未確認。


### Spotlight
索引データが初期化されているせい？なのか、Spotlightからアプリケーションの起動がなかなかできない。
地味に面倒。
これはもうしばらく触っていくしかないかな・・


### Dashboard
どうやらBig Surでなくなった様子・・？
代わりに通知センターという機能で代用するらしい。
https://howchoo.com/macos/get-dashboard-on-macos-big-sur

今まで3本指でスワイプとかでダッシュボード画面を出していたのでかなり違和感。
それと、計算機ウィジェットがなくなっていたので使っていた人は別の方法で代用する必要あり。
他のAppを入れるか、Dockに計算機を持ってくる方法しかないみたいです。
正直Dashboardの廃止は改悪だと思う。
[Swift]UITextViewで指定の文字数以上入力できないようにする(リアルタイムで入力内容をチェックする)

##やりたいこと
UITextView内で文字を入力時に、指定の文字数を超えて入力できないようにする。

ちなみに、この処理方法を応用すれば

* 数字を入力できないようにする
* スペースや改行を入力できないようにする

などの、リアルタイムで入力をチェックする処理を実装可能かと思います。


##実装の流れ

基本的な実装の考え方は以下になります

* TextViewの内容が変わろうとするときに
* 入力された変更を加えた後の文字数が140文字を超える場合、入力された変更を受け付けない

下記の流れで実装します。

1. ViewControllerをUITextViewDelegateプロトコルに準拠させる。
1. textView(_:,shouldChangeTextIn:replacementText) メソッドを追加する。
1. textView(_:shouldChangeTextIn:replacementText)メソッド内に、変更後の結果文字数を計算して、140文字以下ならtrue、140文字超ならfalseを返すようにする。


##実装方法

前提：[UITextViewに文字を入力中に、ラベル内の文字数表示を自動更新する](https://qiita.com/addson/items/84ace0312eb6499a6de4) の状態。


```swift:RealtimeCountViewController.swift
class RealtimeCountViewController: UIViewController, UITextViewDelegate {
    
    @IBOutlet weak var memoTextView: UITextView!
    @IBOutlet weak var countLabel: UILabel!
    
    let maxCharacterCount = 140

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        memoTextView.delegate = self
    }
    
    //文字数カウント表示を更新する。
    func textViewDidChange(_ textView: UITextView) {
        countLabel.text = String(format: "%d/%d",textView.text.count, maxCharacterCount)
        
        if textView.text.count > maxCharacterCount {
            countLabel.textColor = .red
        } else {
            countLabel.textColor = .systemGray
        }
    }

    //文字数が140文字を超えないようにする。
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        //入力された変更を行った場合の文字数を計算。
        let newCharacterCount = textView.text.count - range.length + text.count

        //140文字以下でtrueを返し、140文字超でfalseを返す (手抜き)
        return (newCharacterCount <= maxCharacterCount)
    }
    
}
```


##動作確認環境

下記２環境で動作を確認しました。

Xcode: 11.7
iOS: 13.7

Xcode: 12.4
iOS: 14.4

以上
【VisualForce】演算子 '=' のパラメータ種別が間違っています。期待テキスト、受信数字



# 演算子 '=' のパラメータ種別が間違っています。期待テキスト、受信数字
VisualForceでちょっと詰まったので備忘録。

```html:親page

<apex:variable var="pageNumber" value="1" />
<!-- repeatというループすることができるVisualForceのタグ -->
<apex:repeat value="{!viewModel.displayListEachPage}" var="displayList">
  <!-- コンポーネント呼び出し -->
  <c:COMPONENT pageNumber='{!pageNumber}' />
  <!-- pageNumberをインクリメント -->
  <apex:variable var="pageNumber" value="{!VALUE(pageNumber)+1}" />
</apex:repeat>
```
親ページからコンポーネントにpageNumberを渡す

```html:component
<apex:component>
  <!-- 親ページからページ数をもらうとする。型はInteger、必須項目 -->
  <apex:attribute name="pageNumber" type="Integer" description="ページ数" required="true" />

  <!-- outputPanelのrenderedでIF関数を使用 -->
  <apex:outputPanel rendered="{!IF(pageNumber == 1, true, false )}">
　　　こんにちは
  </apex:outputPanel>
</apex:component>
```
attributeというタグで親ページからpegeNumberを受け取る。
outputPanelのrenderedという属性を使用してタグで囲ったコンテンツを描画するかどうか判別できる。

# エラー
画面を表示するとエラーが表示された。

```
演算子 '=' のパラメータ種別が間違っています。期待テキスト、受信数字
component c:COMPONENT のコンポーネント <apex:outputPanel> の式「{!IF(pageNumber == 1, true, false)}」にエラーがあります。
```

期待するのはテキストですよ。指定されているのは数字になってますよ。

と言ってそう。


# デプロイエラー

```html:VisualForce
  <apex:outputPanel rendered="{!IF(pageNumber == '1', true, false )}">
  </apex:outputPanel>
```
1をStringにした。

```
演算子 '<>' のパラメータ種別が間違っています。期待数字、受信テキスト
```
デプロイが通らない。


このやり方だといけたみたいな記事も見た

```html:VisualForce
  <apex:outputPanel rendered="{!(pageNumber == '1'}">
  </apex:outputPanel>
```
できない。


# 解決策

```html:VisualForce
<apex:component>
  <!-- 型をString -->
  <apex:attribute name="pageNumber" type="String" description="ページ数" required="true" />

  <!-- 数字をStringにした -->
  <apex:outputPanel rendered="{!IF(pageNumber == '1', true, false )}">
    こんにちは
  </apex:outputPanel>
</apex:component>
```

- apex:attributeのtype属性をIntegerからStringに変更
- apex:outputPanelのIF関数で等価する値をStringにする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190685/f9dd6f44-bbd3-2558-fede-573b4f879ffd.png)
表示された

IF関数を省略するしても大丈夫。

```html:VisualForce
  <apex:outputPanel rendered="{!(pageNumber == '1'}">
  </apex:outputPanel>
```
Railsアプリで一覧と詳細表示機能を実装する

#一覧表示機能を実装する
投稿された全ての投稿を確認するための一覧表示機能を作成します。
indexアクションで、投稿データをDBから取得し、indexのビューで出力します。

#一覧表示アクションで投稿データを取得する。
PostsControllerのindexアクションを以下のように書き換えます。
indexアクションでは、全投稿のデータをPost.allで取得し、ビューに出力するためのインスタンス変数に格納します。

```app/controllers/posts_controller.rb
  def index
    @posts =Post.all
  end
```
#一覧画面ですべての投稿を表示する
次にビューを実装します。@posts内の各Postオブジェクトの内容をテーブル形式で表示します。

```app/views/posts/index.html.slim
h1 タスク一覧

= link_to '新規投稿', new_post_path, class: 'btn btn-primary'

.mb-3
table.table.table-hover
  thead.thead-default
    tr
      th= Post.human_attribute_name(:content)
      th= Post.human_attribute_name(:created_at)
  tbody
    - @posts.each do |post|
      tr
        td= post.content
        td= post.created_at
```
![A33A1370-0D8F-4E59-8D6C-085A9FE4A43F.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/999125/141a8031-49b8-6f08-7fa8-df0469d72829.png)

#詳細表示機能を実装する
一覧画面内の投稿をクリックして、詳細画面に遷移する機能を作ります。
app/views/posts/index.html.slimの、投稿を表示している部分を以下に変えます。

```ruby:app/views/posts/index.html.slim
        td= link_to post.content, post
```

投稿部分がリンクになり、"post/[投稿ID]"という詳細表示のURLに遷移します。URLは、Railsが推測して作ってくれます。

続いてアクションを実装します。

```ruby:app/controllers/posts_controller.rb
  def show
    @post = Post.find(params[:id])
  end
```

findメソッドを使って詳細表示にするためのPostオブジェクトを取得しています。
params[:id]には、paramsから得られるid、リクエストされたURL"posts/[投稿id]"の[投稿id]が格納されています。

続いてビューを修正し、投稿(@post)の各属性を表示するようにします。

```ruby:app/views/posts/show.html.slim
h1 投稿の詳細

.nav.justify-content-end
  = link_to '一覧'、posts_path, class: 'nav-link'
table.table.table-hover
  tbody
    tr
      th= Post.human_attribute_name(:id)
      td= @post.id
    tr
      th= Post.human_attribute_name(:content)
      td= simple_format(h(@post.content), {}, sanitize: false, wrapper_tag: "div")
    tr
      th= Post.human_attribute_name(:created_at)
      td= @post.created_at
    tr
      th= Post.human_attribute_name(:updated_at)
      td= @post.updated_at
```

投稿内容は複数行にわたるテキストなので、改行コードを含む場合があります。改行コードはHTML自体の改行として扱われて、画面上では改行しているように見えません。文字列内の改行を\<br>タグなどに変換する必要があります。
今回のsimple_formatメソッドでは、デフォルトのsanitizeオプションをfalseにし、hというメソッドでXSS(クロスサイトスクリプティング)対策を施しています。また、デザイン上の制約でwrapper_tagで\<div>を指定しています。

これで、詳細表示機能が完成しました。
一覧画面で投稿をクリックすると…
![7693978D-8408-42E4-BFF8-00678791F2BB.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/999125/77aca4b9-32bd-8e6c-c04f-729cdfc6f3f7.png)

投稿の詳細が確認できました！

#参考
[現場で使える Ruby on Rails 5速習実践ガイド](https://www.amazon.co.jp/gp/product/B07JHQ9B5T/ref=ppx_yo_dt_b_d_asin_title_o03?ie=UTF8&psc=1)
GoでJpegからWebpへの変換

# 開発環境
- Windows10
- Visual Studio Code
- go version go1.15.5 windows/amd64

# Code

```go:main.go
package main

import (
	"log"
	"os"

	"github.com/chai2010/webp"
	"github.com/disintegration/imaging"
)

func main() {
	img, err := imaging.Open("haruchan.jpg", imaging.AutoOrientation(true))
	if err != nil {
		log.Fatal(err)
	}
	file, err := os.Create("haruchan30.webp")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()
	options := webp.Options{Lossless: false, Quality: 30}
	if err := webp.Encode(file, img, &options); err != nil {
		log.Fatal(err)
	}
	log.Println("ok.")
}
```

# その他
コード自体はほぼ[ここ](https://www.gitmemory.com/issue/disintegration/imaging/97/504658972)の写経なのですが、Optionsが設定されていなかったので、ロスレスでQualityを設定してみたのですが、そのサイズの激減ぶりに驚いた。家族の写真で試したので貼れないのですが4608x2592のデジカメ画像。視力は良い方ですが特に強い劣化を感じず。blog(写真ブログなどでなければ)ならdefaultでQuality:50でいいのでは？という感じ。自前サーバのWordPress pluginでjpeg/pngをwebpに変換しているのですが、こんなにも減っているとはなあと。

![1612532617742.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/595802/71dd7e13-a30c-b375-825c-d3539d5f3237.png)
[混ぜるな危険] npmに関して

[症状] なぜかnext/Imageが動作しなかった
[原因] node_modules/sharpのバイナリが'win32-x64'になっていた為、'linux-x64'環境では動かなかった。
[解決] 手動でnode_modules/sharpを削除し、再度`npm install sharp`を実行
[教訓] デュアルブート環境でReactを使う際には、OS毎にディレクトリを分ける。
テスト報告書について

##テスト報告書とは

※[こちら](https://qiita.com/kunrenyouAcount/items/2db802619ce6ea497516)の記事で紹介したテスト仕様書をもとに話を進めます。

まず「テスト報告書」自体は新しく用意する必要ありません。テスト仕様書にある結果欄と備考欄にテスト結果を記載したものがテスト報告書になります。

###エビデンス

次に必要なものが「エビデンス」です。英単語のebidenceは日本語に訳すと「証拠」に当たります。ここで使用するエビデンスも同義です。具体的に何がエビデンスに当たるかというと下記のようなスクリーンショット(以下「スクショ」と言う)になります
※windowsでのスクショのショートカット：[windowsボタン]+[PrtScrキー]
保存先：ピクチャ→スクリーンショット

■必要なスクショ
・ 画面遷移をする操作を実行する直前のスクショ
・ 画面遷移をする操作を実行した直後のスクショ
・ データベースに追加、更新、削除の処理を加える直前のスクショ
・ データベースに追加、更新、削除の処理を加えた直後のスクショ

ここで、良いスクショについて考えます。良いスクショはスクショだけを見てどんな処理を行ったのかがわかり、その結果までもスクショのみでわかることをいいます。
そこで注目していただきたいのは、「画面を遷移する操作(ボタンをクリックなど)を実行する直前のスクショ」です。ここで意味している直前というのは、下記のスクショのような必要項目が入力されており、マウスカーソルがクリックするボタンの上に載っている状態で、もうクリックだけすればよい状態という意味です。

![登録フォームの画像.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1066052/cd59f175-bf29-b05d-13c5-bddd99f54410.png)

またスクリーンショットは一つのテストだけでも4枚以上必要になることがあるため、スクリーンショットの命名規則などもあらかじめ決めておくとよいでしょう。ひとつひとつ名前を変えるのが大変であれば、テスト番号ごとにフォルダ分けをするという方法もあります。
テストフォルダを大量に作るときは[こちら](https://hiro2n.com/create-folder-993)の記事が参考になりました。


###チーム内でのルール決め
**※実はこれが一番大事です。時間をしっかりとりましょう！**
チーム内で細かくルールを決めましょう。「何が正解か」よりも「全員の認識が一致しているか」が大事です。これができていないと実施後に修正が必要になったり、再テストが必要になるので、必ず実施してください。口頭で確認を取るのはもちろん文章としても決めたルールを残して共有しておきましょう。
>
例）
・スクショのタイミング(操作の直前、捜査の直後、データベースの処理実行前、データベースの処理実行後)
・枚数(データベースの処理がない場合もあるため最低2枚)
・スクショの命名規則、フォルダ分け
・テスト実施した際に期待値が3回以上連続でずれた場合の対応(テスト作成者の意図がテスト実施者に正しく伝わっていない可能性有)
　→テスト作成者に状況を説明し、実施手順があっているか確認。補足が必要な場合や仕様書を書き換える必要がある場合にはリーダーに報告


さらにダメ押しで、ルールを決めたうえで実際に一人ずつテストを実施し、他の人はその作業を見ることでより認識のすり合わせができるかと思います。

ここまでできればテスト実施を始めてください。
Windows10HomeのDocker環境でJupyterNotebookを起動する時のポート指定方法

# 【Windows10Home＋Docker】でJupyterNotebookを起動する時の注意

Windows10HomeのDocker環境でJupyterNotebookを起動しようとしてはまってしまったので備忘録を残しておきます。

Windows10HomeにDockerをインストールするときはちょっと手間がかかります。本記事では扱いませんので、下記の記事などを参照ください。

- [windows 10 home で docker を導入するメモ](https://qiita.com/idani/items/fb7681d79eeb48c05144)
- [Windows10 HomeでDocker環境を導入する](https://qiita.com/zeffy1014/items/dda78f4ab0449989dfe1)

## 課題

LinuxPCでは下記のサイトを参考に作成したdocker-compose.ymlで起動できたのに、自宅のWindowsHomeでは動かないという事態に直面しました。（localhost:8889にてJupyetrLabが開かない。）

[Dockerを使って5分でJupyter環境を構築する](https://qiita.com/fuku_tech/items/6752b00770552bf4f46b)

```yml
version: "3"
services:
  notebook:
    image: jupyter/datascience-notebook
    container_name: dss_notebook_sample
    ports:
      - "8889:8889"
    environment:
      - JUPYTER_ENABLE_LAB=yes
    volumes:
      - ./work:/home/jovyan/work
    command: start-notebook.sh --NotebookApp.token=''
```

## 原因

原因はWindowsHomeにおけるDockerコンテナの扱いになります。WindowsHomeでは下のような階層になっています。

- Windows10Home => Virtual Box => Linux（仮想マシン） => Dockerコンテナ

そのためymlファイルでポートを指定して開けたとしてもそれは、Linux（仮想マシン）とDockerコンテナ間の話であり、WindowsHomeからは接続できません。

## 解決策

VirtualBoxのホストOSからゲストOSにアクセスできれば良いのですが、いくつか方法があります。ここでは最も単純な方法である「ポートフォワーディング」を使う手法を紹介します。
 
- 1. VirtualBoxを開き、Dockerに使用されている仮想マシンの設定画面を開く。（下図にて右クリック⇒設定）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447885/7f6b4603-88df-ac75-d49c-fcf309f1b0c5.png)

- 2. 設定の中から「高度」を開く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447885/d19e8844-9ee2-2896-0223-eaf48122f76c.png)

- 3. 「ポートフォワーディング」を選択する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447885/617956b9-4e2d-743b-3d7d-9442644bc5fd.png)

- 4. ホストOSのポートとゲストOSのポートを対応させる。（今回は8889）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/447885/d3e897b1-578f-76f3-eb91-4f45a1c1583e.png)


これで完了です。

一度、仮想マシンを再起動した後、docker-compose.ymlが保存されているディレクトリにて、下記のコマンドを実行し、バックグランドでコンテナを起動します。

```bash
docker-compose up -d --build
```

以下のコマンドでコンテナに入ります。

```bash
docker exec -it dss_notebook_sample bash
```

JupyterLabを起動します。

```bash
start-notebook.sh --NotebookApp.token=''
```

http://localhost:8889/ にアクセスするとJupyterLabが開いています。

## 参考資料

- [VirtualBox: ホストOSからゲストOSへのアクセス](http://zorinos.seesaa.net/article/450523806.html)
- [Dockerを使って5分でJupyter環境を構築する](https://qiita.com/fuku_tech/items/6752b00770552bf4f46b)
PythonでBMPファイルを読み込む


# 対象
いわゆる256bmpが対象です。
外部ライブラリは使っていません。
python3.9で動作確認しましたが、多少下のバージョンでも動くかもしれません。
対象読者は、どちらかというと、256bmpのフォーマットを知っており、他の言語で256bmpの読み書きを実装したことがある人です。
すぐ使えること、行数が少ないこと、ある程度読みやすいことを優先しています。堅牢さや多機能さの優先度は下げています。

# 経緯
256bmpのフォーマットはシンプルで、多少の加工であれば、幅、高さ、パレット、画素の4つがあれば十分です。
ならば小物ツールを作るとき、install不要の標準ライブラリだけで楽に実装できそうです。

ということで参考情報を探すべく「python BMP 読み書き」等でぐぐったのですが、ドンピシャのものが見つからなかったため作りました。

なお、用途によっては画像系の外部ライブラリを使うことも多いでしょうし、あくまで参考になれば程度です。

# サンプルコード
```python:bmpRead.py
#!/usr/bin/env python3.9
"""
BMP to bytes
用途
  BMPファイルからパレットと画像を得る
使用方法
  readBmp() を呼び出す
"""

def read256BmpFile(filename):
  with open(filename, 'rb') as f:
    # いわゆる '256bmp' 専用。それ以外での動作は未定義。

    # BMP file header
    f.read(2) #bfType         = f.read(2) # 今後エラーチェック追加時に利用したいときなど用。以降の類似したコメントアウトも同様
    f.read(4) #bfSize         = int.from_bytes(f.read(4), byteorder='little')
    f.read(2) #bfReserved1    = int.from_bytes(f.read(2), byteorder='little')
    f.read(2) #bfReserved2    = int.from_bytes(f.read(2), byteorder='little')
    f.read(4) #bfOffBits      = int.from_bytes(f.read(4), byteorder='little')

    # BMP information header
    f.read(4) #bcSize         = int.from_bytes(f.read(4), byteorder='little')
    bcWidth                   = int.from_bytes(f.read(4), byteorder='little')
    bcHeight                  = int.from_bytes(f.read(4), byteorder='little')
    f.read(2) #bcPlanes       = int.from_bytes(f.read(2), byteorder='little')
    f.read(2) #bcBitCount     = int.from_bytes(f.read(2), byteorder='little')
    f.read(4) #biCompression  = int.from_bytes(f.read(4), byteorder='little')
    f.read(4) #biSizeImage    = int.from_bytes(f.read(4), byteorder='little')
    f.read(4) #biXPixPerMeter = int.from_bytes(f.read(4), byteorder='little')
    f.read(4) #biYPixPerMeter = int.from_bytes(f.read(4), byteorder='little')
    biClrUsed                 = int.from_bytes(f.read(4), byteorder='little')
    f.read(4) #biCirImportant = int.from_bytes(f.read(4), byteorder='little')

    if not biClrUsed:
        biClrUsed = 256

    # color table
    colorTable = f.read(biClrUsed * 4)

    # pixels
    pixels = f.read()

    return (bcWidth, bcHeight, colorTable, pixels)

(width, height, colorTable, pixels) = read256BmpFile('sample.bmp')

print(width, height, colorTable, pixels)
```

# サンプルコードの使用方法
[PythonでBMPファイルを出力する](https://qiita.com/cat2151/items/6db59a0dc54d7ba1b87a)
を実行後に使用すると、生成された `sample.bmp` を読み込んで内容を`print`します。

# 参考
[PythonでBMPファイルを出力する](https://qiita.com/cat2151/items/6db59a0dc54d7ba1b87a)
Salesforce Classic での Pardot を使用した対ビジネスマーケティングの自動化 ⇒ B2B Marketing Analytics のデータセットとダッシュボード ⇒ 標準ダッシュボードの探索

１．B2B Marketing Analytics のすべてのダッシュボードに表示されるコンポーネントはどれですか?
２．Engagement ダッシュボードからは、どのようなインサイトを得ることができますか?
３．Pipeline ダッシュボードの 1 つの利点はどれですか?
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/95b478a6-9a93-60cc-2f97-517e89a6004f.png)
[Swift]UITextViewに文字を入力中に、ラベル内の文字数表示を自動更新する

##やりたいこと
UITextView内で文字を入力時に、リアルタイムでその文字数をラベル表示に反映する。（Twitterの投稿画面みたいな感じで。）



##実装の流れ

基本的な実装の考え方は以下になります

* TextViewの内容が変わったときに
* 文字数を取得し、その文字数でラベルの文字列を更新する

下記の流れで実装します。

1. ViewControllerをUITextViewDelegateプロトコルに準拠させる。
1. textViewDidChange(_ textView: UITextView) メソッドを追加する。
1. textViewDidChange内で、memoTextViewの文字数を取得して、countLabelの文字列を更新する処理を記述する。


##実装方法

前提：下記の名前でIBOutletが作成されていること。
　文字入力するTextView → memoTextView
　文字数を表示するラベル → countLabel

せっかくなのでおまけで、
* 140文字以内の場合、文字数をグレー表示
* 140文字を超えている場合、文字数を赤文字表示
としてみましょう。


```swift:RealtimeCountViewController.swift
class RealtimeCountViewController: UIViewController, UITextViewDelegate {
    
    @IBOutlet weak var memoTextView: UITextView!
    @IBOutlet weak var countLabel: UILabel!
    
    let maxCharacterCount = 140

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        memoTextView.delegate = self
    }
    
    //文字数カウント表示を更新する。
    func textViewDidChange(_ textView: UITextView) {
        countLabel.text = String(format: "%d/%d",textView.text.count, maxCharacterCount)
        
        if textView.text.count > maxCharacterCount {
            countLabel.textColor = .red
        } else {
            countLabel.textColor = .systemGray
        }
    }
}
```


##動作確認環境

下記２環境で動作を確認しました。

Xcode: 11.7
iOS: 13.7

Xcode: 12.4
iOS: 14.4

以上
【JavaScript】moment.format()するときの初歩ミス


# 経緯
「日付、yyyy-mm-dd hh : mm : ss形式でフォーマットしといてー」


って言われたので実装してみたら24時間形式にならない...:thinking:
一瞬タイムゾーンか？ってなったけど、違う。


よーーーくみてみたら、時間の部分を「HH」じゃなくて「hh」て書いてました。
めっちゃ初歩ミスです。。。


頭ボーってしてると意外と見逃しちゃうので、自分への戒めとして記事にしときます。


# 「hh」と「HH」実行したときの違い

例えば、20時半に実行したら。

```mathematica
moment().format('YYYY-MM-DD hh:mm:ss'); // 2021-02-05 08:30:00

moment().format('YYYY-MM-DD HH:mm:ss'); // 2021-02-05 20:30:00
```
Gmailの検索演算子を使って特定条件のメールを抽出（でも検索演算子は覚える必要ないよ！）

#何がしたいん？
**件名に〇〇を含むメールを一覧で表示して欲しい！（ただしアーカイブしたものは除外して受信トレイの中だけね）**

**どのラベルかとかアーカイブ済とか関係なく、星付きかつ未読のメールを一覧で表示して欲しい！**

みたいなことをやりたいのです。

んで、調べたところGmailの **検索演算子** っていうのがなかなか使えそうだと分かったわけです。

#Gmailの検索演算子ってなんぞ？

ここに入力して使うやつ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/542179c7-5584-2e8b-f54f-6bef31b3a332.png)

例えば **受信トレイの中にあって件名に『コナン』を含む未読のメール** をリストアップしたい時はこう！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/0f39c3d7-9e09-ab01-faa7-05ee16e39ed2.png)

```
in:inbox is:unread subject:コナン
```

文章を作るような感覚で書けます。
ね、かんたんでしょ？

#便利な検索演算子たち

| 検索演算子 | 絞り込む条件 |
|-----------|------------|
| in:inbox | 受信トレイの中。<br>自動振り分けをしてないメールを整理するのに便利です。 |
| is:unread | 未読メール。 |
| subject:〇〇 |  タイトルに〇〇を含む。 |
| before:2014/3/31 | 受信日が2014年3月31日 **より前** 。<br>2014年3月31日に届いたメールは絞り込み結果に **含まれません** 。 |
| after:2014/3/31 | 受信日が2014年3月31日 **以後** 。<br>2014年3月31日に届いたメールは絞り込み結果に **含まれます** 。 |
| is:starred | 星付き。 |
| newer_than:1d | 24時間以内に届いたもの。<br>d = dayだからといって午前0時を過ぎた瞬間にリストアップされる閾値が変わるわけじゃないので注意。<br>ここ二日の間に届いたメールにしたいなら ```newer_than:2d``` |
| older_than:1d | 24時間より前に届いたもの。 |

#検索演算子は基本的にAND検索

例えば件名に複数の条件をつけて ```subject:(コナン 犬)``` のように絞り込むこともできます。
この場合、件名に『コナン』を含む **かつ** 『犬』を含むメールに絞り込まれます。

Gmailの検索演算子は **かつ** でつなぐっていうのが分かりやすいかなと思います。

#ブックマークという名の検索結果ショートカット

比較演算子で検索するとURLに比較演算子が埋め込まれます。
つまり、このURLをブックマークしておけばサクっと呼び出せます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/f06e7ce2-ab5c-ed24-f3ab-5a46c1a8327d.png)
↓こうしちゃえばOK！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/7e729874-d198-485a-b893-f33c1ad408bb.png)
**検索演算子は覚える必要なし！**

#蛇足
新しいことを覚えるのはメンドクサイ！でも便利に使えるようになりたい！
あたいはいつもそんな考えで動いてます。

#参考サイトさん
https://www.ipentec.com/document/software-google-gmail-search-inbox
https://support.google.com/mail/answer/7190?hl=ja

#バージョン
Google Chrome バージョン: 88.0.4324.150（Official Build） （64 ビット）
[Python]Seleniumでタブ移動する方法

# 結論
`driver.window_handles[タブの順番]`を操作することで実現できる。

```:コード例
driver = webdriver.Chrome(chrome_options=op)
driver.get(home_url)

#新しいタブが出来るとする
driver.find_element_by_id("move-tab")
driver.click()

#新しくできたタブに移動して元のタブを閉じる動作
# driver.window_handles[タブの番号]
driver.switch_to.window(driver.window_handles[1])
driver.switch_to.window(driver.window_handles[0])
# 初めに開いていたウインドウを閉じる
driver.close()
# 新しく開かれたタブを新規にインスタンスウインドウとする これをしないとNosuchElementエラーになる
driver.switch_to.window(driver.window_handles[0])

```

#参考文献
[How to switch to new in Selennium for Pythonn

](https://stackoverflow.com/questions/10629815/how-to-switch-to-new-window-in-selenium-for-python)
AGLをビルドしてRasPiで起動した流れ

#目次
[1.環境構築](#環境構築)
[2.ビルド手順](#ビルド手順)
[3.デプロイ手順](#デプロイ手順)
[4.起動](#起動)
[5.参考サイト](#参考サイト)
# 環境構築

VertualBox上にUbuntu18.04の仮想環境作成
必要なパッケージをインストール

```
sudo apt update
sudo apt install gawk wget git-core diffstat unzip texinfo gcc-multilib \
     build-essential chrpath socat libsdl1.2-dev xterm cpio curl
```

公式の手順だと以上で足りるはずだが、python3が必要だったので入れる

```
sudo apt install python
```

gitの設定

```
git config --global user.email "XXXX@XXXX"
git config --global user.name "XXXX"
```

repoのインストール

```
mkdir -p ~/bin
export PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
```

# ビルド手順

repo init

```
repo init -b jellyfish -u https://gerrit.automotivelinux.org/gerrit/AGL/AGL-repo
```

下記のような表示が出るので「Y」を押してEnter

```
Testing colorized output (for 'repo diff', 'repo status'):
  black    red      green    yellow   blue     magenta   cyan     white 
  bold     dim      ul       reverse 
Enable color display in this user account (y/N)? Y
```

repo sync

```
repo sync
```

RasPi向けに環境変数設定し、bitbakeでビルド

```
source meta-agl/scripts/aglsetup.sh -m raspberrypi4 agl-demo agl-appfw-smack
bitbake agl-demo-platform
```

ビルド成功のログ

```
NOTE: Tasks Summary: Attempted 8253 tasks of which 1 didn't need to be rerun and all succeeded.

Summary: There were 11 WARNING messages shown.
```

# デプロイ手順

※先に公式のリリース物を焼いて練習する場合↓
[https://download.automotivelinux.org/AGL/release/jellyfish/10.0.1/raspberrypi4/deploy/images/raspberrypi4-64/](https://download.automotivelinux.org/AGL/release/jellyfish/10.0.1/raspberrypi4/deploy/images/raspberrypi4-64/)

下記にあるimageを取得

```
[repo initしたディレクトリ]/build/tmp/deploy/images/raspberrypi4-64/agl-demo-platform-raspberrypi4-64-[ビルドした日時].rootfs.wic.xz
```

etcherという便利なソフトがあるのを知ったので、今回はこれを使ってmicroSDカードを作る
[https://www.balena.io/etcher/](https://www.balena.io/etcher/)

普通にやる場合は下記を参照
https://qiita.com/shakushi1985/items/9c9a1748afbb3b3c520a

# 起動

作成したmicroSDをさして起動。
起動すると縦長の画面が出てくる。マウスを接続したら画面操作も可能だった。
![launch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/359050/ea00779e-5a83-9898-d82c-dcab976493d1.png)



## 参考サイト

公式の手順書
[https://wiki.automotivelinux.org/agl-distro/agl-raspberrypi](https://wiki.automotivelinux.org/agl-distro/agl-raspberrypi)
[https://docs.automotivelinux.org/en/master/#0_Getting_Started/1_Quickstart/Using_Ready_Made_Images/#2-raspberry-pi-4](https://docs.automotivelinux.org/en/master/#0_Getting_Started/1_Quickstart/Using_Ready_Made_Images/#2-raspberry-pi-4)

同じようなことしている人の記事
[https://qiita.com/seigot/items/2fac658812a6856548f9](https://qiita.com/seigot/items/2fac658812a6856548f9)

リリースノート
[https://wiki.automotivelinux.org/agl-distro/release-notes#jumping_jellyfish](https://wiki.automotivelinux.org/agl-distro/release-notes#jumping_jellyfish)

repoのリファレンス
[https://source.android.com/setup/develop/repo](https://source.android.com/setup/develop/repo)
PythonでBMPファイルを出力する


# 対象
いわゆる256bmpが対象です。
外部ライブラリは使っていません。
python3.9で動作確認しましたが、多少下のバージョンでも動くかもしれません。
対象読者は、どちらかというと、256bmpのフォーマットを知っており、他の言語で256bmpの読み書きを実装したことがある人です。
すぐ使えること、行数が少ないこと、ある程度読みやすいことを優先しています。堅牢さや多機能さの優先度は下げています。

# 経緯
256bmpのフォーマットはシンプルで、出力に必要な最低限の情報は、幅、高さ、パレット、画素の4つだけです。
ならば小物ツールを作るとき、install不要の標準ライブラリだけで楽に実装できそうです。

ということで参考情報を探すべく「python BMP 出力」等でぐぐったのですが、ドンピシャのものが見つからなかったため作りました。

なお、用途によっては画像系の外部ライブラリを使うことも多いでしょうし、あくまで参考になれば程度です。

# サンプルコード
```python:bmpWrite.py
#!/usr/bin/env python3.9
"""
bytearray to BMP
用途
  作成した画像データを、BMPファイルに保存する
使用方法
  colorTables と pixels を用意し、
  writeBmp() を呼び出す
"""

def writeBmp(filename, width, height, colorTables, pixels):
  with open(filename, 'wb') as f:
    lenOfColors = len(colorTables)
    numOfColors = lenOfColors >> 2
    lenOfPixels = len(pixels)
    bfOffBits = 14 + 0x28 + lenOfColors
    fileSize = bfOffBits + lenOfPixels

    #FILE_HEADER
    b = bytearray([0x42, 0x4d])               #シグネチャ 'BM'
    b.extend(fileSize.to_bytes(4, 'little'))  #ファイルサイズ
    b.extend((0).to_bytes(2, 'little'))       #予約領域
    b.extend((0).to_bytes(2, 'little'))       #予約領域
    b.extend(bfOffBits.to_bytes(4, 'little')) #ファイル先頭から画像データまでのオフセット[byte] ※誤った値だとアプリによっては表示失敗した

    #INFO_HEADER
    b.extend((0x28).to_bytes(4, 'little'))      #ヘッダーサイズ
    b.extend(width.to_bytes(4, 'little'))       #幅[dot]
    b.extend(height.to_bytes(4, 'little'))      #高さ[dot]
    b.extend((1).to_bytes(2, 'little'))         #プレーン数 常に1
    b.extend((8).to_bytes(2, 'little'))         #byte/1pixel(1byteを表すために必要なbit)
    b.extend((0).to_bytes(4, 'little'))         #圧縮形式 0 - BI_RGB（無圧縮）
    b.extend(lenOfPixels.to_bytes(4, 'little')) #画像データサイズ[byte]
    b.extend((0).to_bytes(4, 'little'))         #X方向解像度[dot/m] 0の場合もある
    b.extend((0).to_bytes(4, 'little'))         #Y方向解像度[dot/m] 0の場合もある
    b.extend(numOfColors.to_bytes(4, 'little')) #使用する色の数 ※0だとアプリによっては表示失敗した
    b.extend((0).to_bytes(4, 'little'))         #重要な色の数 0の場合もある

    #COLOR_TABLES
    b.extend(colorTables)

    #DATA
    b.extend(pixels)

    f.write(b)

# sample data
colorTables = [0x00, 0x00, 0xFF, 0x00,  0x00, 0xFF, 0x00, 0x00]  #Blue, Green, Red, Reserved, ...
pixels      = [0x00, 0x00, 0x01, 0x01,  0x00, 0x01, 0x00, 0x01]
writeBmp('sample.bmp', 4, 2, colorTables, pixels)

```


# 参考
[Pythonでビットマップデータをバイナリレベルで作成する](http://bttb.s1.valueserver.jp/wordpress/blog/2018/12/13/python_bitmap/)
[Bitmapファイルフォーマット](http://www.umekkii.jp/data/computer/file_format/bitmap.cgi)
[BMP ファイルフォーマット](https://www.setsuki.com/hsp/ext/bmp.htm)
応用数学　第三章　情報理論

##個人的メモ

####記述統計
集団の性質を要約し記述する。

####推測統計
集団から一部を取り出し元の集団（母集団）の性質を推測する。

####確率変数
- 事象と結び付けられた数値
- 事象そのものを指すと解釈する場合も多い

####確率分布
- 事象の発生する確率の分布
- 離散値であれば表に示せる

####期待値
- その分布における、確率変数の...平均の値or「ありえそうな」値

```math
E(f)=\sum_{k=1}^nP(X=x_k)f(X=x_k)
```
```math
E(f)=\int P(X=x)f(X=x)dx
```
####分散
- データの散らばり具合
- データの各々の値が、期待値からどれだけズレているのか平均したもの

```math
分散Var(f)=
E \bigl(
( f_{(X=x)} - E_{(f)} )^2
\bigr)
=E(f_{(X=x)}^2)-(E_{(f)})^2
```
###共分散
- 2つのデータ系列の傾向の違い
- 正の値を取れば似た傾向
- 負の値を取れば逆の傾向
- ゼロを取れば関係性に乏しい

```math
共分散Cov(f , g)=
E \bigl(
( f_{(X=x)} - E_{(f)} )
\bigr)
\bigl(
( g_{(Y=y)} - E_{(g)} )
\bigr)
=E(fg)-E(f)E(g)
```
###分散と標準偏差
- 分散は２乗してしまっているので元のデータと単位が違う
→２乗することの逆演算（つまり平方根を求める)をすれば元の単位に戻る

```math
\sigma=
\sqrt {Var(f)}
=\sqrt{
E \bigl(
( f_{(X=x)} - E_{(f)} )^2
\bigr)}
```

###ベルヌーイ分布
- コイントスのイメージ
- 裏と表で出る割合が等しくなくても扱える

```math
P(x|\mu)=\mu^x(1-\mu)^{1-x}
```
###マルチヌーイ（カテゴリカル）分布
- さいころを転がすイメージ
- 各面の出る割合が等しくなくとも扱える

###二項分布
- ベルヌーイ分布の多試行版

```math
P(x|\lambda,n) =\\
\frac{n!}{x!(n-x)!}\lambda^x(1-\lambda)^{n-x}

```
###ガウス分布
- 釣鐘型の連続分布

```math

N(x;\mu,\sigma^2) =
\sqrt \frac{1}{2π\sigma^2}
\exp\Bigl(-\frac{1}{2\sigma^2}(x-\mu)^2\Bigr)

```
##推定
母集団を特徴づける母数(パラメーター：平均など）を統計的に推測すること。

###推測統計
集団から一部を取り出し元の集団（母集団）の性質を推測する

点推定：平均値などを１つの値に推定すること。
区間推定：平均値などが存在する範囲（区間）を推定すること。

###推定量（estimator)
パラメータを推定するために利用する数値の計算方法や計算式のこと。推定関数とも。
###推定値(estimate)
実際に試行を行った結果から計算した値。

真の値をθとすると・・・

```math
\hat{\theta}

```
のように表す。

###標本平均
母集団から取り出した標本の平均値

- サンプル数が大きくなれば、母集団の値に近づく→一致性
- サンプル数がいくらであっても、その期待値は母集団の値と同様→不遍性

###標本分散

```math
\hat{\sigma}^2 = \frac{1}{n}\sum_{i=1}^n(x_i-\overline x)^2

```
一致性は満たすが不遍性は満たさない


###不偏分散

```math
s^2 =\frac{n}{n-1}× \frac{1}{n}\sum_{i=1}^n(x_i-\overline x)^2　\\
=\frac{1}{n-1}\sum_{i=1}^n(x_i-\overline x)^2

```

##情報科学

- 自己情報量

対数の底が２のとき、単位はビット(bit)
対数の底がネイピアのeのとき、単位は(nat)

```math
I(x) =-\log(P(x))=\log(W(x))
```

- シャノンエントロピ

微分エントロピーともいうが、微分しているわけではない
自己情報量の期待値

```math
H(x)=E\bigl((I(x)\bigr)
=-E\Bigl(\log\bigl(P(x)\bigr)\Bigr)
=-\sum\Bigl(P(x)\log\bigl(P(x)\bigr)\Bigr)
```


- カルバック・ライブラー　ダイバージェンス

同じ事象・確率変数における異なる確率分布P,Qの違いを表す

```math
D_{KL}(P||Q)=\mathbb E_{X～P}\Bigl[\log\frac{P(x)}{Q(x)}\Bigr]=\mathbb E_{X～P}\bigl[\log P(x)-\log Q(x)\bigr]
```

- 交差エントロピー
KLダイバージェンスの一部を取り出したもの
Qについての自己情報量をPの分布で平均している

```math
D_{KL}(P||Q)=\sum_x P(x)\bigl(\log(Q(x))-(\log(P(x))\bigr)

\\
H(P,Q)=H(P)+D_{KL}(P||Q)
\\
H(P,Q)=-\mathbb E_{X～P}\log Q(x)=-\sum_x P(x)\log Q(x)

```
[ruby] returnとは

# returnとは
定義したメソッドの中の戻り値を返す情報をあらわします

メソッドの中でreturnを書いた時点で、そのメソッドから抜け出してしまうという特徴があります

#戻り値を返すには
戻り値を返したい情報の前にreturnと書くことです

コードでかいてみます

```

def hello
  return "こんにちは"
end

puts hello

#実行結果
こんにちは
```

```
def hello
  return "こんにちは"

  "おはよう"
  
end

puts hello

#実行結果
こんにちは
```

この場合returnの時点でメソッドから抜け出してしまうのでおはようは実行されません

```
def hello
  "こんにちは"

  "おはよう"

end

puts hello

#実行結果
おはよう
```

この場合、returnがないので一番最後の式が評価されることとなるので

おはようが戻り値となります

#まとめ
return は省略可
returnがある場合はそこでの戻り値でメソッドは抜けます
returnがない場合は一番最後の戻り値が返される
いいじゃん！AWS パートナートレーニングと認定

#はじめに
某ベンダで、クラウドの人材育成企画と研修トレーニングのデリバリを担当しています。ここ最近、会社に限らず、Zoom飲み会やってる学生時代の友達にも「クラウドの講師やってんだったら、クラウドを学ぶいい教材知らない？」って聞かれることが増えてきました。折角なので、AWSが公開しているパートナートレーニングと認定があるので紹介します。

#AWS Partner Networkとは？
パートナトレーニングを受講するためにはAWS Partner Network（APN）アカウントが必要になります。APNに関する詳細情報は、AWSサイトに情報が出ています。

・[**AWS パートナーネットワーク**] 
(https://aws.amazon.com/jp/partners/)

>AWS パートナーネットワーク (APN) とは、アマゾン ウェブ サービスを活用して顧客向けのソリューションとサービスを構築しているパートナーの、グローバルなコミュニティです。 AWS は、ビジネス、技術、マーケティングの有益なサポートを行うことで、パートナーが自身の AWS サービスを構築、マーケティング、販売できるよう支援します。

日本でも、数百社がAPNに加盟しているようです。自分の会社がAPNに加盟しているか、以下のサイトから探索+確認できます。

・[**AWS パートナーを見つける**]
(https://partners.amazonaws.com/jp/)

#APNアカウントの作成
もし所属する会社がAPNに加盟していたら、APNアカウントを作りましょう。APNアカウントは以下のサイトから作成します。

・[**APN パートナーセントラル**]
(https://aws.amazon.com/jp/partners/apn-partner-central/)

***"注意"***
必ず、会社のメールアドレスでAPNアカウントを作成しましょう。アカウント作成時にドメイン情報を判別して、自動で登録済みのAPNと作成したアカウントをAPNアカウントとして紐付けてくれます。

#AWS パートナートレーニングと認定
さて、APNアカウントが作成できたら、ここからトレーニングの本番です。クラウドを学ぶ教材として、次の３つのデジタルトレーニングを学習することをお勧めします。

・[***AWS パートナートレーニングと認定***]
(https://aws.amazon.com/jp/partners/training/?nc2=sb_pt_pto)
> ・AWS ビジネスプロフェッショナル認定
・AWS テクニカルプロフェッショナル認定
・AWS クラウドエコノミクス認定

この３つのコースを受講するとAWSの技術、企業文化、クラウドが実現する経済価値を網羅的に学習することができます。各トレーニングの最後に確認テストがあり、合格すると合格認定証がゲットできます。確認テストは何度でも受験可能、８０点以上の理解度で合格のようです。

#おわりに
AWS認定を受験する際にも、基礎学習にAWS パートナートレーニングは最適です。AWS 認定 クラウドプラクティショナーにトライする際の基礎教育としてお勧めします
Docker/Laravelのpost時に419エラーがでた話

### 問題

###前提

・Docker/Laravel環境で記事post時419エラーが出ました。
・ローカルのビルドインサーバ/Laravel環境では出ていませんでした。

### 調査

１）CSRFがformに入っているか
419エラー出るときはCSRF関連のエラーと言うことで
下記の記事にある通り、formタグに```@csrf```が入っていないか調べました。

https://qiita.com/takuma-jpn/items/f4e23bed778b17b52e36

しかし、変わらず出てくるという状況でした。
そもそも、ローカル環境ではできているので、ここは問題無しとしました。

2) sessionのファイル保存がされていないか
問題はここにありました。
Laravelの```.env```ファイルに```SESSION_DRIVER=file```という項目があります。
```SESSION_DRIVER=file```にしていると```storage```ディレクトリに
sessionファイルが保存されるようになっています。
このままだと、Dockerの環境でCSRFのtokensession
sessionとCookieに関しては下記のサイトを参考にさせていだきました。

https://reffect.co.jp/laravel/laravel-sessions-understand#CookieXSRF_TOKEN

Docker内に保存することできないので、```.env ```を下記のように変更しました。

``` php:.env
    - SESSION_DRIVER=file 
　 　+  SESSION_DRIVER=database  #sessionsファイルをdatabaseに保存するようにする。
 ```

そして、データベースにsessionsテーブルを作成してデータベースににcsrｆのtokernを（payload)を保存するようにしました。

````php:crate_sessions_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateSessionsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('sessions', function (Blueprint $table) {
            $table->string('id')->primary();
            $table->foreignId('user_id')->nullable()->index();
            $table->string('ip_address', 45)->nullable();
            $table->text('user_agent')->nullable();
            $table->text('payload');
            $table->integer('last_activity')->index();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('sessions');
    }
}

```
そして、マイグレーションします。
```
php migrate
```

これでDocker/Laravel環境で419エラーでなくなりました。

###考察
Dockerでは、一時保存するファイルに関する処理があるときは、
Docker内での書き込み設定が必要になってくるので、
そこについて調べて対応することが求められてくると思います。

##追記(2021/2/11)
　別件のECSにデプロイする際に、php-fpmでユーザー権限が問題になることがありました。
  php-pmでのユーザー権限とphp-fpmのユーザー権限の問題もあると思います。
　
　---- ECSでデプロイしてもエラーになる  ----
*** Nginx, php-fpmで500のエラーが出ていた***
EC2からコンテナの中にあるログファイルを確認したところ、 /var/www/public への権限エラーが出ていた
権限エラーを追っていくと、 /var/www/public は root がオーナーになっているが、phpを実行しているユーザのIDは 33 だった
EC2側で ps aux | grep php などで確認
/etc/passwd を見たところ、33のユーザIDは www-data ユーザだった
DockerfileのCOPYに --chown=www-data:www-data をつけたら動作しました。

----
このような問題と同様に対処しても行けるかもしれませんが要検討です。
以上、何かのヒントになれば。
ドットインストール　詳解HTML 基礎文法編　メモ

プログラミング用フォントの設定をしておくと、半角と全角空白の見分けがつく

下記のように文書の説明を書くとよい
＜meta name="description" content="プログラミング学習サービスのドットインストールです">

alt属性は画像が何らかの理由で表示できなかったときに表示される代替テキストで、目が不自由な人が使っている、音声読み上げブラウザで読み上げられるテキストでもあるので、丁寧に設定する。


＜strong＞は強調タグ
＜blockquote＞は引用タグで、字下げされる　（出典URLが分かる場合は cite 属性で表現することもできる）
ここで少し話題が変わったよ、ということを示したい場合は horizontal rule の略である、 ＜hr＞タグを使ってあげることもできます。→水平線が表示される
＜br＞は改行
↑これらのタグは文章の意味を考えてつけるもので、装飾のために使ってはいけない

文字実体参照
＆ｌｔ；で＜　　＆ｇｔ；で＞

command + D　で複数選択

改行や空白をそのまま表現するには pre タグを使ってあげます。
preformatted の略で、すでに整形されたテキストをそのまま表示してね、というタグ

コードを表すcodeタグでも囲む

 &lt;dl&gt; はdescription listで、項目に説明も書くときのリストタグ
&lt;dt&gt;　はdescription termで項目
&lt;dd&gt; はdescription detailで説明


表はtableタグ
見出しはthead (table head)
本体はtbody (table body)

行はtrタグ　(table row)
見出しとなるセルにはthタグ　(table header cell)
見出し以外のセルにはtdタグ　(table data cell)

※見出しがない表はtheadを省くことができる

aタグにtarget属性で_blankとすれば、別タブでリンクを開く

ページ内リンクのhref 属性ですが、同じページ内にリンクを貼るには # （パウンド記号）を付けてあげて、そのあとに好きな識別子(今回はabout)をつけてあげます
そのうえで、飛ばしたい見出しのほうに、その識別子を id 属性でつける

ページ内リンクに飛んだ時の URL には、先ほどの識別子がつく点にも注意！


header , footerタグは必ずしもページの最初と最後だけに配置されるというわけではなくて、こうした意味合いのコンテンツだったら、ページ途中でも使われるという点に注意しておきましょう。

navタグはメニューのリンクなどによく使われる

aside は本文とは関連性の薄いコンテンツに使われる
サイドバーに配置される広告などに使う

article はメディアの記事などの独立して配信されることを想定したコンテンツに使われます。
article タグは使いどころに悩んだりしますが、投稿日や著者情報があって、外部に配信することを想定しているような独立したコンテンツに使ってあげる

main はページ内の主要なコンテンツを表すもので、ページ内で 1 つしか使えないというルールがある.

sectionは汎用的に使える区切り

特に文書が長くなってくると、この section がどのレベルの見出しだったかな、と覚えていくのが面倒なので、 section ごとに見出しをh1に振り直してあげても良いでしょう。

日付や時刻、期間を表すコンテンツには time タグを使うことができる

見出しタグは section や aside と同様、 article タグ中でも相対的な順序であれば良いので、この中で 1 番重要な見出しという意味で h1 に振り直してあげても OK 

HTML を書いていると、このタグの中にこのタグを書いて良いのかな、と迷うときがありますが、そうしたときは必ずMDNを確認

HTML要素リファレンスを見ればタグ一覧が見れる