OmniAICreator/Qiita-1.07M huggingface dataset:
[Blender]テクスチャペイントをお手軽に体験する

#概要
Blenderの３Dオブジェクトに外部アプリで作ったテクスチャを貼り付ける手順です。
とりあえず体験してみたい方向けなので操作を必要最低限なものにしてあります。
#本文
##テクスチャペイント用の画像ファイルを書き出す
デフォルトで表示されているCubeにテクスチャペイントしていきます。

![スクリーンショット 2022-01-23 12.27.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/7467392b-86b6-35e5-65f5-2dbb13e15223.png)
①UV Editingタブを選ぶ
（画面左半分の展開図が表示されない時はマウスカーソルを画面右側に持っていって全選択（Aキーを押す）をすると表示されます）
②UVを選択して
③メニューからExport UV Layoutを選択します。
![スクリーンショット 2022-01-23 12.37.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/52c59329-e2af-6f6c-8f81-668b01dc1f4d.png)

FillOpacityを０にしてExport UV Layoutを押します。
##テクスチャペイント
書き出した画像ファイルはお絵描きアプリで自由に書き込めます。
![Cube 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/21e35d5f-b313-59f2-6c38-6ba8a04db47f.png)
※ 最後に保存する時「UV展開の区切り線」はお絵描きアプリのレイヤー機能で表示OFFにしておきます。ONのままでも問題なく出来ますが３Dオブジェクトの区切り位置に線が表示されてしまいます。
##テクスチャをBlenderに取り込む
![スクリーンショット 2022-01-23 13.01.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/c267d7f2-4b45-774a-7984-175123ed61e1.png)
①Layoutタブを選び、次にCubuを選択状態にする
②MaterialPropertiesを選ぶ
③BaseColorのボタンを押す→表示されるメニューから「ImageTexture」を選びます
![スクリーンショット 2022-01-23 13.07.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/6b8617f2-f153-8412-554b-9f68207b3e65.png)
Openを押して、先ほどお絵描きアプリで作った画像を選びます。
![スクリーンショット 2022-01-23 13.10.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/0b9b7f83-cb20-e496-c821-76a7886d19b6.png)
Zキーを押すとメニューが表示されるので、Renderedを選ぶと
![jifvs-cp52q.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/a360a7a3-e184-7676-06aa-376a7ce1570f.gif)
Cubeにオリジナルの模様が描かれます
Tableauで地震発生状況を可視化する

[気象庁震度データベース](https://www.data.jma.go.jp/svd/eqdb/data/shindo/index.html)から、地震の発生状況を可視化する方法を紹介します。

完成版は[Tableau public](https://public.tableau.com/shared/KDY674D89?:display_count=n&:origin=viz_share_link)参照。
![震度４以上の地震.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2114658/937e61bb-246f-217f-4e92-1f1eff68ffa7.png)

## データのダウンロード
 [気象庁震度データベース](https://www.data.jma.go.jp/svd/eqdb/data/shindo/index.html)で、右下の検索条件を設定してから「地震を検索」ボタンを押すと画面に地震の発生状況が表示されます。データをダウンロードするには、右上の三本線のアイコンを押して表示される地震一覧の最下部の「CSVダウンロード」ボタンを押します。ただし、1000件までしかダウンロードできないので、注意。
![Screenshot 2022-01-23 14.23.02 - Display 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2114658/8caee2d6-a9db-0ea2-4861-878f249f0918.png)

## 緯度経度を１０進に変換
ダウンロードしたデータには震源地の緯度経度が含まれているので、これを使って地図に落としていきます。普通であればTableauで簡単に地図化できるのですが、緯度経度表記が度分秒なのですぐにはできません。これを計算フィールドを使って、１０進表記に変換します。

度分秒表記（34°12.5′N）の分の部分（12.5′）は６０進数です。なので、度の部分（34°）に分を６０で割ったものを足せば10進表記に変換できます。

```:緯度（10進表記）
int(split([緯度],"°",1))+FLOAT(replace(split([緯度],"°",2),"′N",""))/60
```

```:経度（10進表記）
int(split([経度],"°",1))+FLOAT(replace(split([経度],"°",2),"′E",""))/60
```

splitを使って、「°」の前後の文字列を取り出しています。文字列のままでは数値計算に使えないので、intやfloat関数で数値に変換しています。

## ダッシュボードの作成
ダッシュボードには、震央の位置、深さと発生日、一覧の３つのビューを用意しました。円のサイズを震度、色をマグニチュードとしています。一覧の網掛けも含め、色はマグニチュードの大きさで統一しています。表示年のフィルターやアクションフィルターを設定して完成です。
[React Native] Flatlistのitemごとにstyleを設定する

# Flatlistのitemごとにstyle適応

## ● 概要

ReactNative でFlatlistを作成する際に item ごとに style 設定したかったのでその内容を自分なりにまとめてみました。

## ● 実行環境

- Expo Snack



## ● コード

```react
import React from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar } from 'react-native';

const DATA = [
  {
    id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
    title: 'First Item',
  },
  {
    id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
    title: 'Second Item',
  },
  {
    id: '58694a0f-3da1-471f-bd96-145571e29d72',
    title: 'Third Item',
  },
];

let itemStyle1 = {color: 'red',backgroundColor: '#96daff'};
let itemStyle2 = {color: 'black',backgroundColor: 'pink'};
let itemStyle;

//DATAのtitleごとにstyleを設定
function divideStyle (word) {
  if(word.title =='First Item') {
      itemStyle = itemStyle1
    }
  else{
      itemStyle = itemStyle2
  }
  return itemStyle
}

const Item = ({ title }) => (
  <View style={styles.item}>
    <Text style={divideStyle({title})}>{title}</Text>
  </View>
);

const App = () => {
  const renderItem = ({ item }) => (
    <Item title={item.title} />
  );

  return (
    <SafeAreaView style={styles.container}>
      <FlatList
        data={DATA}
        renderItem={renderItem}
        keyExtractor={item => item.id}
      />
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    marginTop: StatusBar.currentHeight || 0,
  },
  item: {
    backgroundColor: '#acafb3',
    padding: 10,
    marginVertical: 8,
    marginHorizontal: 16,
  },
});

export default App;
```



## ● ポイント

関数 divideStyle() でDATAのtitleごとにstyleを設定。

今回は DATA の title が

・First Item'だった場合:  Flatlist 内のitemに itemStyle1 = {color: 'red',backgroundColor: '#96daff'};

・それ以外の場合:  Flatlist 内の item に itemStyle2 = {color: 'black',backgroundColor: 'pink'};

を適応させています。



##● 実行結果

![2022-01-23 13_04_38-.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1247619/64034796-ff66-be47-989c-14864caa7707.png)
マテハン在庫報告をFormsとPower Automate Desktopでスマホ報告できるように作成

#報告は紙かパソコン処理があたりまえ？

店舗が毎月の報告するものは結構多くあります。
後方業務で１か月に報告するものはいくつあるのでしょうか？

それらを簡素化できるものがあるのでは？と思いついた一つが
”店舗がマテハン什器を数えて報告する”ことでした。

現在の状況は下記の通り
![棚卸の流れ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2495084/caf4a438-6283-59b0-0301-07b878091039.jpeg)
この2.3.5をFormsとPADを使って簡素化することにしてみました。
Forms機能で報告内容を作り、スマホを使って数量報告。
PADでエクセルデータ取得、保存、メール送信機能を作成し、作業を簡素化してみました。

#使用したもの

[Microsoft Forms](https://support.microsoft.com/ja-jp/office/microsoft-forms-%E3%81%AB%E3%82%B5%E3%82%A4%E3%83%B3%E3%82%A4%E3%83%B3%E3%81%99%E3%82%8B-620daa7a-3e03-4013-8f92-5cce86210ef6)
 [Power Automate Desktop](https://powerautomate.microsoft.com/ja-jp/desktop/)
Microsoft Excel

#全体の完成イメージ
・店舗
スマホでQRコードを読み取って画面を表示し報告。

![スマホ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2495084/e49f30c7-a135-d57b-4326-be19abaf11e1.jpeg)
・本社スタッフ
Formsで集計されたExcelファイルをセンターにメール送信（自動）

#Microsoft Formsで作成
##作成のポイント
・エリア、店舗は選択制にして入力しなくてよいようにします。
・エリア入力後は指定エリアの入力された店舗選択画面が表示されるように分岐設定をします。
・実際数量カウントしたいマテハン什器は写真付きで表示できるようにします。
　また入力は数字のみ入力する制限をかけます。（集計時に楽になるため）
・QRコード作成ボタンを押し、ダウンロードすることで画像を取得できます。
　この作成できたQRコードを店舗へ連絡し読み取ることでスマホでの報告が可能になります。

#Power Automate Desktopで作成
##PADを利用した流れ
Fomsからエクセルデータを取得・保存し、名前を付けて保存。Outlookメールで送信します。
![フロー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2495084/4e41ce43-dd24-e0f5-60e7-4eee8ba509c1.jpeg)

##フロー①
chromeを立ち上げFormsを表示。Excelデータをクリックしダウンロードまで。
![F1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2495084/1a5dec47-53c8-ad21-8023-d05a89ad9a0a.jpeg)

##フロー②
保存したファイルを確認し、名前を付けて保存、エクセルを閉じるまで。
![F2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2495084/12dca43c-e0aa-5f22-8bcf-1ffbff9305a8.jpeg)

##フロー確認③
メール内容を保存したExcelを起動し、Outlookのメールへ読み取り（コピー）ファイル添付し送信するまで。

![F3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2495084/81a48316-c8f5-fa3a-d648-201c8a41f424.jpeg)

PADの最後にアクション「Outlookを閉じます」はあえて入れませんでした。
これは自分の目で送信されたかを確認したかった為でもあります。

PADでエクセルファイルを任意の名前を付けて保存（自動で）したかったのですが、
わからず一旦このままになりました。

#完成後、部内に共有
作成した内容を部内で説明し「利用できないでしょうか？」と相談してみました。
「今時この報告方法もないよね」とのことで、早速センターに問い合わせをしていただきました。
数店舗でまずはトライアルをしてみたいと。使えるようであれば各社グループ展開はいかがですか？とまで伝えてくれました。（今は返答待ちです）
マテハン報告は3ケ月に1度ですが、次の報告に使えないかと考えています。
とにかくこれが簡素化の第一歩になれるようにと部内複数名を巻き込んでの改良を決意しました。
Jetson Nano 2GBを手に入れてJetPack4.6でOpenCV4.4.0 compiled CUDA: Yesにしようとしたら困った

　Jetson Nanoでナウでヤングになろうとおじさんの試行錯誤記録です。CUDAなJetsonでYOLOしく!

#せっかちなものぐささん向け
　この記事は

```
E: パッケージ 'libatlas-base-dev' にはインストール候補がありません
E: パッケージ 'libdc1394-22-dev' にはインストール候補がありません
E: パッケージ 'libglew-dev' にはインストール候補がありません
E: パッケージ 'libopenblas-dev' にはインストール候補がありません
E: パッケージ libtesseract-dev が見つかりません
E: パッケージ libxine2-dev が見つかりません
E: パッケージ libxvidcore-dev が見つかりません
E: パッケージ libx264-dev が見つかりません
E: パッケージ qv4l2 が見つかりません
E: パッケージ 'v4l-utils' にはインストール候補がありません
E: パッケージ v4l2ucp が見つかりません
```
で困っている時に

```
$ sudo add-apt-repository universe
```
するといいことあるかもYo!という記事です。

#前準備
 SDカードに[JetPack4.6のイメージ](https://developer.nvidia.com/jetson-nano-2gb-sd-card-image)を焼きます。手持ちにMacがあるので、32GB SDカードをUSBのReaderに差し込み、

```
$ diskutil list
```
でDiskを見つけ、

```
$ sudo diskutil unmountDisk /dev/diskほにゃらら
$ sudo dd bs=10m if=sd-blob.img of=/dev/diskほにゃらら
```
を順次実行してSDカードの準備をします。
JetsonにSDカードを差し込んで電源ON!

#前準備完了…ん?
初期設定はそれっぽくデフォルト値を選びながら進め、WiFiドングルを接続しつつおもむろにapt updateをすると…

```
$ sudo apt update
(略)
ports.ubuntu.comを解決できませんでした。
```
…
いやいや、
http://ports.ubuntu.com/dists/bionic/
とか普通にいるじゃん、といつものLinux系似て非なるトラブルシュートをぐねぐね５時間ぐらいかける。ぐぬぬ…とふとJetson上のChromiumでWebサーフしようとするとネットワークがつながっていない旨が表示される。どうも、WiFi設定を覚えてくれておらず、途中のreboot以降、ネットワーク断状態でアレコレ試行錯誤していた模様。正座し直して有線LANに切り替え、試行錯誤過程でいじった/etc/apt/sources.listを元に戻して無事apt updateを通す。

#さて、CUDAを… え？こんなにめんどいの？
　その他、pipあたりをこちょこちょinstallしつつ、CUDAを有効にしようかな、と調べていると随分と手順が煩雑。サボる方法を模索して辿り着いたのが[ここ](https://github.com/mdegans/nano_build_opencv)。ありがとう！mdegansさん！

```
$ wget https://raw.githubusercontent.com/mdegans/nano_build_opencv/master/build_opencv.sh
$ sudo chmod 755 ./build_opencv.sh
$ ./build_opencv.sh
(略)
E: パッケージ 'libatlas-base-dev' にはインストール候補がありません
E: パッケージ 'libdc1394-22-dev' にはインストール候補がありません
E: パッケージ 'libglew-dev' にはインストール候補がありません
E: パッケージ 'libopenblas-dev' にはインストール候補がありません
E: パッケージ libtesseract-dev が見つかりません
E: パッケージ libxine2-dev が見つかりません
E: パッケージ libxvidcore-dev が見つかりません
E: パッケージ libx264-dev が見つかりません
E: パッケージ qv4l2 が見つかりません
E: パッケージ 'v4l-utils' にはインストール候補がありません
E: パッケージ v4l2ucp が見つかりません
```
えー、またぁー？とこれまたグネグネして辿り着いたのが[ここ](https://www.uebu-kaihatsu.jp.net/ja/apt/kubuntu-1804%EF%BC%88bionic%EF%BC%89%E3%81%ABatlas%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/998331335/)。

```
$ sudo add-apt-repository universe
$ ./build_opencv.sh
(略)
```
大体１時間半ぐらい経って

```
(略)
[ 41%] Building CXX object modules/ml/CMakeFiles/opencv_ml.dir/src/ann_mlp.cpp.o
[ 41%] Building CXX object modules/ml/CMakeFiles/opencv_ml.dir/src/boost.cpp.o
[ 42%] Building CXX object modules/ml/CMakeFiles/opencv_ml.dir/src/data.cpp.o
```
な感じなのでまだまだ続くなー。
ちょっと[足柄茶業センター](https://www.yamakita.net/shopping/detail.php?id=9)までほうじ茶買いに行ってくる。[これ](https://www.yamakita.net/shopping/detail.php?id=9)が美味しいので病みつきなのよ。
Docker で GPG 的な意味での他人を再現する

## この記事に書いてあること
ファイルの暗号化を行う [記事](https://qiita.com/kino-ma/items/fb41378d2db03f022ebd) で，テスト用に「他人」＝「正当な秘密鍵を持っていない人」を再現したので，一応その手順を残しておく．

## やったこと
ある秘密鍵を持たない人を再現するにあたって，自分の PC だとちょっとめんどくさそうだった．なぜなら， GPG が気を利かせて適切な鍵を使ってしまうかもしれないからだ．また，「秘密鍵を持っているがキャンセルした」といった出力ではなく，「秘密鍵を持っていない」ことを示す出力を見たかったから．

したがって自分の PC 以外の環境が必要だが，他人の PC を使うわけにもいかないし，テスト用でもサーバに秘密情報を置いたりしたくなかった．

そのため， Docker を使うことにした．公式ではないが GPG の [Docker イメージ](https://hub.docker.com/r/vladgh/gpg) が公開されていたので，それを使わせていただく．

### 1. `docker run`
```
$ docker run -it --rm --entrypoint sh vladgh/gpg
Unable to find image 'vladgh/gpg:latest' locally
latest: Pulling from vladgh/gpg
a0d0a0d46f8b: Pull complete
60b2aa58f8f6: Pull complete
Digest: sha256:95c5d7fa3782183eeb03b4427cd8f4a237e3660bc4b155cd5bee3d4ef83f9d54
Status: Downloaded newer image for vladgh/gpg:latest
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
/ #
```

デフォルトだと気を利かせて `gpg` が起動するので，エントリーポイントとしてシェルを指定する． bash はないらしい．

### 2. 鍵を生成する
```
/ # gpg --quick-gen-key hoge
gpg: directory '/root/.gnupg' created
gpg: keybox '/root/.gnupg/pubring.kbx' created
About to create a key for:
    "hoge"

Continue? (Y/n) y
```

パスフレーズを聞かれたりするので，適当に設定する．

```
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: /root/.gnupg/trustdb.gpg: trustdb created
gpg: key E9FE1B76DBF8B1D3 marked as ultimately trusted
gpg: directory '/root/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/root/.gnupg/openpgp-revocs.d/1DCFC4D1554F769937DEBA00E9FE1B76DBF8B1D3.rev'
public and secret key created and signed.

pub   rsa3072 2022-01-23 [SC] [expires: 2024-01-23]
      1DCFC4D1554F769937DEBA00E9FE1B76DBF8B1D3
uid                      hoge
sub   rsa3072 2022-01-23 [E]

/ #
```

### 3. やりたいことをやる
ある秘密鍵を持たない誰かとして再現したい好きな動きをやる．

今回は暗号化されたファイルの復号に失敗する人を再現したかったので，以下のようにした． `#` が Docker コンテナ内で， `$` はホストマシンである．

```
$ docker ps
CONTAINER ID   IMAGE        COMMAND   CREATED         STATUS         PORTS     NAMES
8112f224d570   vladgh/gpg   "sh"      5 minutes ago   Up 5 minutes             tender_rosalind
$ docker cp ./test.tgz.gpg tender_rosalind:/

/ # ls
bin           etc           lib           mnt           proc          run           srv           test.tgz.gpg  usr
dev           home          media         opt           root          sbin          sys           tmp           var
/ # gpg --decrypt test.tgz.gpg
gpg: encrypted with ECDH key, ID 02ABDEDC13AB19F4
gpg: decryption failed: No secret key
```

最後の三行（復号に失敗した時の出力）が取れたので，目的が達成できた．

## まとめ
Docker を使って，ある秘密鍵を持っていない人 = 第三者 の環境を再現した．
VSCode Python importエラー 上の階層の自作モジュールをインポートしたい

# 環境
windows 10
pyenv-win
python 3.9.6

# ue_no_direcotryはどこ？ (Pythonの気持ち)

importは`sys.path`上に存在するパスから`相対パス`で目的のモジュールを探索するため、`sys.pathに存在しないパス（パスが通っていない）`自作モジュールなどではで`上の階層(ue_no_direcotry:この記事のヒロインの居場所)`を探索することができない。
標準モジュールやサードパーティーモジュールがどこからでもインポートできるというのは、それらのモジュールが`sys.pathの下の階層`にインストールされているからである。

そうなんです、`sys.pathの下の階層`にあるモジュールならどこからでもインポートできるということなのです！当たり前のようなことですが、重要なことなので２回繰り返しました。ここでつまずいている人はこのことが理解できていないのだと思います。

```python:ルートのパスで実行すると
import sys
print(sys.path)
```

```:sys.pathの実行結果
['c:\\Users\\yourname\\lessons\\python_lessons\\ue_no_directory\\current_root',
 'c:\\Users\\yourname\\.vscode\\extensions\\ms-toolsai.jupyter-2021.11.1001550889\\pythonFiles',
 'c:\\Users\\yourname\\.vscode\\extensions\\ms-toolsai.jupyter-2021.11.1001550889\\pythonFiles\\lib\\python',
 'C:\\Users\\yourname\\.pyenv\\pyenv-win\\versions\\3.9.6\\python39.zip',
 'C:\\Users\\yourname\\.pyenv\\pyenv-win\\versions\\3.9.6\\DLLs',
 'C:\\Users\\yourname\\.pyenv\\pyenv-win\\versions\\3.9.6\\lib',
 'C:\\Users\\yourname\\.pyenv\\pyenv-win\\versions\\3.9.6',
 '',
 'C:\\Users\\yourname\\.pyenv\\pyenv-win\\versions\\3.9.6\\lib\\site-packages',
 'C:\\Users\\yourname\\.pyenv\\pyenv-win\\versions\\3.9.6\\lib\\site-packages\\win32',
 'C:\\Users\\yourname\\.pyenv\\pyenv-win\\versions\\3.9.6\\lib\\site-packages\\win32\\lib',
 'C:\\Users\\yourname\\.pyenv\\pyenv-win\\versions\\3.9.6\\lib\\site-packages\\Pythonwin']
```

現在のルートのツリーは以下の通りで、`sita_no_moudle.py`から`ue_no_module.py`をインポートしとうとする。繰り返しになるが、パスが通っていないため相対パスで探索するimportではインポートすることができない。

```:current-root
ue_no_directory
├── ue_no_module.py
├── current_root 
│   ├── sita_no_module.py ← いまここにいるよ！
```


```python:current_root/sita_no_module.py
# 現在のsys.path
current_sys_path = sys.path[0]
print(current_sys_path)
# 上のディレクトリpath
sp = os.sep  # パスの区切り文字を取得 (windowsなら\\, linxuなら/)
ue_no_dir_path = sp.join(current_sys_path.split(sp)[:-1])
print(ue_no_dir_path)
# sys.pathに上のディレクトリpathを追加
sys.path.append(ue_no_dir_path)
# パス追加後のsys.path
print(sys.path)

import ue_no_module
```
```python:短い書き方
sys.path.append(os.sep.join(sys.path[0].split(os.sep)[:-1]))
import ue_no_module
```

# おまけ

パスが汚れてしまうけど、このやり方もありです
`入力し終わったらログアウトしてね！`

```:windows
win+r sysdm.cplと入力 > advanced > new user variable > パス名: PYTHONPATH  追加したいパス
```

```:linux
export PYTHONPATH="<追加したいパス>:$PYTHONPATH"
```
【Rails】bulletで検知できないN+1を解消する part1

# 初めに

Rails の view の表示速度の低下理由として、partial の使いすぎや N+1 問題があります。
この N+1 問題を検知する gem として、有名なのが bullet です
ただし、全ての N+1 を検知してくるわけではありせん

### 本記事について

bullet で検知できない N+1 が発生した時の対処法を僕なりに書いた記事になっております。
間違ったことなどがあれば、ご指摘ください。

# 本文

### モデルの定義

まずは、モデルを定義します。
コーヒー豆(bean)とレビュー(review)が 1 対多で関連付けされています。
bean にはコーヒー豆の名前と国、説明のカラムが存在しています。
review にはレビュー内容と香り・甘味・苦味・コク・酸味のカラムが設定されています
香り・甘味・苦味・コク・酸味は 1 から５の数字で評価していただく形になっています。

```ruby:bean.rb
# Table name: beans
#
#  id            :bigint           not null, primary key
#  name          :string(255)      not null
#  country       :string(255)      not null
#  description   :text(65535)
#  created_at    :datetime         not null
#  updated_at    :datetime         not null

class Bean < ApplicationsRecord
  has_many :reviews

  def evaluation_average
    evaluation = [:acidity,:bitter,:flavor,:rich,:sweet]
    evaluation.map do |e|
      review.average(e)
    end

  end

end

```

`ruby:review.rb

# Table name: reviews

#

# id :bigint not null, primary key

# content :text(65535) not null

# acidity :integer

# bitter :integer

# flavor :integer

# rich :integer

# sweet :integer

# created_at :datetime not null

# updated_at :datetime not null

# bean_id :bigint

class Review < ApplicationRecord
belongs_to :bean

end
`

view で香りなどの評価の平均を取ろうとすると、合計でクエリが５つ走ります。

```sql:コンソール
[1] pry(main)> Bean.find(1).evaluation_average
  SELECT AVG(`bean_reviews`.`acidity`) FROM `reviews` WHERE `reviews`.`bean_id` = 1
  SELECT AVG(`bean_reviews`.`bitter`) FROM `reviews` WHERE `reviews`.`bean_id` = 1
  SELECT AVG(`bean_reviews`.`flavor`) FROM `reviews` WHERE `reviews`.`bean_id` = 1
  SELECT AVG(`bean_reviews`.`rich`) FROM `reviews` WHERE `reviews`.`bean_id` = 1
  SELECT AVG(`bean_reviews`.`sweet`) FROM `reviews` WHERE `reviews`.`bean_id` = 1


```

### 対処法

select メソッドのサブクエリを利用することで1つの SQL で対応できます。
今回は ActiveRecord::Relation オブジェクトである必要がないので pluck メソッドを利用しています。
あと、レビューの関することなので review モデルのメソッドとして定義します。

```ruby:review.rb
class Review < ApplicationRecord
  belongs_to :bean

  def evaluations_average
    pluck("AVG(acidity) AS acidity_sum ,
           AVG(bitter) AS bitter_sum,
           AVG(sweet) AS sweet_sum,
           AVG(rich) AS rich_sum,
           AVG(flavor) AS flavor_sum")[0].map(&:to_s)
  end

end
```

これにより1つのクエリで５つの平均が配列で受け取れます。

# まとめ

そこまで view の表示速度は変わりませんが、N+1 がこれでなくなりました。
これ以上にいい方法があるような気がしますがご指摘があれば、コメントでお願いします。

### 参考記事

[Rails: Bullet で検出されない N+1 クエリを解消する　- TechRacho](https://techracho.bpsinc.jp/yusiro/2019_12_24/85407)
Dokcerによる Rails:6.0, MySQL:8.0の環境構築

```yml:docker-compose.yml
version: "3"

# .envファイルに環境変数は指定
services:
  db:
    image: mysql:8.0
    environment:
      - MYSQL_ROOT_PASSWORD=$DB_PASSWORD
      - MYSQL_USER=$DB_USER
      - MYSQL_PASSWORD=$DB_PASSWORD
      - TZ=Asia/Tokyo
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    volumes:
    - mysql-data:/var/lib/mysql
    ports:
    - 3306:3306
  app:
    build: .
    command: /bin/bash -c "rm -f /$WORKDIR/tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/$WORKDIR
      - ./vendor/bundle:/$WORKDIR/vendor/bundle
    environment:
      - TZ=Asia/Tokyo
      - DB_HOST=$DB_HOST
      - DB_USER=$DB_USER
      - DB_PASSWORD=$DB_PASSWORD
    ports:
      - $API_PORT:$CONTAINER_PORT
    depends_on:
      - db
    tty: true
    stdin_open: true
volumes:
  mysql-data:
    driver: local
```

## Dokcerfile
```Dockerfile:Dokcerfile
FROM ruby:3.0

# yarnのインストール
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install -y yarn

RUN apt-get update && apt-get install -y nodejs 

WORKDIR /usr/src/app
COPY Gemfile Gemfile.lock ./

RUN bundle install

COPY . ./

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
```

## Gemfile
```rb:Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 6.1'
```

## Gemfile.lock
```rb
# empty
```

## entrypoint.sh
```sh:entrypoint.sh
#!/bin/bash
set -e

# remove a potentially pre-existing server.pid for rails.
rm -f /myapp/tmp/pids/server.pid

# then exec the container's main process (what's set as cmd in the dockerfile).
exec "$@"
```
laravel 配列をrequest形式に変換してテスト

`$request` をダミーで用意し、テストしたい場合がある。
object形式にする必要があるが、

```hogeController.php

    //クレジット決済バック画面
    public static function test(request $request)
    {
//
        $ar = [
            "ip" => 123,
            "name" => 'hideki'
        ];

//
//        機能しない
//        $request = (object)$ar;

        $request->merge($ar);

        print_r($request->all());


```

merge を使う。
これでrequestのダミー値として、`$ar`を利用できる。
補数ってなんだ

コンピューターはその構造上、基本的には足し算しかできません。
加算器のみで構成した方が効率が良いから、直接減算ができないとのことです。
でも、電卓アプリやエクセル、スプレッドシートでは普通に減算処理ができていますよね。

実はそれは、**引き算を足し算に置き換えて計算する『補数』という仕組みを利用**しているからなんです。

#補数とは
補数とは・・・
**「元の数」と「ある数」を足したときに桁上がりが発生する数のうち「最小の数」のこと**を言います。
ちょっとわかりづらいので、普段から身近に使っている十進数を例にとって説明いたします。
##十進数の補数
十進数の97を例に取ってみます。
97にとって桁上がりする数字は3です。

```math
\begin{align}
x &=& 100 - 97\\
x &=&3
\end{align}
```

このとき、**97にxを足すと桁上がりが発生するため、桁上がりが発生する最小の数x=3は97の補数**と言えます。
つまり、補数とは**桁上がりを補完してあげる数**のことだと言い換えられますね。

この補数を用いることで、引き算を加算に置き換えることができます。

例えば以下の計算。

```math
20 - 3 = 17
```

上記を補数を用いて計算すると・・・

```math
\begin{align}
x &=& 100 - 3\\
x &=& 97
\end{align}
```
-3の補数が97とわかりましたので、20に加算してみます。

```math
\begin{align}
20 + 97 &=& 117
\end{align}
```
ということで補数を用いた計算をすると、答えは117だとわかりました。
これだとなんだか変ですよね。
引き算をしたかったのに、求めたい17という数字から大幅に上振れた117という数字が出てきちゃいました。

・・・鋭い方はお気づきかもしれませんが、**十のくらいまでだけを見るとしっかりと17が求められています**。
もともと二桁までの計算をしていたものなので、**三桁目（百の位）は桁溢れとして切り捨ててしまいます**。
従って、**117の三桁目を切り捨てた17が答え**になります。

これが補数を用いた計算になります。

##二進数の補数
それではコンピュータはどのように引き算をしているのでしょうか。
イチゼロしか取り扱えないコンピュータは全てを二進数で処理をします。しかもコンピュータは基本的に加算器で構成されていますので、直接的な引き算はできません。
（なんでもできるコンピュータが引き算もできないなんてなんか面白いですよね）

そこで補数の出番です。
先ほどと同様の計算を二進数で行ってみます。

```math
\begin{align}
(20)_{10} - (3)_{10} &=& (17)_{10}\\
(0001　0100)_2 - (0000　0011)_2 &=& ??
\end{align}
```
問題となるのは上記の"$- (0000 0011)_2$"の部分です。ここを**補数**で表現します。
十進数のときに求めた流れで、十進数→二進数に変換するのも問題ないですが、もう一つ二進数の補数（**二の補数**と言います）を求める方法があります。
流れは以下の通りです。

①該当する数のイチゼロを全て反転させる
②上記で反転した数にイチを足す

上記の二点を実施するだけで補数が求められます。
それでは、今回の計算で該当する"$-(0000 0011)_2$"の補数を求めてみます。

①$(0000　0011)_2$　→ $(1111　1100)_2$
②$(1111　1100)_2$ + $(0000　0001)_2$ → $(1111　1101)_2$

上記の補数を先の計算に当てはめてみましょう。

```math
\begin{align}
(0001　0100)_2 - (0000　0011)_2 &=& (0001　0100)_2 + (1111　1101)_2\\
&=&(1　0001　0001)_2
\end{align}
```
今回は8bitの計算なので、**9bit目は桁溢れとして切り捨てます**。
従って、補数を用いて求めた答えは・・・

```math
\begin{align}
(0001　0001)_2 = (17)_{10}
\end{align}
```

となり、しっかりと20-3の計算ができました。

こんな面倒なことをコンピュータは一生懸命やっているのです。

なので、コンピュータを労りたい気持ちがあるなら上記の繰り返し処理が必要となる**除算なんかはやらないように気をつけましょう**。（半分冗談です）
Laravelでデータべースに複合インデックスを貼ったり削除したりする

# 背景
Laravel8でマイグレーションを使ってインデックスをいろいろ操作するのってどうするんだろうと調べたときのメモ。
既存テーブルに複合インデックスを追加するのが主な目的。

## 実行環境
Laravel： 8.65
MySQL：8.0

## やりたい事
- Laravelでマイグレーションを使ってDBインデックスの貼りたい（複合インデックス）
    - 新規（テーブル作成時）
    - 追加（テーブル作成後）
    - 削除

### 作業手順
- マイグレーションファイルを手動でつくる。中身の編集は手動。ファイルができたら手動で`php artisan migrate`する。

- Laravelが作成したインデックス名はデフォルトで以下の名称になる。独自指定も可能。

> インデックス名 ： テーブル名__インデックスしたカラム名__インデックスタイプ

##  インデックスの追加方法いろいろ
事前に調べてみるとインデックスを作成するには以下の様な指定をすると作成できそう。
また、primaryキーを削除したり、uniqueインデックスを削除する、といったメソッドもあるよう。

①1つのカラム名だけを指定してインデックスを作成する。実際に作成されるインデックス名はLaravelに任せる。

```php
$table->index('user_name');
```

② 1つのカラム名を指定してインデックスを作成する。実際に作成されるインデックス名は指定された名前になる。

```php
$table->index('user_name','idx_user_name');
```

③ 複数のカラム名を指定して複合インデックスを作成する。複数指定する場合は配列として指定する。

```php
$table->index(['user_name', 'department']);
```

### 新規（テーブル作成時）
普通にテーブル定義した後にインデックスを追加する。※テーブルの意味は適当です

```php:
        Schema::create('accounts', function (Blueprint $table) {
            $table->id();
            $table->string('user_name',20)->unique();
            $table->string('department',20);
            $table->index(['user_name', 'department']); // ←複合インデックス追加
```

### 追加（テーブル作成後）
- マイグレーションファイル追加

インデックス名称はLaravelがデフォルトのものをつけてくれる。

```bash
$ php artisan make:migration add_index_to_accounts --table=accounts
```

- 上記で作成されたマイグレーションファイルを開き、up()とdown()を以下の様に修正する。

```php:database/migrations/YYYY_MM_DD_TIME_add_index_to_acconts.php

    public function up()
    {
        Schema::table('accounts', function (Blueprint $table) {
            // 複合インデックスを追加。名称はLaravelに任せる。
            $table->index(['user_name', 'department']);
        });
    }

    public function down()
    {
        Schema::table('accounts', function (Blueprint $table) {
            // カラム名のみを指定。名称はLaravelに任せる
            $table->dropIndex(['user_name', 'department']);
        });
    }

```

マイグレーション実行

```php:
# マイグレーション実行
php artisan migrate
# DBを確認すると複合インデックスが作成されている。
```

### インデックス削除
```php:
# マイグレーションロールバック
php artisan migrate:rollback --step=1
# DBを確認すると複合インデックスが削除されている。
```


## 気を付けるポイント
- 通常、DBに付与されるインデックス名はLaravelのデフォルトできまる。なので作成時にインデックス名は分からない（ルールに沿った名称がつけられるが）。
- 削除時もルールに沿った名称で削除してほしかったらカラム名を配列で指定する。
- こちらの方がルールを意識せずにしていできるのでよさげ。
※ただし、該当カラムに外部キーが指定してある場合にインデックスを削除しようとする(rollback)と、MySQLでは1553エラーがでるようなので、先に外部キーを削除するようにマイグレーションファイルを記載する。つまり外部キーの作成もこのインデックス追加のマイグレーションとセットですることが望ましいと思われる。

> SQLSTATE[HY000]: General error: 1553 Cannot drop index


## 参考記事
Laravel でインデックスを作成、削除する
https://qiita.com/sato_ryu/items/311df38b81ab427447ca

Laravel 8.x マイグレーション
https://readouble.com/laravel/8.x/ja/migrations.html

【Laravel】初めてのマイグレーション
https://qiita.com/manbolila/items/c19735438affefbfbe69

MySQL スロークエリ改善 初心者向け
https://zenn.dev/ohkisuguru/articles/48dff6cf195244
磁気浮上キースイッチに改造する　加工無し版

#注意事項
この記事を読んで、同じような作業をして、ケガやその他、損害等が生じる危険性があります。
作業は**自己責任**でしてください。

磁石の危険性を理解した上で、下記の記事を読んでください。
購入した磁石の取り扱いの説明と、注意事項をよく読んで、十分に理解してから作業をしてください。

反発した磁石が飛ぶ恐れがあるので、とても小さくて、一度、無くすと確実に見つけられなくなります。
飛んだ磁石が目に入り、ケガの恐れがあります。安全のために保護メガネの使用を検討してください。

#キーボードスイッチの分解と測定
キーボードスイッチを分解して、ステムの寸法を測定していた。

Gateron Pre-lubed 3 Pins Linear　Sliver　

ピン径　φ1.9
ボス径　約φ4.1　　ノギスで測れず、Gateron Caps switcheのスプリングが入った　

ふと、リング型の磁石があれば、スプリングを磁気反発に置き換えができるかも。

#磁石の選定
特注になるかもと、思いながら既成品がないか検索。
ありました。

ネオマグ株式会社
ネオジム磁石(N40)、リング型、Φ4xΦ2x1(mm)、高さ方向
キースイッチ1つにつき磁石2つ使います。

磁石の補足事項
内径公差がφ2±0.1のためマイナス公差の磁石は
ステムのピンに入らないか擦れるため
スムーズに入る磁石だけを選んで使用してください。

サマコバ磁石 リング型
サイズ的に使用できる種類がありますが、衝撃に弱く試用中に粉々になりました。
種類はネオジム磁石を使用してください。

#スプリングと磁石の入れ換え（ボトムケース加工なし）
キースイッチを分解して、スプリングと磁石を入れ替える。

ステムにネオジム磁石をはめる。反発するようにネオジム磁石をステムにピンに通したまま
磁石が飛ばないようにボトムケースに収める。
ペンなどで、ステムを押さえながら、トップカバーをペンに通して、嵌め合わせる。

接着材は使用をしなくても、ステムのピンがガイドになり磁石が外れることは無い。


#スイッチの動作確認
磁石の反発を利用してるため、押し下げるごとに押し込み強さが強くなる。

反発力が弱い組み合わせのため、重いキーキャップをつけるだけでもキーオンする場合があります。
完全に押し上げられていないので、ステムが0.5㎜程度下がっています。
磁石がステムの隙間より大きいためストロークが3.4㎜が3㎜程度になります。

小指を乗せるだけキーオンします。フェザータッチの押し加減になります。
キーを押し下げでステムがトップカバーにもボトムケースにも当たらないので
とても静かです。

この改造はスピードスイッチの使用を推奨します。

前回の方法より加工がない分は簡単に改造できますが、磁石の購入コストが約1.5~3倍になります。

このキースイッチの特徴
底打ちが無くタイピング音がしない静かなキースイッチ
軽い力でタイピング、連続的なタイピング操作に向いている

想定される使用用途は
エンブレムキーボード等の浅いキーストロークに慣れている人
FPSの早い入力レスポンスを求める人
長時間のタイピング
MIDI Pad　のキースイッチ　（静音性）

標準的なキーストロークが好みの方は
簡単な加工が必要ですがこちらを参考にしてください。
[Qiita:磁気浮上キースイッチに改造する](https://qiita.com/deepprog/items/1927d5a4cd439e1ffe83)


#補足1　磁石の変更　2022.01.25
片方の磁石を
株式会社マグファイン
NR0103
ネオジム Φ4mm X Φ2mm X 1.5mm
に変更

1.0㎜ 1.0㎜だと反発力が弱くステムが0.5㎜程度下がっていた。
1.5㎜ 1.0㎜にするとちょうどいい高さまでステムが上がる。
反発力がわずかに強くなったためか、キーキャップのグラつきが少なく感じる。
接点(プレトラベル)1.2mmに戻ったため、前より打ちやすく感じる。
その違いはわずかで使用者の好みしだい。

1.5㎜に変更によるメリットは磁石のコストが下がること。
交換コストはキースイッチ１つあたり磁石2個が約100円に下がった。
（磁石それぞれ100個分を購入した場合で算出）

ステムのピンにルブは滑るよりも張り付きによる抵抗が大きくなる場合があります。
磁石の反発力は離れるほど弱くなります。
そのためにルブの影響がいつもよりシビアになります。
ルブ無しか、ごく薄くルブをしてください。

#補足2　スプリング追加　2022.02.20
補足１はステムが0.5㎜程度下がっているのを改善のために行った。
磁石の反発は底打ちしない感触がスプリングよりも好みだ。
押し始めはスプリングのスッと下がり、巻き数で調整がやり易い。

良い所を組み合わせる。

磁石1.0㎜ 1.0㎜に　2ステージスプリングを半分に切ってボトムケースに入れる。
ステムがトップカバーに突き当たりキーキャップの高さが一定に戻った。

3ステージスプリング37gを2ステージスプリングにした切れ端のスプリングが
残っていたので、それと交換する。
バネ長が７ｍｍほどで、磁石を僅かに持ち上げる。

動作点まではスプリングで、磁石の反発で底打ちしないキースイッチとなった。
自宅を出発するときに到着時刻を通知する[iPhone ショートカット]

# はじめに
初めまして。今回、自分で初めて日常的に使えるプログラムっぽいものができたので紹介します。

:::note alert
この記事はiPhoneのショートカットを使用しています。
androidでは使えません。androidの方はMacro Droidなどがおすすめです。
:::

# こんな人におすすめ
#### **iPhoneを使いこなしている感を出したい人**

毎日遅刻しそうでハラハラしている人。
朝、毎日職場や学校までの所要時間を調べている人。
AndroidからiPhoneに乗り換えて、自動化ツールがなくてイライラしている人。
# 導入方法
以下のリンクからまずショートカットを手に入れてください。
[このリンクです](https://www.icloud.com/shortcuts/10c12fa79ac646afb78555018d8e46c3)
その後ショートカット内の一番上のタグの東京都を職場又は、学校に設定してください。
そしてオートメーションを開き、オートメーションから「自分が(自宅の住所を出発した時)に実行」と設定する
# 課題
ショートカットでは分数や秒数のみを単独で取得できない。そのため使うタグの数が長くなってしまい、わかりずらくなってしまった。
またオートメーションに使っている「出発するときに実行」はこちら側が、通知に対してアクションしなければならず、半自動化になってしまった。ショートカット内で、出発しているかどうかを判断しようとしたが、現在地をifの判定の中に入れることができなかった。
技術書典12に出展を決意してからイベント初日を終えるまで

[zenn](https://zenn.dev/mo_ri_regen/articles/techbookfest12)でも同じ記事を投稿しています📮
[zenn](https://zenn.dev/mo_ri_regen)ではフロントエンドのことも含め、いろいろ投稿しているのでぜひご覧ください👋

[技術書典 12](https://techbookfest.org/event/tbf12/)に出展しました 🎉
内容は私が転職したときの体験記です。
最後にリンクを載せておくので、よろしければ購入してください。
本著には紙面の都合上、一部しか載せられなかったエピソードも交えながら、
技術書典の出展初日を終えた感想を書きます。

## 執筆経緯

「技術書典で本、出さへん?」
この一言からすべてが始まりました。

たまたま友人と Discord で雑談をしていたときにこの言葉で誘ってみました。
すると、友人も乗り気になってくれたので次回の開催日が未定な状態で、
執筆作業の準備をしていました。

最初は何か技術本を書く予定で勉強を行っていましたが、
次回の技術書典の開催日が発表されたときに、
今の勉強のペースでは出展に間に合わないと判断しました。

そのときに今回はあきらめ、次回に向けて引き続き勉強をするという選択肢もありましたが、
やる気があるうちに出展した方がよいと考えました。
なので、特に知識が必要のない体験記を執筆する方向で進めた方がよいという話になりました。
ちょうど、転職活動を終えてその文章を Zenn で執筆しようと進めていましたが、
うまく文章にまとめることができずに筆が止まっていました。

技術書典で体験記を書くという方針と
私の転職活動体験記を書きたいという思いがマッチしたため
転職活動の話を執筆することに決まりました。

## 技術書典 12 初日を終えて

出展するまでは、何者でもない個人の転職活動の話に需要があるのか、
500 円という値段設定したが、お金を出してまでこの本をほしい人がどの程度いるのかという気持ちでした。
なので、友人と「10 冊売れたら成功だ」という話をしていました。
そのような心配をしていると、出展 30 分後に購入したという通知が来ました。

私が執筆した内容に興味をもって購入してくれたのか、
それとも知り合いが執筆したから購入しよう[^1]と思ってくれたのかわからないですが、
今までの苦労が報われた瞬間でした。

そして、初日を終えたときには売り上げ目標に迫る 9 冊も売れました。
あのとき、「今回の技術書典の出展をあきらめる」という選択を取らなくて正解だと確信した瞬間でした。

なので、私の本でなくても構わないので、
もし興味があるという本に出合った場合は購入してほしいと思います。
それが執筆者の励みになり、次回作の執筆モチベーションとなります。

私も興味のある本があれば積極的に購入しようと思います。

[^1]: 執筆者は、誰が購入したかまではわからないので安心してください。

## イベント

https://techbookfest.org/event/tbf12/

## 執筆本

https://techbookfest.org/product/5837317140905984?productVariantID=6556986827079680
技術導入についての覚書

先日、『[技術を的に当てる技術について](https://speakerdeck.com/altech/re-introduce-graphql)』という話をした。これに関連して、影響範囲のある技術を途中から導入するときにこのあたりは一定抑えるのが大事かなあと思ってるポイントがあるので、現状認識のスナップショットとしてちょっとまとめておく[^1]。

[^1]: 経験上、そういうポイントをちゃんと抑えておけば、「技術を入れたけどなんか良くならなかったね」みたいにな状況になることをかなりの割合で避けることができたり、同じ期間で受けられる恩恵を大きく増やすことができる。

- 最初に確認したいこと[^2]
  - その技術はどのような課題を解決しようとしているか？それはどのように要素分解できるか？
  - その技術を入れることで確実に解決される “重要な課題” はあるか？
- 初動
  - その価値が利用の増大に応じて複利的に利くようなものである場合、最初の根を張る必要がある
  - 早い段階で価値を感じられるように、具体性の高い問題解決を行うのが定石
  - 技術を整備したり使う人が理解するのには一定のコストがかかる
  - このコストをリターンがちょっとでも上回るようになることを最短で設計するということが重要
- 初動を終えた後
  - そこまでの手応えを振り返り、進むか撤退するかを決める
  - 定常的なワークフローに自然に組み込む、それに困った時に自然と使うように仕向けるなど拡大するようにする
  - ここまで来るとこのリターンとコストの差分であるプラスが増えていく状態になる
  - 経過を観察しながら、ボトルネックがあれば対処し、またさらに技術を使いやすくするなどの再投資を適宜行い、開発を加速させていく

[^2]: 技術のコンテキストと、自分たちのコンテキスト（困りごとなど）の両面から見ることが重要。自分たちの困りごとばかりに目を向けていると技術の価値を矮小化してしまう可能性があるし、技術の側だけから見ていると使いたいだけ、になってしまう可能性があるため。

### 競合技術がある場合

競合技術がある場合はそれと比較すると良い。これは、より良い選択肢を選ぶという意味ももちろんあるが、比較することで元の技術がより良く理解できるというになるためである。

- その技術の競合技術はあるか？その競合技術はどのように要素分解できるか？
- 要素のうち重複する部分と、そうでない部分を明確にする
- それらの要素はなぜ重複して、なぜ重複しないのかを明らかにする
- 特にエコシステムの発展度合いに起因する要素と、そうではない固定的な要素の違いに注意を払う
- この検討の過程で、それぞれの技術がどのような問題設定を敷いており、どのようなスコープの問題解決を行おうとしているのかの理解が深まる
- そのような問題設定はある程度、一般的に存在する問題であることが期待される
- その一般的な問題が自分たちの組織・ビジネス・プロダクトの状況にどの程度適合しているかを検討する
- 適合するのであれば、その技術が現在提供している価値だけでなく、発展することにより将来的に提供される価値も享受できることが期待できる
- 一般的に、技術の仕様と実装だけでなく、その技術が解決しようとしている問題の方を正しく捉えることが非常に重要
  - 参考：[技術を的に当てる技術について](https://speakerdeck.com/altech/re-introduce-graphql)
- 性質を理解した上で複数の技術を使いたい場合はポジショニングを明確にする
  - 参考：[Wantedly における Go 導入にまつわる技術背景](https://qiita.com/Altech/items/d0f7986ae86a4626199b)

### 耐用年数

時間の観点についても触れておく。技術の価値は、それが適切に機能している限りにおいて、入れてから使い終えるまでのトータルの時間積分になる。

このため、外的変化と内的変化のそれぞれについて粗くても良いので見立てを持っておく方が良い。

- その技術の耐用年数はどの程度か？より良い選択肢が市場に生まれるのにどのくらいかかるか？
  - 導入前：代替となる強力な選択肢の当たりをつけて、その状況を調べてみる
  - 導入後：その選択肢がどの程度、力を持つかを継続的にモニタリングしておくことで、状況が変化した場合に早い段階でキャッチする
- その技術の問題設定が自分たちの状況に適合しなくなるとしたらどのような要因がありうるか？
  - よくある観点：開発者の人数、ビジネスのモデル、サービスの性質、ソフトウェアの複雑性

これらを見立てると次のようなことを考えることになる。

- 技術の耐用年数があまりにも短ければ、モノとしてそっちの方が良いとしてもスルーした方が良い可能性がある
- 一方で、耐用年数内に導入コストを回収できるかは、いかに早く技術を立ち上げられるかでもある
- 立ち上げが早ければ、耐用年数が短くても使った方が良い可能性がある

おまけ：

こうやって見ただけでも事は複雑に絡み合っているわけだが、そういう中で一定のバランスを見てやっていくという動的性質がソフトウェアエンジニアリングの奥深いところなのではないかと思う。また、その意味ではここに書いたことも（あるいは世の中にある重厚な技術選定のフレームワークですら）、ルールではなく一つの目安でしかないので注意したい。
Ruby on Railsでアプリを作成してみた【recipegram】

## ER図

```mermaid
erDiagram
  users ||--o{ recipe : "1人のユーザーは0以上のレシピの投稿を持つ"

  users {
    bigint id PK
    string title　FK
    string content　FK
    string timestamp 　FK
  }

  recipe {
    bigint id PK
    references post FK
    references user FK
    text content "コメント内容"
    timestamp created_at
    timestamp deleted_at
  }

```



## gemとは？
便利な機能を簡単に実装できる

①device（ログイン機能）

②bulma-rails（デザイン）

③refile
(ファイルのアップロード先を柔軟んい設定できる。画像のサムネイルを生成できる)

④refile-mini_magick(refileと同じ)

## 環境開発の構築

```
rails new <プログラム名>
cd <プログラム名>
```

```
//Gemfileにコードを追加し、『bundle add devise』する

gem 'devise'
gem "refile", require: "refile/rails", github: 'manfe/refile'
gem "refile-mini_magick"
gem "bulma-rails"
```
```
// app/assets/stylesheets/application.css

@import 'bulma';
```
## ①device（ログイン機能）

```
rails g devise:install
rails g devise:views
```





## 参考サイト
[【プログラミング入門】Ruby on Rails でウェブアプリを作ってみよう ~ PART 1（イントロダクション）~ Let's build with Ruby on Rails](https://www.youtube.com/watch?v=auLih2TFwio&t=647s)
[【プログラミング入門】Ruby on Rails でウェブアプリを作ってみよう ~ PART 2（実装①）~ Let's build with Ruby on Rails](https://www.youtube.com/watch?v=uos2mowBrJA)
[Action Cable With Devise User Accounts | Ruby On Rails 7 Tutorial](https://www.youtube.com/watch?v=EpMWkX5t0dk)
Docker AmazonLinux2のコンテナにNode.jsを入れている部分の処理を紐解いてみた

# 概要

- 他の方が作ってくれたのLaravelアプリケーションローカル開発環境のAmazonLinux2コンテナにNode.jsを入れている部分の処理をまとめてみる

# Node.jsの入れている部分Dockerfile

- 下記のような処理をしている。

    ```Dockerfile
    RUN curl -fsSL https://rpm.nodesource.com/setup_14.x | bash -
    RUN yum -y install nodejs && \
        npm i -g n && \
        n 14
    ```

- 理解しやすくするため&&と\で一行に記載されている部分を冗長だがRUNになおしてみる。

    ```Dockerfile
    RUN curl -fsSL https://rpm.nodesource.com/setup_14.x | bash -
    RUN yum -y install nodejs
    RUN npm i -g n
    RUN n 14
    ```

- 上記に対して一行づつ見てゆく。

# 何をしているんだろう

- 下記の処理に一行づつコメントを入れて何をしているか解説してみる。

    ```Dockerfile
    RUN curl -fsSL https://rpm.nodesource.com/setup_14.x | bash -
    RUN yum -y install nodejs
    RUN npm i -g n
    RUN n 14
    ```

- コメントを追加した

    ```Dockerfile
    # Node.js14系のyumリポジトリを追加
    RUN curl -fsSL https://rpm.nodesource.com/setup_14.x | bash -
    # Node.jsを一旦インストール（npmコマンドを実行するためだけ）
    RUN yum -y install nodejs
    # 「n」というNode.jsのバージョン管理ツールを取得
    # npm i は npm installの省略記法
    RUN npm i -g n
    # 「ｎ」を用いてNode.js14系のインストールを実行
    RUN n 14
    ```

- `RUN curl -fsSL https://rpm.nodesource.com/setup_14.x | bash -`について
    - Node.jsのリポジトリを追加している。
    - こちらにコマンドが記載されている[https://github.com/nodesource/distributions#enterprise-linux-based-distributions](https://github.com/nodesource/distributions#enterprise-linux-based-distributions)
- `RUN yum -y install nodejs`について
    - 先に取得したリポジトリからyumを用いてNode.jsをインストールしている。
    - これはこの次に実行するnpmコマンド実行のため。
- `RUN npm i -g n`について
    - Node.jsのバージョンを管理してくれるツール「n」をインストールしている。
    - このコマンドそのものは当該のツールのGithubに記載されている。
    - [https://github.com/tj/n#installation](https://github.com/tj/n#installation)
- `RUN n 14`について
    - 「n」を用いてNode.jsのバージョン14をインストールしている。
    - [https://github.com/tj/n#installing-nodejs-versions](https://github.com/tj/n#installing-nodejs-versions)

# 参考文献

- [https://yucatio.hatenablog.com/entry/2019/12/06/081900](https://yucatio.hatenablog.com/entry/2019/12/06/081900)
- [https://qiita.com/standard-software/items/2ac49a409688733c90e7](https://qiita.com/standard-software/items/2ac49a409688733c90e7)
- [https://parashuto.com/rriver/tools/updating-node-js-and-npm](https://parashuto.com/rriver/tools/updating-node-js-and-npm)
- [https://github.com/tj/n](https://github.com/tj/n)
- [https://github.com/nodesource/distributions](https://github.com/nodesource/distributions)
メモ：STM32F407開発ボードとKeilでFreeRTOSを動かす際の注意点

# 記事の概要
STM32 Discovery開発ボードのSTM32F407 DISCOVERY Boardを用いて、FreeRTOSを動かす際の注意点メモです。

統合開発環境にはKeilの無料試用版を用います。

STM32においてFreeRTOSを使用する事例には、FreeRTOSのラッパーAPIであるCMSIS RTOSを用いている場合が多いです。
CMSIS RTOSは統合開発環境のSTM32CubeMXから自動で組み込むことができるので便利です。
ですが、今回はオープンソースとして提供されているFreeRTOSをダウンロードしてプロジェクトに組み込みます。

# 方法

基本的には以下のサイトの指示に従えば、STM32F407開発ボードでFreeRTOSを動かすことができます。

https://www.instructables.com/Setting-Up-FreeRTOS-From-Scratch-on-STM32F407-Disc/

FreeRTOSは[2022年1月23日時点の最新版であるFreeRTOS 202112.00](https://www.freertos.org/a00104.html)を使用しても動きませんでした。
また設定ファイルのFreeRTOSCOnfig.hは自分で作成しないといけません。

上記サイトのGitHubのFreeRTOSとFreeRTOSConfig.hを使うのが一番簡単だと思います。
必要に応じて、新しいバージョンのFreeRTOSと置き換えたり、FreeRTOSConfig.hの設定を変更します。

https://github.com/SharathN25/FreeRTOS_On_STM32F407Discovery

サイトではSTDOUTをIMTとして使用しています。そしてST-LInkのオプション設定もそれに合わせてTrace Enableを有効にしています。
これはDebug Viewerでprintfを使用するためです。
printfを使用しないのでしたら、この設定はしなくても問題ありません。

https://embeddeddiaries.com/alternative-debugging-prints-for-stm32-without-uart/

https://www.keil.com/support/man/docs/jlink/jlink_trace_itm_viewer.htm

# 注意点

KeilのARM Compilerコンパイラが2022年1月23日時点のデフォルト設定であるversion 6だと、RVDS\ARM_CM4Fのファイルport.cが原因で大量のエラーが出ます。

```
./FreeRTOS/Source/portable/RVDS/ARM_CM4F\portmacro.h(167): error: unknown type name '__forceinline'
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
```

RVDS\ARM_CM4Fのport.cをGCC\ARM_CM4Fのport.cに置き換えるとエラーは消えますが、スケジューリングが動作しなくなります。
このエラーは、以下のようにコンパイラを古いバージョンのversion 5に変更すると消えます。

![FreeRTOSのコンパイラ設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150620/1928e410-de12-469b-504a-ff1f41d0eb27.png)


今後、この問題は解消されると思いますが、それまではこの方法で対応します。

# 参考
Keilでのプロジェクト作成方法については以下をご参照ください。

https://qiita.com/Kosuke_Matsui/items/93e02399b00ac7026bed
定例業務の断捨離！Power Automateで今週の予定をTeamsに自動投稿する。

#定例業務を見直したい
現在、小売業の企業で社内教育や採用を担当しています。
上記を行う十数名のメンバーの業務の進捗確認、勤怠管理も私の業務の1つです。
現部署に異動して4ヵ月が経ち、メンバーの働く場所・時間・業務内容が全員異なることから、各自が滞りなく業務が遂行出来るようにするためにも、私自身の「定例業務」を整理していつでもサポートが出来るような働き方に変えていく必要があると思い始めたところです。

#私が毎週している主な定例業務
- メンバーへの定期連絡（週1回～）…今週のノー残業デー、勤務実績、社内情報の共有など
- 経費の承認（～週5回）…社内の専用システムを利用
- メンバーの出張申請の事前・事後承認（週3回）…同上
- 毎週複数の定例ミーティングへ出席（週4回）…主に社内で対面実施
- メールの確認・返信（1日10～50通）…他に「社内連絡専用システム」の確認もある

#したいこと
- まずは実施者・受信者双方が受け入れやすいと思われる「定期連絡」から見直したい。
- 業務ツールとしてTeamsを2021年12月から全員へ導入してもらったが、活用している人が限られているため、使って慣れて欲しい。（ついでに私のメールの数も減らしたい）

#完成品
Power Automate上で「毎週月曜日」に「今週のノー残業デーの実施日」を取得し、Teamsに投稿出来るようにしました。
<B>これで今まで「ノー残業―での実施日の確認→メッセージ作成→送信」をしていた1分くらいが短縮出来ます！</B>
![全体フロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2490846/5e603437-9936-5110-509c-8cd965e20b15.png)

#使用したもの
- Power Automate
- Outlookの予定表

#制作過程
1.予定表へノー残業デー実施日を登録。
![質問③.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2490846/4f31558c-962a-bfdc-419e-74b6379590ec.png)
2.Power Automateで「スケジュール済みクラウドフロー」を選択し、メッセージを送信したい日時を指定。（今回は「日本時間」「毎週月曜日」「10時」を設定）
![フロー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2490846/c858094d-8763-95ed-ba19-7bd8b57870ff.png)
![質問１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2490846/40e9ea2e-809d-61e9-5fb9-f21c564c6da4.png)
3.予定表「ノー残業デー」に開始日・終了日（＝実行した日。つまり毎週月曜日）に登録がある予定を取得するよう設定。
![質問２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2490846/3c744cac-25bf-9262-8345-00eb1342623e.png)
4.Teamsの投稿先を「チーム：稼働テスト」「チャネル：ノー残業デー」へ設定。予定の本文に「その週のノー残業デーの日」を入力してあるため、「本文」をメッセージとして投稿するよう設定。
![④.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2490846/a96c45f1-59b1-8264-0a0f-5923eed32de6.png)
![テスト123.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2490846/7a4b02b4-585f-70d1-7d34-135e0dae2144.png)

#気を付けたいこと
動作テストのために設定時間を「1分おきに投稿」にしていたら、ご飯を食べている間に延々と投稿が続いていたので、メッセージ欄が大変なことになっていました。
自分の設定で次第でメンバーに迷惑をかけかねないので、設定をよく確認してから開始する必要があります。
![完成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2490846/ead4e9ac-e5d9-60c4-8d36-712b7406f898.png)

#今後、活かせそうなこと
例えば「シフトの開始・終了日」「経費の処理日」など日付が社内的に固定されているものも多く、月間単位では定期的に連絡をしていることが結構あると気づきました。あくまでパッと浮かんだ一例ではありましたが、メンバーが忘れないようにする仕組みも考えつつ、自分の定例業務の見直しをもっと進めたいと思いました。
GitHub Actions で DynamoDB Local を使う

GitHub Actions で DynamoDB Local を使う方法について調べると、[mooyoul/dynamodb-actions](https://github.com/mooyoul/dynamodb-actions) や [rrainn/dynamodb-action](https://github.com/rrainn/dynamodb-action) を使う方法は出てくるんだけど、サードパーティのアクションを使わずにできるのでメモ。

やることは簡単で、

1. [GitHub Actionsのサービスコンテナ](https://docs.github.com/ja/actions/using-containerized-services/about-service-containers) として [dynamodb-local](https://hub.docker.com/r/amazon/dynamodb-local/) を起動
1. dynamodb-local に `aws dynamodb create-table` する
 - aws-cli は [GitHub Actions のランナー環境](https://github.com/actions/virtual-environments)には大体標準で入ってる

だけ。

```yml:example.yml
name: Some Job Using DynamoDB

jobs:
  some_job_using_dynamodb_local:
    runs-on: ubuntu-latest

    # services で localhost:8000 で dynamodb-local を起動する
    services:
      dynamodb:
        image: amazon/dynamodb-local
        ports:
          - 8000:8000

    steps:
      - name: Setup DynamoDB Local
        env:
          AWS_ACCESS_KEY_ID: dummy
          AWS_SECRET_ACCESS_KEY: dummy
          AWS_DEFAULT_REGION: ap-northeast-1
        # create-table とかする
        # https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Tools.CLI.html
        run: |
          aws dynamodb create-table --endpoint-url http://localhost:8000 \
            --table-name your_table_name \
            --attribute-definitions AttributeName=hoge,AttributeType=S \
            --key-schema AttributeName=hoge,KeyType=HASH \
            --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1

      - name: Check DynamoDB Table
        env:
          AWS_ACCESS_KEY_ID: dummy
          AWS_SECRET_ACCESS_KEY: dummy
          AWS_DEFAULT_REGION: ap-northeast-1
        run: aws dynamodb describe-table --endpoint-url http://localhost:8000 --table-name your_table_name
        
```

create-table する対象が増えてきたら何か工夫しないといけなそう。

# 例

https://github.com/oieioi/dynamodb-local-in-github-action/actions/workflows/example.yml

# リンク

- [GitHub Actions / サービスコンテナについて - GitHub Docs](https://docs.github.com/ja/actions/using-containerized-services/about-service-containers) 
- [actions/virtual-environments (GitHub Actionsのランナー環境) - GitHub](https://github.com/actions/virtual-environments)
- [amazon/dynamodb-local - Docker Hub](https://hub.docker.com/r/amazon/dynamodb-local/)
- [Amazon DynamoDB AWS CLI の使用 - AWS Documentation](https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Tools.CLI.html)
C#でWindowsサービスを作る（6）

#概要
[前回の記事C#でWindows Serviceを作る（5）](https://qiita.com/yossihard/items/09133e2170c888fd78e0)の続き。
タイマー処理の実装を行った際の備忘録。

#環境
Windows 10
Visual Studio 2019 Community
⇒前回は2017でしたが、今回から諸般の事情で2019にしてます。すみません・・・。

#手順
サービスでよくある処理は、定周期で何かを実行することだと思います。

ServiceBaseクラスの派生クラス（デフォルトだとService1.csというファイル。私のプロジェクトではServiceTest.cs）を開きます。

まず、以下のusingステートメントを追加して、タイマーコンポーネントを使用できるようにします。

```csharp
using System.Timers;
```

つづいてタイマーコンポーネントの生成、設定をしていきます。
Intervalで、周期を指定します。
周期はミリ秒単位で指定できます。
⇒例えば分単位で実行したい場合は、60000を指定します。

Elapsedに、指定した周期が経過した際に実行されるイベント処理関数（ここではOnTimerという関数）を登録します。

Start関数を呼び出すと、タイマーが起動します。

```csharp
    // タイマー処理
    CheckTimer = new Timer();
    CheckTimer.Interval = 60000; // 1分（60秒）
    CheckTimer.Elapsed += new ElapsedEventHandler(this.OnTimer);
    CheckTimer.Start();

```

ここではOnTimer関数内に、周期実行したい処理を記載します。

```csharp
    protected void OnTimer(object sender, EventArgs e)
    {
        // 定周期処理
    }
```


周期実行を止めたい場合は、Stop関数を呼び出します。

```csharp
    CheckTimer.Stop();
```

※Enabledをfalseに設定することでも、タイマー処理を停止できるようですね。
[https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer.stop?view=net-6.0#remarks](https://docs.microsoft.com/en-us/dotnet/api/system.timers.timer.stop?view=net-6.0#remarks)


#参考
[チュートリアル: Windows サービス アプリを作成する](https://docs.microsoft.com/ja-jp/dotnet/framework/windows-services/walkthrough-creating-a-windows-service-application-in-the-component-designer)
新しい PROFILE を試す (Oracle Database 21c)

Oracle Database 21c にはユーザーのパスワードやリソースを制限する PROFILE に ORA_CIS_PROFILE が追加されました。本記事では DEFAULT プロファイル以外のプロファイルを比較し、ORA_CIS_PROFILE を紹介します。

# PROFILE
 PROFILE は古いバージョンの Oracle Database から存在する機能です。パスワード期間やユーザーのセッション時間等、ユーザーのセキュリティやリソース使用量に関する制限をグループ化し、ユーザーの属性に指定することで制限を行います。プロファイル自体はデータベース管理者が自由に作成や変更できます。テンプレートとしていくつかのプロファイルがあらかじめ提供されています。

|PROFILE名|説明|バージョン|
|:---|:---|:---|
|DEFAULT|標準のプロファイル||
|ORA_CIS_PROFILE| CIS に準拠するプロファイル|21cから|
|ORA_STIG_PROFILE| STIG に準拠するプロファイル|12cから|

プロファイル名の一部になっている STIG や CIS はセキュリティ標準を決定している組織の名前です。
[STIG (Security Technical Implementation Guides (STIG)](https://public.cyber.mil/stigs/) は米国国防総省の国防情報システム局 (DISA) が作成したシステム構成の標準です。
[CIS (Center for Internet Security)](https://www.cisecurity.org/) はセキュリティ標準を定めた非営利団体です。Oracle Database のプロファイルにはそれぞれの団体が定めたセキュリティ標準に準拠する方法としてプロファイルが用意されています。

# 比較
それぞれのプロファイル構成について比較します。一般的には ORA_STIG_PROFILE の方が厳しくなっていますが、パスワード再利用の検査回数（PASSWORD_REUSE_MAX）やセッション数の上限（SESSIONS_PER_USER）等は ORA_CIS_PROFILE の方が厳しくなっています。

| Resource Name| ORA_CIS_PROFILE | ORA_STIG_PROFILE|DEFAULT|備考|
|:---|---:|---:|---:|:---|
|FAILED_LOGIN_ATTEMPTS | 5| 3| 10||
|IDLE_TIME|DEFAULT|15|UNLIMITED||
|INACTIVE_ACCOUNT_TIME | 120| 35| 365||
|PASSWORD_GRACE_TIME |5 | 0| 7|
|PASSWORD_LIFE_TIME | 90| 35| 180|
|PASSWORD_LOCK_TIME | 1|DEFAULT | 1|
|PASSWORD_REUSE_MAX | 20| 5|UNLIMITED ||
|PASSWORD_REUSE_TIME |50| 175|UNLIMITED||
|PASSWORD_VERIFY_FUNCTION| ORA12C_VERIFY_FUNCTION| ORA12C_STIG_VERIFY_FUNCTION|NULL ||
|SESSIONS_PER_USER |10 | DEFAULT|UNLIMITED||

# パスワードの複雑性
　パスワードに求められる複雑性は PASSWORD_VERIFY_FUNCTION 属性に指定されたファンクションによって担保されます。
ORA_STIG_PROFILE と ORA_CIS_PROFILE には異なるファンクションが指定されています。こちらも ORA12C_STIG_FUNCTION の方が厳しくなっています。

|チェック項目|ORA12C_VERIFY_FUNCTION|ORA12C_STIG_VERIFY_FUNCTION|
|:---|:---|:---|
|パスワード長|8文字以上|15文字以上|
|含む文字|文字, 数字, 特殊文字を含む|大文字２個以上, 小文字２個以上, 数字２個以上, 特殊文字２個以上を含む|
|禁止文字列|ユーザー名, 逆順のユーザー名,データベース名, oracle|無し|
|前回との差分|3文字以上|４文字以上|

マニュアルは「[Oracle Database セキュリティガイド 21c](https://docs.oracle.com/cd/F39414_01/dbseg/index.html)」を参照してください。
Django REST APIの構築中に"ImportError: cannot import name 'six' from 'django.utils'"と"ImportError: cannot import name 'six' from 'django.utils'"が発生

Django REST APIの勉強をしていたら、以下エラーが出てmanage.py runsererが動いてくれない。

```
ImportError: cannot import name 'six' from 'django.utils'
```
この問題はpipでdjango-utils-sixをインストールして解決。

```
$ pip install django-utils-six
```

すると今度は新たに以下エラーが出てしまった。

```
ImportError: cannot import name 'STATUS_CODE_TEXT' from 'django.core.handlers.wsgi'
```
調べてみると、djangorestframeworkが古いと出ることがあるらしい。結局以下コマンドで解決出来た。

```
$ pip install -U djangorestframework
```
（自分の環境ではdjangorestframework-3.1.1が入っていたのだが、これがdjangorestframework-3.13.1にアップグレードされてエラーが出なくなった）
URLSearchParamsを活用する

`URLSearchParams`を使ってみたら、けっこう便利でした。

## `URLSearchParams`とは

`URLSearchParams`は、URLのクエリ文字列を生成するための、JavaScriptにあるオブジェクトです。死に体なIE11を除けば、サポートの続くブラウザでほぼ利用可能です（[Can I use](https://caniuse.com/urlsearchparams)）。

## 何が便利か

何も使わずにクエリ文字列を組み立てようとすれば、ループを回しつつエスケープをかけたりなど、各種の作業が必要となります。`URLSearchParams`は、そういった作業を全部担ってくれるので、中身のデータを作るのに専念できます。

また、HTTPリクエストを投げる際に、`application/x-www-form-urlencoded`は`multipart/form-data`よりデータがコンパクトになりますので、その点でも有利です。

## 生成する

`new URLSearchParams`で生成できますが、コンストラクタに渡せる引数のパターンがいくつもあります。

* 引数なしで、クエリのない状態のオブジェクトを作る
* クエリ文字列を与えて、それを解析してオブジェクトに起こす
* `[['キー1', '値1'], ['キー2', '値2']…]`のような、キーと値のペアの配列を渡す
* `{'キー': '値'}`形式のオブジェクトを渡す

なお、`URLSearchParams`は同じキーを複数入れることができますが、オブジェクト形式では同じキーを複数渡せませんので要注意です。

## 編集する

出来上がった`URLSearchParams`オブジェクトに対して、`append`でキー・値のペアを追加する、`delete`で削除する、`set`でキー・値のペアを設定する、`forEach`や`for-of`でループを回すなどが可能です。

なお、「既存のクエリを`URLSearchParams`でパースして、必要な部分を書き換える」ような応用であれば別ですが、そうでないなら「配列やオブジェクトとしてデータを整えた上で、最後に`new URLSearchParams`へ投げ込む」ほうがやりやすいかもしれません。

## 出力する

`URLSearchParams`の結果を得たい場合、大きく2つの方法が考えられます。

* `toString()`でクエリ文字列にする。なお、`文字列 + オブジェクト`のような連結演算や、`` `${オブジェクト}` ``のような式展開を行うと、自動で`toString()`が行われますので、自分から書かなくて構わない場合もあります。
* `URLSearchParams`オブジェクトをそのまま`XMLHTTPRequest`や`fetch`に渡して、`POST`などのリクエストボディに組み込む。なお、`URLSearchParams`を使う場合、リクエストの`Content-Type`は`application/x-www-form-urlencoded`であることが明らかですので、自分でヘッダを書く必要もなくなって楽です。

## 応用編

`<form>`の値をクエリ文字列に起こしたい場合、`FormData`に「フォームから値を拾う」機能がありますので、`new URLSearchParams([...new FormData(formElement)])`のように、「`FormData`をspreadして配列にしてから`URLSearchParams`に入れる」という形で実行可能です（もちろん、`<input type="file">`がないのが前提です）。

## 外部リンク

* [URL Standard - WHATWG](https://url.spec.whatwg.org/#urlsearchparams)
* [URLSearchParams - MDN](https://developer.mozilla.org/ja/docs/Web/API/URLSearchParams)