OmniAICreator/Qiita-1.07M huggingface dataset:
【小ネタ】SharePointリストの日本語になっている列名を取得する

PowerAutomateやPowerAppsでSharePointリストの列名を取得するとき、列名が日本語になっているときそのままの列名では取得できない場合があります。
そんなときに列名を日本語のままで取得するお話です。
![qiita1-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/e7e48987-6a6a-1369-2a5c-c4c63878b416.jpeg)
![qiita1-2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/e1c92132-db0b-81cf-ba0b-696b8fb773f2.jpeg)
## フィールド名を取得
日本語になっている列名は「OData_」+「フィールド名」とすることで取得できます。
フィールド名は以下画像の赤枠の通りクリックして取得します。
![qiita1-3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/6b1fbe0e-83e7-c927-729d-a17aafc179e2.jpeg)
![qiita1-4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/2b801ed4-db3c-5ccd-01eb-0e7bde8794f3.jpeg)
![qiita1-5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/cf7bfaac-d8d1-d0ba-95ee-ed7c999b51df.jpeg)
![qiita1-6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/59ff665a-20e0-8ebe-c168-8332a17e720a.jpeg)

上記の例の場合、「OData\_」+「\_x597d\_\_x304d\_\_x306a\_\_x98df\_\_x30」で、
「OData\_\_x597d\_\_x304d\_\_x306a\_\_x98df\_\_x30」から「好きな食べ物」列を取得できます。
![qiita1-7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/374cf595-0088-af62-d9f9-22eee3f99c66.jpeg)
ReactのRefの使い方(初心者向け)

最近React Refを勉強したのですが、大体の初心者向けに書かれている記事に記載されている使い方がRefの使用用途としてはあまりしっくり来なかったので、自分用メモとして残しておきます。

#React Refの使い道
RefはDOMを参照するために使うことが出来て、テキストフォーカスや、テキスト選択、アニメーションやサードパーティーDOMライブラリに使うことが出来ます。

ここではDOMに変更を入れる例を書いてみました。(テキストフォーカスする例は死ぬほどあるので...)

```App.js
import { useRef } from "react";

export default function App() {
  const ref = useRef(null);
  return (
    <>
      <button
        onClick={() => {
          const li = document.createElement("li");
          li.innerText = "test";
          ref.current.appendChild(li);
        }}
      >
        Click
      </button>
      <ul ref={ref}></ul>
    </>
  );
}
```

ちなみに自作コンポーネントへrefを渡す際は、`forwardRef`を使わないとrefが使えないので、そちらを使った例についても書いてみました。

```App.js
import { forwardRef, useRef } from "react";

const Input = forwardRef((props, ref) => {
  return <input type="text" ref={ref} {...props} />;
});

export default function App() {
  const ref = useRef(null);
  return (
    <>
      <Input ref={ref} />
      <button
        onClick={() => {
          ref.current.value = "test";
        }}
      >
        Click
      </button>
    </>
  );
}

```
Microsoft TeamsのWebhook URLにNode.jsから情報を送ってみる ~その2~


## ただのテキスト以外も送ってみたい

https://qiita.com/n0bisuke/items/f61b1f653d0aa2f93dc2


前回の記事の続きで、ちょっとリッチな情報を送ってみます。
前回は最もシンプルなテキスト送信を試していました。

WebhookのURLの取得は前回記事参照です。

> ![スクリーンショット 2022-01-17 17.27.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/321cd0d6-173a-af59-169f-04933a3dafda.png "スクリーンショット 2022-01-17 17.27.10.png")


こちらのドキュメントを参考に書いてみました。

https://docs.microsoft.com/ja-jp/outlook/actionable-messages/message-card-reference

## コピペ用コード

こんな感じです。

```js
'use strict'

const axios = require('axios');

const URL = `https://xxxxxxxxxxxxxxxxxxxxxxx`;

axios.post(URL, {
    "@type": "MessageCard",
    "@context": "https://hogehoge.com/hogehoge.png",
    "themeColor": "0076D7",
    "summary": "ディスカッション",
    "sections": [{
        "activityTitle": "Larry Bryant created a new task",
        "activitySubtitle": "On Project Tango",
        "activityImage": "https://hogehoge.com/xxxxx.png",
        
        "facts": [{
            "name": "投稿者",
            "value": "うこ"
        }, {
            "name": "URL",
            "value": "https://hogehoge.com/xxxx/yyy"
        }, {
            "name": "Status",
            "value": "Not started"
        }],
        "markdown": true
    }]
})
    .then(res => console.log(res.data));
    
```

こんな処理を実行すると冒頭に載せた画像のような通知がTeamsに投稿されます。


## activityImageが表示されない

`activityImage`に画像を指定すればアイコンっぽいのが表示されるようなことがドキュメントに書いてますが、何度か試してもうまくいきません。

エラーが出るわけでもないので何かの読み込みが追いついてないのかなんなのか...
debug.gemでDocker上のRubyをデバッグしたい

# やりたいこと

[ruby/debug: Debugging functionality for Ruby](https://github.com/ruby/debug)

を使って、Dockerコンテナ上で起動したプログラムをデバッグする

# 参考

- [Ruby 3.1 の debug.gem を自慢したい - クックパッド開発者ブログ] (https://techlife.cookpad.com/entry/2021/12/27/202133)

# 方法

各環境で下のようなコマンドを実行する。
このとき、利用したいコンテナ側のポートを公開しておく。

```bash
# コンテナ側
$ rdbg -O --host=0.0.0.0 --port={任意のポート} main.rb
# ホスト側
$ rdbg -A localhost {任意のポート}
```

# サンプル

## 準備

```bash
# 構成
$ tree app
app
└── main.rb
```

```main.rb
a = 1
b = 2
puts a
puts b
c = a + b
puts c
c += 3
puts c
```

## 検証

### コンテナ側

```bash
# コンテナを起動
$ docker run -it -p 1234:1234 -v $PWD/app:/app --rm ruby:3.0.3-bullseye /bin/bash
# コンテナ内
## debugをインストール
$ gem install debug
## debugの起動
$ cd /app
$ rdbg -O --host=0.0.0.0 --port=1234 main.rb
DEBUGGER: Debugger can attach via TCP/IP (0.0.0.0:1234)
DEBUGGER: wait for debugger connection...
DEBUGGER: Connected.
1
2
3
6
DEBUGGER: Disconnected.
```

### ホスト側

```bash
$ rdbg -A localhost 1234
[1, 8] in main.rb
=>   1| a = 1
     2| b = 2
     3| puts a
     4| puts b
     5| c = a + b
     6| puts c
     7| c += 3
     8| puts c
=>#0    <main> at main.rb:1
```
【Ruby】eachの入れ子

※初心者向け
※アウトプット練習の為

開発環境
rails 6.0.4.4
ruby 2.6.5

##問題内容
以下のように、果物の名前と値段が入った配列があります。
この配列を用いて、果物の名前とそれぞれの合計額が出力される
コードを記述してください。

```ruby
fruits_price = [["apple", [200, 250, 220]], ["orange", [100, 120, 80]], ["melon", [1200, 1500]]]
```

```
（出力）
appleの合計金額は670円です
orangeの合計金額は300円です
melonの合計金額は2700円です
```

##解答

```ruby

fruits_price = [["apple", [200, 250, 220]], ["orange", [100, 120, 80]], ["melon", [1200, 1500]]]

fruits_price.each do |fruit|
  sum = 0
  fruit[1].each do |price|
    sum += price
  end
  puts "#{fruit[0]}の合計金額は#{sum}円です"
end
```

##解説
最初に、3行目でfruits_priceから**["apple", [200, 250, 220]]**という値を取り出し、
変数fruitに代入します。その後4行目〜7行目で、fruitの1番目の値**[200, 250, 220]**
から値を1つずつ取り出して、自己代入しながらsumを出力します。
丁寧に見ると以下のようになります。

```ruby
# 1回目
sum = 0
#sum += priceはsum = sum + priceと解釈できる
sum = 0 + 200
# 返り値
sum = 200

#2回目 
#sumは1回目の200という値が返されます
sum = 200
sum = 200 + 250
sum = 450

#3回目 
#sumは2回目の450という値が返されます
sum = 450
sum = 450 + 220
sum = 670
```

この結果、**appleの合計金額は670円です**と出力されるようになります。この作業を残り2つorange、melonでも繰り返します。
IBM Cloud Pak for Data as a Service ハンズオン資料（5.データ仮想化編）2022年1月作成/2022年12月更新

# 5.データ仮想化編
# Cloud Pak for Data as a Serviceハンズオンメニュー一覧
- [0.目次](https://qiita.com/Shumpei_Kubo/items/993e0fabfaebf56fe547)
- [1.構築編](https://qiita.com/Shumpei_Kubo/items/c228b70bd33a8fce9c8f)
- [2.設定編](https://qiita.com/Shumpei_Kubo/items/6793b6759c761301b687)
- [3.分析編](https://qiita.com/Shumpei_Kubo/items/78274f839205e703a8a6)
- [3.分析編-A ダッシュボードで新型コロナの新規陽性者変遷を見る](https://qiita.com/Shumpei_Kubo/items/76144e3bf1898b7a48de)
- [4.ETL編](https://qiita.com/Shumpei_Kubo/items/aad5aa18731224d3dfcb)
- [5.データ仮想化編（当記事）](https://qiita.com/Shumpei_Kubo/items/a8e2418b171d60793cc6)
- [a.Db2へのデータロード編](https://qiita.com/Shumpei_Kubo/items/c9b51fbbe1b470e9c4ad)
- [99.動画編](https://qiita.com/Shumpei_Kubo/items/8dc1e55f62ddee859c30)


# 1.Watson Query有効化
- [こちら](https://qiita.com/Shumpei_Kubo/items/1ff4f6d98de1435ce458)の手順でWatson Query を有効化させてください。
- 一度有効化させると、３０日間は無料で使用できます。

# 2.Watson Query で、Db2とSnowflakeのデータを仮想化して結合する。
- [こちら](https://qiita.com/Shumpei_Kubo/items/ecd02072e6eb5f84b499)の手順でWatson QueryへDb2とSnowflakeを仮想化し、テーブルを結合してみてください。
- Watson Queryがサポートするデータソースについては、[こちら](https://dataplatform.cloud.ibm.com/docs/content/dvaas/dv_supported_data_sources.html?context=wdp&audience=wdp)を御覧ください。
Linux標準教科書 第3章【基本的なコマンド】についてまとめ

# 記事を書くにあたって
こんにちは。初めての投稿になります。web系エンジニアとして、転職を目指し独学中のIT初学者です。Linuxについて学習中ですので、自分なりに理解したことやわかったことをまとめていこうと思います。間違い等もあるかもしれないので、もしよかったらご指摘いただけると幸いです。


# ファイルとディレクトリ
ファイル・・・word、Excel、textなどのファイルのこと
ディレクトリ・・・私が普段使う呼び方だとフォルダのこと。ファイルを種類ごとに区別する際に有効！！

## ファイルやディレクトリを参照するコマンド(ls)
##### 書式
【 ls [オプション] [ファイル] 】
##### 具体的な使用例
$ ls ・・・現在のディレクトリの「ファイル」と「ディレクトリ」を全て表示する。
$ ls /etc/ ・・・etcディレクトリ内の「ファイル」と「ディレクトリ」を全て表示する。
$ ls *.conf・・・語尾に「.conf」がつく「ファイル」と「ディレクトリ」を全て表示する。
$ ls ???.conf・・・「3文字」＋「.conf」の「ファイル」と「ディレクトリ」を全て表示する。
$ ls -l・・・詳細な情報が出る。
$ ls -a・・・ドットで始まる「隠しファイル」も含めて全て表示される。
$ ls -t・・・更新時刻が新しい順に表示する。

## ファイルのコピー(cp)
##### 書式
【 ls [オプション] コピー元 コピー先 】
##### 具体的な使用例
$ cp /etc/hosts ~・・・「etcディレクトリ」の「hosts」を「~」にコピーする。
$ cp /etc/hosts ~ /hosts.newname・・・「etcディレクトリ」の「hosts」を「~」に「hosts.newname」という名前でコピーする。
$ cp -p hosts.newname hosts.sametime・・・「host.newname」を「hosts.sametime」という名前で「現在のディレクトリ」に「作成日時を維持したまま」コピーする。
###### ディレクトリのコピー
$ cp -r olddir/ newdir/・・・「newdir」の中に「olddir」を作成。ディレクトリのコピーを行う際には「-r」のオプションが必須。

## ファイルの移動(mv)　※名前の変更も可能


###### ファイルやディレクトリの移動
【 mv (ファイルorディレクトリ) (存在するディレクトリ) 】
$ mv hosts.renew test/・・・ファイルの「hosts.renew」を「testディレクトリ」に移動
$ mv directory1 directory2・・・「directory1ディレクトリ」を「directory2ディレクトリ」に移動
###### ファイル名、ディレクトリ名の変更
【 mv (ファイルorディレクトリ) (存在しないディレクトリ) 】
$ mv hosts.A hosts.B・・・ファイル名「hosts.A」を「hosts.B」に変更
$ mv directoryA directoryB・・・ディレクトリ名「directoryA」を「directoryB」に変更 

## ファイルの消去(rm)
##### 書式
【 rm ファイル名 】
##### 具体的な使用例
$ rm hosts.sametime・・・ファイル「host.sametime」の消去
$ rm -r test・・・「testディレクトリ」の消去
※ディレクトリ内の「ファイル」および「ディレクトリ」も全て消去される。

## ディレクトリの操作 (pwd,cd,mkdir)
$ pwd・・・現在のディレクトリの表示
$ cd /user・・・「userディレクトリ」に移動する。
$ cd・・・「ホームディレクトリ」に移動する。
$ mkdir dir1・・・「dir1ディレクトリ」を作成する。
$ mkdir -p dir1/dir2/dir3・・・「dir1」を作り、「dir1」の中に「dir2」を作る。そして、「dir2」の中に「dir3」を作成する。

## ディレクトリの消去(rmdir)
$ rmdir dir1・・・「dir1」を消去する。
※注意：「dir1」の中が空でないと消去できない。
$ rm -r directory/・・・「directoryディレクトリ」を、中の「ファイル」や「ディレクトリ」を含めて全て消去する。

## ファイルの検索(find)
##### 書式
【 find パス -name ファイル名 】
##### 具体的な使用例
$ find /etc/ -name hosts・・・「/etc配下」にある「hosts」という名前のファイルを全て表示する。

## 章末テスト (正しいかどうかはわかりません)
(1) ファイルやディレクトリを参照するコマンドを記述しなさい。
【 ls [オプション] [ファイル] 】

(2) /media/cdrom/の中身を最新更新日順に並べて表示するコマンドを選びなさい。
1. ls -lf /media/cdrom/
2. ls -la /media/cdrom/
3. ls -lt /media/cdrom/
4. ls -l /media/cdrom/
→3. ls -lt /media/cdrom/

(3) 今いるパスを表示するコマンドを記述しなさい。
$ pwd

(4) /home/userフォルダにあるtestというファイルを/root/backupフォルダに移動します。空欄にあてはまるコマンドを書きなさい。
(     ) /home/user/test /root/backup
(     ) /root/backup
→(  mv  ) /home/user/test /root/backup
→(  ls  ) /root/backup

(5) パス/etc の中のファイルで、ファイル名の一部に「resolv」という文字列が含まれるファイルを検索するコマンドを記述しなさい。
$ find /etc/ -name resolv
terraform update後の「Error: Invalid legacy provider address」に対処する

# 1. はじめに

今回terraform version 0.12で作成されたtfstateをメンテした際に遭遇したエラーで、ターミナルの指示通りに`terraform init -migrate-state`、`terraform init -reconfigure`しても動作しませんでした。

エラーでググると「providersの/-/awsをreplaceすれば直るよ」みたいな記事は多かったのですが、自分の場合はtemplateも対象だったので一応書いておきます。

記事の時点ではTerraform v1.1.0です。  


# 2. init出来ない状況確認
```terraform
$ terraform init -reconfigure

│ Error: Invalid legacy provider address
│
│ This configuration or its associated state refers to the unqualified provider "aws". 
│
│ You must complete the Terraform 0.13 upgrade process before upgrading to later       
│ versions.
```

```terraform
$ terraform providers

Providers required by configuration:
.
# ～省略～

Providers required by state:

    provider[registry.terraform.io/-/aws]
    provider[registry.terraform.io/-/template]
```

# 3. terraform state replace-providerで修正する
```terraform
# terraform providersにregistry.terraform.io/-/awsがあったら
$ terraform state replace-provider registry.terraform.io/-/aws registry.terraform.io/hashicorp/aws

# terraform providersにregistry.terraform.io/-/templateがあったら
$ terraform state replace-provider registry.terraform.io/-/template registry.terraform.io/hashicorp/template
```

# 4. 動作確認
```terraform
$ terraform init

Initializing modules...

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Reusing previous version of hashicorp/template from the dependency lock file
- Using previously-installed hashicorp/aws v3.72.0
- Using previously-installed hashicorp/template v2.2.0

# ～省略～

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other        
commands will detect it and remind you to do so if necessary.
```
powershellでレジストリの設定を無限に変更し続けるスクリプトを作った。

#はじめに
Windowsサーバーで作業する際に、「プロキシサーバーを使う」がオンの状態で実施する作業があるのですが、作業中にほかの人にオフにされて煩わしいので作った。結局怖くて使ってない。
きっと誰の役にも立たない。作ってる時も特にハマることはなかった。


#中身
```powershell:proxy.ps1
$regPath = "Registry::HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings"


while($true){
    $ProxyEnable = (Get-Item -Path $regPath).GetValue("ProxyEnable") 

    if($ProxyEnable -eq 0){
        echo $ProxyEnable
        Set-ItemProperty -Path $regPath -name ProxyEnable -Value 1
        
    }else{
        echo $ProxyEnable
    }
    Start-Sleep -s 1
}
```

#おわりに
powershellでレジストリいじったことなかったから勉強にはなりました。
M1 MacのDockerでnativeのarm 64版 MySQL Server 8動くよ

M1 MacのDockerでMySQLを動かすとき、x86 emulator機能を使う記事が多いですが、MySQL 8.0はarm64にも対応しているので`mysql/mysql-server:8.0`を使うと軽く動かせます。

デフォルト設定ではlocalhostからしか接続できないので、`docker-compose.yml`では下記のように`MYSQL_ROOT_HOST`を指定してそれを回避しています。

```docker-compose.yml
  mysql:
    image: mysql/mysql-server:8.0
    environment:
      MYSQL_ROOT_PASSWORD: "passwd"
      MYSQL_ROOT_HOST: '%' # https://zenn.dev/ryo_kawamata/articles/mysql-connect-error
```
M5StackのENV III（環境センサ）を試してみた。

先日ENV IIの後継版、ENV IIIが登場した。
スイッチサイエンスでは旧版在庫がなくなった為、後継版のみ販売となった。
しかし、大体の既存記事がENV II前提ですので、変更点のメモ。

https://www.switch-science.com/catalog/7254/
# ENV IIとENV IIIの違い
- 温湿度センサ：SHT30（共通）
- 気　圧センサ：BMP280→QMP6988に変更
- 動作温度範囲：0~60℃→0~40℃に変更（Operating temperature→Working temperatureに表現も変わってます）

同じモバイル用絶対気圧の気圧センサだが、別物。要注意。
# コード記述上での注意点
※注意※（2022/07/30 追記）
記事制作時、M5StackをArudino IDE + Javaで弄っておりました。他環境・言語の方は適宜読み替えて下さい。

## ライブラリ
以下のGitHubのsrcフォルダ内より、QMP6988.h,QMP6988.cppの2ファイルのみ、または、フォルダ内一式を入手し、""でincludeする。

https://github.com/m5stack/UNIT_ENV
## 新規で書き起こす
QMP6988以外にもSH3Xも一緒にinclude出来るフォルダ内一式入手後に"UNIT_ENV.h"でincludeが簡単でオススメ。

```java:記述例
//ENV_II_III_共通
#include <Adafruit_Sensor.h>
#include "UNIT_ENV.h"
SHT3X sht30;
QMP6988 qmp;
float tmp = 0.00;
float hum = 0.00;
float pressure = 0.00;
```

## 既存(ENV IIでのコード）の書き換えなら
QMP6988.h,QMP6988.cppを入手し、"QMP6988.h"でincludeがオススメ。
（変更前）

### include周辺
```java:ENV_II版
//ENV_II_III_共通
#include <Adafruit_Sensor.h>
#include "SHT3X.h"
SHT3X sht30;
float tmp = 0.00;
float hum = 0.00;

//ENV.II用
#include <Adafruit_BMP280.h>
Adafruit_BMP280 bme;
float pressure = 0.00;
```
↓

```java:ENV_IIIに変更後
//ENV_II_III_共通
#include <Adafruit_Sensor.h>
#include "SHT3X.h"
SHT3X sht30;
float tmp = 0.00;
float hum = 0.00;

//ENV.III用
#include "QMP6988.h"
QMP6988 qmp;
float pressure = 0.00;
```
　
### setup内やloop内の変更点

```java:ENV_II版
void setup(){

 // Setup ENV II Unit SHT30 and BMP280 
  bme.begin();

}

void loop(){

　// Read ENV II Unit SHT30 and BMP280 
  pressure = bme.readPressure()/100;

}
```
↓

```java:ENV_IIIに変更後
void setup(){
　
 //ENV.III QMP9688用
  qmp.init();

}

void loop(){

  //ENV.III QMP6988用
  pressure = qmp.calcPressure()/100;

}
```
レスポンシブデザインの考え方（コーディング）

クライアントの予算は正直ピンきりなので、予算枠に応じてWEB制作するときのブレイクポイントの基準をメモしておきます。

##ブレイクポイント3箇所
区間は以下の通り
・sm：360px ~ 767px
・md：768px ~ 1023px
・lg：1024px ~ 1366px
・xl：1367px ~ 2880px
※360pxより小さい端末についてはviewportで対応（参考記事：[TAKさんの俺流レスポンシブコーディング](https://zenn.dev/tak_dcxi/articles/690caf6e9c4e26)）
※ウルトラワイドまで対応しているとキリがないので最大幅はiMacの2880pxまで。
※2880pxについては自分のやり方なので、別に1920pxまでにしても良い。

###記述方法

``` style.css
/* スマホのCSS記述 */
@media and (min-width: 768px){ /* CSS記述 */ }
@media and (min-width: 1024px){ /* CSS記述 */ }
@media and (min-width: 1367px){ /* CSS記述 */ }
```

##ブレイクポイント1箇所
区間は以下の通り
・sm：360px ~ 767px
・lg：1024px ~ 1366px
※768px ~ 1023pxまではコンテンツ幅767px固定のリキッドデザイン。
※1367px以降はコンテンツ幅1366pxのリキッドデザイン。

###記述方法

``` style.css
.container{
  /* スマホのCSS記述 */
  max-width:767px;
  width: 100%;
  margin: 0 auto;
}
@media and (min-width: 1024px){
  .container{
    max-width:1366px;
  }
}
```

##それでもブレイクポイントが足りない場合
正直TAKさんも[記事](https://zenn.dev/tak_dcxi/articles/690caf6e9c4e26)に書かれていますが、1024px付近は曲者なのは間違いないです。
規定のブレイクポイントだけでは対応できないときは、ディレクターやデザイナーと相談しながら個別にメディアクエリを追加できないか相談しましょう。
ちなみに自分は「よしなに」のパターンが多いので、半分責了でメディアクエリを追加してます。

本当は都度クライアントなどに確認が取れる環境が望ましいですが、会社によってはデザインを再現できるならOKというところもあるとは思うので、前もって上の者に確認しておきましょう。
【Python printデバック】bound method Request.body of <starlette.requests.Request object at >の展開方法

```sample.py
import pprint

pprint.pprint(vars(request))
```

ダンプの意味は[こちら](https://e-words.jp/w/%E3%83%80%E3%83%B3%E3%83%97.html)

https://minus9d.hatenablog.com/entry/2015/08/02/204226
Teamsの参加者レポートのメール列のバグ修正

私の職場ではメンバーの作業拠点の分散や、新型コロナウィルス感染対策のリモート業務原則化などを背景に、各種IT技術の勉強会をTeams会議で開催しており、その「参加者のレポート」により参加状況を集計＆分析しています。
今回の記事はこの集計運用をする中で検知した問題の紹介です。

# Problem

「参加者のレポート」（Meeting Attendance Report）は以下のようなフォーマットになっており、
参加者の一覧部分には「メール」列と「参加者ID（UPN）」列があるのですが：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50571/0e305b51-0ae7-10a5-0e79-526271b350e8.png)

2021年12月13日週までは「メール」列にも「参加者ID（UPN）」列と同じ内容、つまりユーザープリンシパル名が入る状態でした。

# Solution

この「メール」列の内容を元に手っ取り早く正しい「メールアドレス」を得ようとする場合、所属組織のActive DirectoryやOutlookの構成にもよるのかもしれませんが、基本的には `@` 以降を文字列置換することになります。

ところで、詳しい時期はわからないのですが、2021年12月20日週ころからこの不具合が修正され、「メール」列には正しくメールアドレスが入る状態になりました（ [こちらのページ](https://docs.microsoft.com/ja-jp/officeupdates/teams-admin) で紹介されている12月23日のリリースによるものでしょうか・・・）。

このことから、仮に「『メール』列にユーザープリンシパル名が入っている」前提に依存した運用（バッチ処理やPower Automateのワークフローなどを含む）を行っている場合、今後は正しく処理が行えない問題が起き得ます。手順やバッチの実装、ワークフローの修正をご検討ください。

例えば前述の参加状況集計の運用は、まさに「『メール』列にユーザープリンシパル名が入っている」前提に依存した運用となっていました。
この集計作業では、「メール」列の値をキーにして、別途Microsoft Formsで集めたアンケート結果との突き合わせを行う手順があります。Microsoft Formsにも類似の不具合（回答者のメールアドレスとしてユーザープリンシパル名が返される）があるので、12月13日週までは「結果的にうまく行っていた」のですが、20日週からはうまく行かなくなりました。
GithubのWikiをGithubコントリビューションに入れるためにGithubActionsでミラーリングしたかった

知見がある方のご意見いただきたいなぁ、と思いつつ。

## やりたいこと
1. リポジトリAの内容をリポジトリBにミラーリングする
1. リポジトリAの履歴をリポジトリBに引き継ぐ
1. リポジトリCにGithubActionsを作る

できたのは2.まで。

## やったこと
```
git clone リポジトリA
git push --mirror リポジトリB
```

これで2.まではできます。

### 補足
httpsでやっているので気にしていませんでしたが、検証環境では.sshも対応しているので、もしかしたら関係があるかもしれません。

## 問題点
GithubActionsで実行させようとすると、権限周りが問題になります。
何も考えずに

``` GithubAction.yml
- name:
  run: |
    git clone .git
    git config --global user.email "${{ secrets.EMAIL }}"
    git config --global user.name "shimajima-eiji"
    git push --mirror .git
```

とやると、様々な問題にぶち当たります。
GithubActionsに詳しくないのですが、設定周りが足りてないのだろうと思います。

試しに.sshを作ってssh通信したり、GITHUB_TOKENが必要とのことでPATを試したりしたんですが、エラーが変わってもpushに失敗する問題が変わらなかったので諦めました。

### 別解
#### リポジトリAの情報を引き継ぐ必要がない場合
リポジトリAの情報を破棄するのが一番手っ取り早いです。
リポジトリBはあくまでミラーリングするためだけであれば、`git clone --bare .git`とかでいいと思います。

今回は、リポジトリAが.wiki.gitであり、これをGithubコントリビューションとして評価したかったので、これはNGです。

#### GithubActions以外での自動化
運用を考えると毎日実施しなくても良く、思い出した時（話題になった時）に適用すれば良いと思ったので手動で実施することにしました。
毎日実施したいならcronなどを使う手もあります。

## 調べたこと
1. 他のリポジトリの情報をクローンする(`git clone --mirror`)
1. リポジトリをミラーリングする(`git push --mirror`)

大体 `error: failed to push some refs to`とか`fatal: --mirror can't be combined with refspecs`に引っ掛かってます。
IPAって何してるの？

## 勉強前イメージ

要件定義の資料とか出してたイメージ・・・
なにしてるかはしらんけど、なんかまとめてる？

## 調査

### IPA とは

Information-Technology Promotion Agency の略で、
正式には 独立行政法人 情報処理推進機構 になります。
日本のITを技術面や人材面から支えるために2004年に設立されました。
主に、ITの技術開発の支援、IT人材の育成や情報セキリティに関して調査を行い、研究・情報や発信を行います。

人材育成面では、情報処理技術者試験を行ったり学生対象のセキュリティ教育「セキュリティキャンプ」を行ったりしています。

セキュリティ面では、セキュリティセンターや産業セキュリティセンターなどを管理しており、情報処理安全確保支援士試験の実施や
脆弱性データベースの運営(JVN)など行っております。

### 個人的にJVNが気になる・・・・

JVNとは↑で書いてる通り、日本で使用されているソフトウェアの脆弱性関連の情報を提供して対策を行うことを目的としているサイトになります。
Japan Vulnerability Notes の略になります。
2004年からJPCERT コーディネーションセンターとIPAの共同で行っております。

脆弱性が確認されたらIPAに届け出を行い、JVNが対策や情報を公表します。

## 勉強後イメージ

IT系のことを推進していくような国の組織ってことかな。
また、脆弱性とかの届け出先って感じかな。

## 参考

- [IPA](https://www.otsuka-shokai.co.jp/words/ipa.html)
- [IPA 【Information-technology Promotion Agency】 情報処理推進機構](https://e-words.jp/w/IPA.html)
- [脆弱性関連情報の届出受付](https://www.ipa.go.jp/security/vuln/report/)
neosでlogix その47

#概要

niosでlogixやってみる。
アンパックしたLogiXをNeosのワールドに配置して、他の人が訪れて実験できるようにする。
#手順
- devtooltipを装着
- 新規->空のオブジェクトをスポーン
- インスペクターを表示
- logixtooltipを装着
- ノードブラウザを表示
- logixを組む
- devtooltipを装着
- インスペクターを表示
- ☆を使って子スロットを作り、それをlogixという名前に
- logixtooltipを装着
- logixスロットをグラブして、リングメニューからSet Packing Rootを選ぶ
- ノードをクリックして、rでパッキング
- Unpack children & connected nodesで アンパック
- 親のスロット名を変更
- それをグラブして、ctrl+sで、インベントリーに保存
- neosrecを公開

以上。
javascriptでボタン押した時にローディング画像表示(CSS変更するのも面倒くさい人向け)

#はじめに
　完全に後付でCSSとか使わずに(面倒くさいから)、いい感じにhtmlに画像表示するのに、あっちこっち検索したので備忘録的に作成。
　基本的にCSS使ったいい感じのサンプルたくさんあるので、そっち使った方がいいです。
　CSS変更できないけどjavascriptは変更できる環境の人向け(そんな人居るのか？)。

　ローディングのgif画像は各自用意してください。

#ソースコード

javascriptの一番下にでもコピペしてください。
jquery使ってます。

```javascript
$(function() {
  var $btnLoading = $('.btn_loading');
  $btnLoading.on('click' ,function(){
    setTimeout(function(){
        $('body').prepend('<div class="loading" style="display:flex; width:100vw; height:100vh; background-color: rgba(0,0,0,0.6); position:absolute; align-items:center; justify-content:center;"><img src="loading.gif"></div>');
    },100);
  });
});
```

ローディング画像挟みたいボタンに"btn_loading"のクラスを追加。

```html
<input type="submit" value="OK" class="btn_loading">
```

#簡単解説

```html
<div class="ajax-loader" 
         style="display:flex; 
                    width:100vw;
                    height:100vh;
                    background-color:rgba(0,0,0,0.6);
                    position:absolute;
                    align-items:center;
                    justify-content:center;">
<img src="loading.gif">
</div>
```

bodyの一番上にウィンドウ全体透過度60%の黒の重なるdiv要素を追加してます。
あとgif画像を　"display:flex; align-items:center; justify-content:center;"　で上下左右の中央に表示するようにしてます。(これが地味にハマった)
【書評】Ruby初学者がプロを目指す人のためのRuby入門改訂第2版を読んで学んだこと

## はじめに
久しぶりの投稿になります。Tassyです。
この度、「プロを目指すための人のRuby入門 改訂第2版」を読ませていただきました。
https://blog.jnito.com/entry/2021/11/11/082704
まさか、当選するとは！！と言う気持ちと感謝の気持ちでいっぱいです。
## プログラミング歴
- 受託開発企業で1年3ヶ月働いている
- 基本的にPHP（Laravel）で実装することが多い
- RubyはProgateで一通り触ったことがあるくらい
- 実際にRuby（Ruby on Rails）を使ってアプリを開発したことはない<br>
Rubyについてはど素人です。

## 学べたこと
- Rubyの基本構文（ハッシュ、ブロックetc）
- オブジェクト指向プログラミングとは
- テスト駆動開発の実践
- 正規表現について
- クラスについて
- yield、Procについて
- パターンマッチについて

Rubyの基本的な構文を学べるのはもちろんのこと、実際にirbを使って手を動かしながらコードを確認できるので、楽しい。そう、Rubyを書くのは楽しいです。
各章ごとに何を学んでほしいのか書いてあるので、要点を掴みながら読み進めることができます。
## 何がよかったか
Ruby初学者の私でしたが、PHPやJavaScriptは少し書ける程度です。
そのため、他言語との比較を行いながらRubyの基礎を学ぶことができたのは非常に良かったなと思います。
また、この機能はどのように使えるのかというユースケースも提示されているので、イメージがつきやすいです。
聞いたことがあるような単語についても、説明が載ってあり、さらに詳細に学んでいきたい場合のおすすめのサイトや書籍も載っているという、ほんとに痒いところに手が届くし、痒いと気づいてなかったところにも手が届いちゃう本です。
## どんな人に勧めたいか
完全な初学者という方ではなく、Rubyを触って半年くらいの方や、
他言語で開発をしたことがあるけど、Rubyを触ったことがなくて不安だなと思う方に、ぜひ手にとっていただきたいです！
## どう活かしていくか
私自身、現在Railsの保守案件に携わらせてもらっています。
Railsもキャッチアップが足りない状態ですが、この本のおかげでブーストをかけた状態で学んでいくことができそうです。
## 何度も読み返したい
一回深く読むより、何度も何度も読むことでプロに近づいていける本なのかなと思います。
実はまだ理解できていない点がいくつかあるのですが、本を読む際のポイントとして「知のインデックス」を貼るくらいの感覚で読むといいと本書にも書かれていました。
実案件で頭を悩ませた際は、本書を手に取り、何度も読み返していきたいと思います。
## 最後に
本書を送っていただいた@jnchito（伊藤 淳一）様、本当にありがとうございました！
気付けば、Rubyの魅力や書き心地に惚れてしまいました。
僕自身も立派な「Rubyist」となれるよう、Rubyに貢献していきます。
ぜひ、皆様も手にとっていただけますと幸いです。
SSL証明書チートシート

## 証明書に対しての操作

### 証明書の有効性を確認
1つのファイル内に中間証明書などを含む複数の証明書が含まれている場合。
同じファイル名が二回使われているが、これは証明書チェーンと検証したい証明書が同じファイルに存在するため。

```

openssl verify -CAfile cert.pem cert.pem
```

### 証明書の有効期限を表示

```
openssl x509 -enddate -noout -in cert.pem
```


### 証明書から鍵のビット長を表示

```
openssl x509 -in cert.pem -text -noout|grep bit
```


### 証明書の詳細情報を表示

```
openssl x509 -text -noout -in cert.pem
```


### 証明書から公開鍵を出力

```
openssl x509 -pubkey -noout -in cert.pem > pubkey.pem
```


## 秘密鍵に対しての操作

### 秘密鍵の作成

乱数ファイルの生成

```
head /dev/random > rand.dat
```


opensslのインストール  
macではOpenSSLから派生したLibreSSLというものが標準で入っているが、オプション体系が微妙に異なる為、普通のopensslをインストールする。

```
brew install openssl
# パスを通す（使っているシェルに応じて適宜変更）
echo 'export PATH="/usr/local/opt/openssl/bin:$PATH"' >> ~/.bash_profile
```


秘密鍵の生成

```
openssl genrsa -aes256 -rand rand.dat 2048 > key_with_pass.pem
```

パスフレーズを求められるのて適当に入力する。

`-aes256` は暗号化アルゴリズム。必要に応じて `-des3` などに適宜変更する。  
`2048` は鍵のビット長。必要に応じて適宜変更する。


鍵のパスワード解除

```
openssl rsa -in key_with_pass.pem -out key.pem
```

パスフレーズを求められるので上で入力したのと同じものを入力する。


### 秘密鍵の詳細情報を表示

```
openssl rsa -text -noout -in key.pem
```


### 秘密鍵から公開鍵を出力

```
openssl rsa -in key.pem -pubout > pubkey.pem
```


### 秘密鍵から鍵のビット長を表示

```
openssl rsa -text -noout -in key.pem | grep bit
```


## CSRに対しての操作

### CSRの作成

```
openssl req -new -key key.pem -out csr.txt
```

### CSRの詳細情報を出力

```
openssl req -in csr.txt -text -noout
```


### CSRから公開鍵を出力

```
openssl req -in csr.txt -noout -pubkey > pubkey.pem
```
スクラムのキャパシティーを確認しようとする時は危ない気がする

# 自分のチームのキャパシティ知ってますか？

知らない方や特に意識していない方は
きっと上手く回っているのではないでしょうか。
いくつかスプリントを実施した後にキャパシティを確認しようと言い出すのは
要注意だと思います。

# なんでキャパシティーを確認しようとしちゃダメなの？
各スクラムイベントのタイムボックスを厳格にするのは大賛成です。

でも、キャパシティーを確認しようとしている時って、
一部の優秀な開発者やPOから
```もっとできたんじゃない？
当初の計画だともうちょっとできると思ってた。```
って思われていて、計画ありきになっちゃってると思います。
チームの信頼感や心理的安全性も崩れるきっかけになるのでは。

# 代わりにこうすればいいと思うよ
モブワークやペアプロなどでチーム全体の力を向上していくことで
キャパシティーなんて気にしないぐらい、インクリメントを出せると思います。
どうしてもPOやマネージャからキャパシティーを数字で出せ！って言われたら
スクラムマスターがサクッと集計すればいいんじゃないかな
WordPress GraphQLでページネーションを返却する

WordPressでプラグイン[WP GraphQL](https://www.wpgraphql.com/)をインストールしてGraphQLに対応する。

# ページネーションに対応する
[valu-digital/wp-graphql-offset-pagination](https://github.com/valu-digital/wp-graphql-offset-pagination)を導入する。

WordPressのpluginsディレクトリにプラグインを`git clone`する。

```
cd wp-content/plugins
git clone --branch stable https://github.com/valu-digital/wp-graphql-offset-pagination.git
```

WordPressの管理画面で`WPGraphQL Offset Pagination`を有効化する。
[Usage](https://github.com/valu-digital/wp-graphql-offset-pagination#usage)を参考にGraphiQL IDEでテストする。

返却されれば完了！
Google Earth Engineでトンガ噴火


#概要

Google Earth Engineで、トンガ噴火を探してみた。

#写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/bfe38644-2071-107b-fea6-ed21a85c2630.png)


#サンプルコード

```
var collection = ee.ImageCollection('COPERNICUS/S5P/OFFL/L3_AER_AI')
  .select('absorbing_aerosol_index')
  .filterDate('2022-01-13', '2022-01-15');

var band_viz = {
  min: -1,
  max: 2.0,
  palette: ['black', 'blue', 'purple', 'cyan', 'green', 'yellow', 'red']
};

Map.addLayer(collection.mean(), band_viz, 'S5P Aerosol');
Map.setCenter(-175.382, -20.536, 8);


```


以上。
GCE + Ubuntu 20.14LTS + MATE + XRDPでリモートデスクトップ環境を作る

## GCE インスタンス作成

webコンソールで。↓は記録のために`同等のコマンドライン`をメモ。

```bash
gcloud compute instances create rdp \
--project=myproj \
--zone=asia-northeast1-b \
--machine-type=e2-medium \
--network-interface=network-tier=PREMIUM,subnet=default \
--can-ip-forward \
--no-restart-on-failure \
--maintenance-policy=TERMINATE \
--preemptible \
--service-account=xxx \
--scopes=https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/servicecontrol,https://www.googleapis.com/auth/service.management.readonly,https://www.googleapis.com/auth/trace.append\
--enable-display-device \
--create-disk=auto-delete=yes,boot=yes,device-name=rdp,image=projects/ubuntu-os-cloud/global/images/ubuntu-2004-focal-v20220110,mode=rw,size=10,type=projects/myproj /zones/asia-northeast1-b/diskTypes/pd-balanced \
--no-shielded-secure-boot \
--shielded-vtpm \
--shielded-integrity-monitoring \
--reservation-affinity=any
```

## いろいろインストール
```
apt update
apt upgrade 
apt install mate-desktop-environment
apt install lightdm lightdm-gtk-greeter
 -> lightGDBを選択
reboot 
apt install xrdp
reboot
```

## エラー
startx でエラー

```
(==) Log file: "/var/log/Xorg.0.log", Time: Mon Jan 17 02:26:03 2022
(==) Using system config directory "/usr/share/X11/xorg.conf.d"
(EE) 
Fatal server error:
(EE) AddScreen/ScreenInit failed for driver 0
(EE) 
(EE) 
Please consult the The X.Org Foundation support 
         at http://wiki.x.org
 for help. 
(EE) Please also check the log file at "/var/log/Xorg.0.log" for additional information.
(EE) 
(EE) Server terminated with error (1). Closing log file.
```

```/var/log/Xorg.0.log
[  1867.739] (EE) FBDEV(0): FBIOPUT_VSCREENINFO succeeded but modified mode
[  1867.739] (EE) FBDEV(0): mode initialization failed
[  1867.739] (EE) 
Fatal server error:
[  1867.739] (EE) AddScreen/ScreenInit failed for driver 0
```

## 解決

/etc/X11/xorg.conf がなかったのが悪かったようなので下記で解決。
諸々入れる前にX環境を入れておけば問題なかったのかもしれない。
設定ファイルがないならないと言ってほしい。

```
# ln -s /etc/X11/xrdp/xorg.conf /etc/X11/xorg.conf
```
tweepy4.0以降のリアルタイムのストリーム取得

# はじめに
自分への備忘録として書きます。

tweepy 4.0以降でStreamListenerがStreamにマージされ、過去のサンプルコードが動かなくなったので、書き換えました。

# 環境
Python　3.10.1
tweepy　4.4.0

# 機能
自分のタイムラインからリアルタイムにツイートを取得して、"検索文"が含まれていた場合のみコンソールに表示する
#tweepy 4.0以降に対応したプログラム
```python:test.py
import tweepy
import settings 

CK = settings.CONSUMER_KEY
CS = settings.CONSUMER_SECRET
AK = settings.ACCESS_TOKEN
AS = settings.ACCESS_TOKEN_SECRET
word = "検索文"
 
auth = tweepy.OAuthHandler(CK, CS)
auth.set_access_token(AK, AS)
api = tweepy.API(auth)
 
print('実行中')

class IDPrinter(tweepy.Stream):
    def on_status(self, status):
        print(status.id)
        print(status.user.screen_name)
        print(status.user.name)
        print(status.text)

printer = IDPrinter(CK,CS,AK,AS)
printer.filter(track=[word])
printer.sample()
```
tweepy 3.6頃までは以下のコードの書き方が公式サンプルとして紹介されていましたが、
4.0でStreamListenerがStreamにマージされたので、そのままコピーでは使えなくなりました。
そのため、上記ソースのように書き換えたところ動くようになりました。

```python:old.py
class MyStreamListener(tweepy.StreamListener):

    def on_status(self, status):
        print(status.text)

myStreamListener = MyStreamListener()
myStream = tweepy.Stream(auth = api.auth, listener=myStreamListener)
```
3.6までのやり方
1.StreamListener を継承するクラスを作成する
2.そのクラスを使用して、 Stream オブジェクトを作成する
3.Stream を使用してTwitter APIに接続します
【シンボルについて】「:hoge」だったり「 hoge:」だったり【ややこしいから整理した】


```
# 普通のハッシュ　　　（ハッシュの値の呼び方は、　　hash[“key1”]　）	
hash = {“key1” => value1, “key2” => value2}	

# 前置コロンを使う　　　　（ハッシュの値の呼び方は、　hash[:key1]　）	
hash = {:key1 => value1, :key2 => value2}	

# 後置コロンを使う	　（ハッシュの値の呼び方は、　hash[:key1]　）	
hash = {key1: value1, key2: value2}	
```

## 普通のハッシュ
キー名を文字列で指定して、「=>」でつないでキーに対応する値を記載します。
ハッシュの値を呼ぶ時は、キー名の文字列を指定します。


## 前置コロン（シンボル）を使う
まず、コロンをキー名の前に書く前置コロンは、シンボルを表します。

シンボルとは、Rubyのオブジェクトのひとつの種類です。文字列の代わりに使うこともできますが、必ずしも文字列と同じ挙動をするわけではなく別物です。

ハッシュでは、キー名指定の部分をシンボルで置き換えることができます。「“key1”」の部分が「:key1」となります。文字列ではなくシンボルなので、クォーテーションはつけません。キー名に対応する値の記載方法は前項のものと同じで「=>」でつないで指定します。

ハッシュの値を呼ぶ時は、シンボルで指定します。

## 後置コロンを使う（シンボル指定で=>を省略）
コロンをキー名の後ろに書く後置コロンは、キー名をシンボルにして=>を省略できる書き方です。

「:key1 => 」という部分が、「key1: 」に置き換えられます。

ハッシュではこの書き方が一般的なようです。

この場合も、シンボルでキー名を指定していることには変わりないので、ハッシュの値を呼ぶ時はシンボルで指定します。

>引用元
https://setechlog.com/ruby-hash-colon/
【Django】SNS app作成手順02-サインアップページ作成2

—————signup.htmlを編集----------

使うなら、その後base.htmlの作成。
継承する内容を記載する。

user登録・重複登録防止処理を記載する。

----------Base.html—————

<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- Bootstrap CSS -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">

    <title>Hello, world!</title>
  </head>

  {%block header%}
  {% endblock header%}

  {% block content %}
  {% endblock content %}

</html>

—————signup.html—————

{% extends 'base.html' %}

{%block content%}
<body class="text-center">
    {{error}}
    
    <main class="form-signin">
      <form method='POST'>{% csrf_token %}
        <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
        <div class="form-floating">
          <input type="text" class="form-control" id="floatingInput" name='username' placeholder="name@example.com">
          <label for="floatingInput">username</label>
        </div>
        <div class="form-floating">
          <input type="password" class="form-control" id="floatingPassword" name='password' placeholder="Password">
          <label for="floatingPassword">Password</label>
        </div>
        <button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
        <p class="mt-5 mb-3 text-muted">&copy; 2017–2021</p>
      </form>
    </main>
    
    
        
</body>
{%endblock content%}


----------Views.py----------

from sqlite3 import IntegrityError
from django.shortcuts import render
from django.contrib.auth.models import User
from django.db import IntegrityError

# Create your views here.


def signupfanc(request):
    object_list = User.objects.all()
    print(object_list)
    
    if request.method == 'POST':
        username = request.POST['username']
        password = request.POST['password']
        
        try:
            user = User.objects.create_user(username,'',password)
        except IntegrityError:
            return render(request,'signup.html',{'error':'このユーザーはすでに登録済みです。'})
    else:
        print('not POST')
        
    return render(request,'signup.html',{})
各レシピサイトの人気レシピを表示するBOTを作ってみた。

##はじめに
皆さん、今日の晩ごはんは何にしますか？
我が家は基本自炊なのですが、計画性が無いので、大抵帰宅途中にスーパーに寄って考えながら買いものをしています。
買い物中に毎回「今日の晩御飯どうする？」と聞かれるのですが、いい加減鬱陶しいので何も考えずに間単にレシピを探せる方法を考えました。

今回は、Node-Redなるノンコ―ディングのツールに触れる機会がありましたので、
この Nod-Red と LINEBOT を連携して晩ごはんのレシピをチャット形式で回答してくれるBOTを作成してみます。
##使ったもの
・Heroku（レンタルサーバー）
・Node-Red（ノンコ―ディング開発ツール）
・Node-Red に Line用のAPIを追加するパレット（node-red-contrib-line-messaging-api）
・Line-MessegingAPI（LineのBotを作るサービス）

##できた上がったもの
LINEのリッチメニューから作りたいカテゴリを選択すると、そのカテゴリ毎に異なるRSSを読みに行って、日替わりレシピを表示します。
![LINE動作.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2471853/a5a292b7-a090-c5ac-dbeb-ece07caceedf.png)

※以下フロー
![フロー(コメント付き）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2471853/5af33108-c6de-3e22-021b-a14875fc5d7e.png)

##開発中にハマったこと
###・LINEからの入力文字列の揺らぎ対策
入力文字列の揺らぎによるswitchノードの不発が起きるため、LINEリッチメニューを利用して入力文字を固定しました。
※「晩ごはん」だと動作し、「晩御飯」だとエラー処理のケースに進む。
###・APIから取得してきた情報データの形式問題
今回、RSS配信用のxmlを利用してみたのですが、うまくJSON形式に変換されないようで、
payload全文が単一の文字列みたいになってしまいました。
このままでは、必要なオブジェクトだけ抜き出すことができない為、少し調べてみたところ、
Node-Red上にxmlをJSON形式に変換してくれるノードがあったので、そちらを利用し解決しました
###・出力文字列がきれいにならない問題
RSSからデータ引っ張ってきているので、HTMLのコードみたいな感じでオブジェクトに格納されてしまい、payload出力するとHTMLのコードがそのまま出力される結果に…！
今回は入力から必要なデータを抜き出すために、Split処理を噛ませて要素分解して解決しました。
###・Splitの仕様がよく分からない
通常、Splitって出力を配列にしてくれる言語が主流だと思いますが、どうもそのままpayloadに連続したデータとして格納されるらしい。
試行錯誤した結果、Split処理で要素分解→Join処理で配列化→changeで、必要な情報だけ抜き出しといった強引な手法で解決しました。

##今後の展望
・ リッチメニューをリッチにする。
・ 接続するRSSの種類を増やす。
・ リッチメニュー自体に各サイトのレシピを載せる。

##最後に
実際に友達登録してみて利用してもらったのですが…。
「考えなくていいのは楽だけど、材料集めと調理工程が大変。」
とのコメント。まだまだ実用化の道は長そうです。
【Gorm✕firestoreで構造体の大文字が変換されない対処法】

Gormからfirestoreにデータを保存する際、タグをつけなければ構造体の大文字のままキーが設定されてしまったので、対処法を記述する。

単純に、`firestore:"name"` のようなタグを構造体に付与すればいい。

```json
type User struct {
	Name                 string  `json:"name" firestore:"name"`
	Email                string  `json:"email" firestore:"email"`
}
```

ドキュメントしっかり読もう。

https://pkg.go.dev/cloud.google.com/go/firestore#example-DocumentRef.Create-Struct
mac標準のPython2.7から3系にアップデートしようとして詰まった

##経緯
mac標準のPython2.7を3系にアップデートしようとして詰まった話。

Python2系から3系へアップデートしようとすると
brewのアップデートが必要に。。。
そうするとxcodeのアップデートが必要に。。。
さらにそうするとmacOSのアップデートが必要に。。。

macOS,xcode,brewのアップデートを終え、やっとのことでPythonをアップデート出来るぞと思ったところで詰まりました。

Pythonのアップデートはたくさん参考になる記事様がありますのでそちらをご覧ください。

##環境
macOS Monterey(12.1)

##エラー内容

順にアップデートを終え、インストールしようとすると

```
Inspect or clean up the working tree at
```

このようなエラーが出ました。
これで検索をかけて出てきた情報から、
brewのライブラリをインストールし環境変数を設定したり、、、

```
$ brew install zlib bzip2

$ export LDFLAGS="-L/usr/local/opt/bzip2/lib -L/usr/local/opt/zlib/lib"
$ export CPPFLAGS="-I/usr/local/opt/bzip2/include -I/usr/local/opt/zlib/include"
```

xcodeの設定をしたり、、、

```
$ sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
$ sudo xcodebuild -runFirstLaunch
```

これらでpyenvを使ったインストールを試みたが失敗。
もう一回エラー分と向き合ってみると

```
Inspect or clean up the working tree at

------------(中略)---------------

No module named '_scproxy'
```

下の方にこんな感じの文章があったので、それで検索
https://github.com/pyenv/pyenv/issues/1107
こちらを参考に `include_old` を作ってそこに退避させる

```
$ sudo mv /usr/local/include /usr/local/include_old
$ sudo mkdir /usr/local/include
$ sudo chown $YOU_USERNAME:admin /usr/local/include
```

これでPython3系をインストールできるようになりました！
Pythonのバージョンを変更する処理をして、無事使えることも確認できました。

##これでも解決できたかも
brew doctorをすると

```
Warning: Unbrewed header files were found in /usr/local/include.
If you didn't put them there on purpose they could cause problems when
building Homebrew formulae, and may need to be deleted.
```

/usr/local/includeを削除することで解決が出来そうでした。
今回は、恐かったので一旦退避する策にいたしました。
praat + シェルスクリプトで複数ファイルまとめて処理&グラフ保存

# あらすじ

訳あって、praat を用いてある音声ファイルのスペクトル解析をする必要が出たときのこと。

> えーと、Open から Read from file で音声ファイル選んでと･･･、Analyse spectrum から To Spectrum で･･･、ほんでこいつを Draw してやればいいわけだ。
あとは描画されたのを PNG に保存と。
さて、こいつをあと 70個近くある音声ファイルに･･･。
**死んでしまうな、これ。**

何か良い手は無いものかと調べてみた。
ターミナル上で `praat --help`、すると･･･

```
--run        regard the command line as a script to run, with its arguments
```

お？スクリプト組んで何かできる臭いぞ？

# そうだ、自動化、しよう

と、いうわけで、praat スクリプトを使って先程の退屈な作業を簡単に片付けてみることに。
こちらの記事を一部参考にさせていただきました。

https://qiita.com/Syuparn/items/186b916054fbba87a3f1

私自身シェルスクリプトに慣れているので、praat script の方は最低限にして、ループとかはシェルスクリプト側で処理してしまうことにします。
単に一定の作業をスクリプトに起こすのは、さして難しくはありません。(後述の GUI 作業履歴スクリプト取得機能のおかげで。)

> praat で完結せず sh シェルを利用しているため、(Linux や Mac では問題無いですが) Windows 環境では動作させるのに sh 環境を導入する必要があります。
慣れた言語で手っ取り早く仕事を片付けたかったので、今回はこの手にしました。

# praat script

まずは praat script。
こちらが行う動作は、「1つの音声ファイルのスペクトル解析グラフを出力する」だけです。

1からスクリプトを書いても良いのですが、ここでは praat の GUI 作業履歴からスクリプトを作る機能を使うことにします。
praat 起動後、最初のあらすじで書いたような作業を行った後に、`Praat Objects` ウィンドウから `Praat -> New Praat Script` を選択。

![praat-objects.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/72d3a256-b6d5-0498-45c8-9238f3c5c4b0.png)

新しく出てきたウィンドウ `untitled script` で `Edit -> Paste history` を選択します。

![untitled-script.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540894/cb52ccc7-9f54-bdc0-ed98-8cc5f4ccff89.png)

さてさて、得られたスクリプトは･･･。

```
Read from file: "/home/akuad/audio.wav"
To Spectrum: "yes"
Draw (log freq): 10, 10000, 0, 0, "yes"
Save as 300-dpi PNG file: "/home/akuad/plot.png"
```

ふむ、1行目の所に入力ファイル、4行目の所に出力ファイルを書くようにしてやればいいわけだ。

あとは、入力ファイル、出力ファイルをコマンドライン引数から指定できるように変えてやるだけです。
絶対パスを指定するため (相対パスでも良いのかも？)、入出力ファイルのディテクトリの指定も受け取ります。
`form` 構文から引数を受け取ることができます。(GUI で praat script を読み込んで実行したとき、form 入力のダイアログが出てきます。)

これをこうしてああして･･･こうじゃ。

```spect.praat
form
  text fdir
  text fnamein
  text fnameout
endform

Read from file: fdir$ + "/" + fnamein$
To Spectrum: "yes"
Draw (log freq): 10, 20000, 0, 0, "yes"
Save as 300-dpi PNG file: fdir$ + "/" + fnameout$
```

変数を入れるには `変数名$` と。
文字列を結合するには `+` でつなげれば良いようです。

このスクリプトを使うには、端末上で以下のように実行します。

```sh
./praat --run spect.praat "`pwd`" "in.wav" "plot.png"
```

# シェルスクリプト

ここまで来たら、あとは上で作ったスクリプトをこちらでループで回してやるだけです。
(praat の実行ファイルをパスで指定、またはパスの通った所に配置する必要があります。)

```spect.sh
for f in data/*.wav; do
  ./praat --run spect.praat "`pwd`" "$f" "${f%.*}.png"
done
```

`f=sound.wav` のとき、`${f%.*}` -> `sound`
同じく、`${f%.*}.png` -> `sound.png`

# 実行

上記の praat script とシェルスクリプトを同じディレクトリ上に配置。
同ディレクトリ上に `data` ディレクトリを作って、解析する音声ファイルをその中に配置。

```
data/
  |-audio01.wav
  |-audio02.wav
  |-audio03.wav

praat (praat 実行ファイル)
spect.praat
spect.sh
```

そしてシェルスクリプトを実行します。

```sh
chmod +x spect.sh
./spect.sh
```

ね？簡単でしょう？
【メモ】WSL2でDドライブ以降がマウントされなくなったときの対処

最近Windowsを起動した際にVSCodeを開くと，最近使用したフォルダのWSLのDドライブが開かれるのですが，フォルダのみ生成されており，中身が空になっております．
毎度WSLの再起動を行っていたのですがDockerDesktopが都度止まるのが面倒くさかったのでちゃんと調べてみました．(正しく自動マウントしてくれるような設定調べろって話ですが)

マウントされてないので`mount`すればいいみたいです．xを任意のドライブのアルファベットに書き換えてください．

```bash
sudo mount -t drvfs X: /mnt/x
```

これだけだと寂しいので少しだけ解説を付け加えておきます．
まずmountはrootしか実行できないので`sudo`が必要です．
オプションの`-t`はマウントするファイルシステムを指定します．WSLで使用するファイルに`drvfs`と`volfs`があるようです．
`volfs`はWSL 内部を，`drvfs`はWSL 外部を管理するファイルシステムとなっています．今回はWSL外のWindowsのフォルダであるため`drvfs`ですね．
後はマウント元，マウント先を指定して終わりです．

## 参考
https://cpoint-lab.co.jp/article/201904/9153/

https://sig9.hatenablog.com/entry/2020/02/19/000000
キーボードの表示状態を監視する方法

#はじめに
たまーにキーボードの表示有無によって、UIを制御したいケースがあったりしますよね。
そういった場合に使用できる実装を残しておこうと思います。

#表示状態を監視
キーボードの表示状態を監視するには、以下の実装を行うことで可能です。

``` fragment.kt
binding.root.addOnLayoutChangeListener { view, _, _, _, _, _, _, _, _ ->
    val r = Rect()
    view.getWindowVisibleDisplayFrame(r)

    val location = IntArray(2)
    view.getLocationOnScreen(location)

    val screenHeight = view.rootView.height

    // 変化の差分を算出
    val heightDiff = screenHeight - r.height() - location[1]

    // 画面縦幅の差分が想定している高さより大きい場合はキーボードが表示されたと判断する
    val isShowKeyboard = heightDiff > screenHeight * KEYBOARD_MIN_HEIGHT_RATIO
}

companion object {
    private const val KEYBOARD_MIN_HEIGHT_RATIO = 0.15
}
```
以上になります。
ちなみに余談ですが、GlobalLayoutListenerで監視することも可能ですが、[こちら](https://antonioleiva.com/kotlin-ongloballayoutlistener/)にあるように色々問題を抱えているようで、画面破棄されるまで使用し続けるような実装はメモリーリークに繋がるのでおすすめ出来ません。

#さいごに
意外とこういった実装に頭を悩まされたりしますよね。
なるべく未来の自分が楽をできるよう、今後もどんどん投資していこうと思います。
rails g modelを実行するとどうなるか？そしてその後にやるべきこととは？

## rails g modelをすると

①db/migrateフォルダに新しくマイグレーションが作成されます。
②新しく作られたマイグレーションファイルの中身をざっと確認する。
③以下のコマンドでデータベースへと反映させる。

```
rails db:migrate

#rails g modelでモデルを作成したあとは、このコマンドを実行してデータベースにちゃんと反映させること！
```

マイグレーションファイルとは？
マイグレーションファイルは、データベースを生成する際の設計図になるものです。 また、マイグレーションファイルを実行することで、記述した内容に基づいたデータテーブルが生成されます。

>引用元：
「マイグレーションファイルとは？」より一部抜粋
https://qiita.com/jackie0922youhei/items/09a7b081e40506f07358#:~:text=%E3%83%9E%E3%82%A4%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%A8%E3%81%AF,%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%8C%E7%94%9F%E6%88%90%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82
[AWS KMS] IAMによるKMSのアクセス制御を一部有効にする


## Identity base policy の基本

AWS公式ドキュメント『[AWS アカウント へのアクセスを許可し、IAM ポリシーを有効にする](https://docs.aws.amazon.com/ja_jp/kms/latest/developerguide/key-policies.html#key-policy-default-allow-root-enable-iam)』に記載されている通りだが。

IAMによるKMSのアクセス制御（identity base policy）を有効にするには、キーポリシーに以下の __ような__ ポリシーを含める必要がある。

```json
{
  "Sid": "Enable IAM policies",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::111122223333:root"
   },
  "Action": "kms:*",
  "Resource": "*"
  }
}
```

このキーポリシーでは、KMSの全アクセス権がIAMポリシーで制御できるようになる。


# 一部のアクセス権の制御をIAMポリシーに渡す

公式ドキュメントからは読み取りづらいのだが、全アクセス権ではなく一部のアクセス権をIAMに渡すこともできる。
下記のキーポリシーのように `Action` を限定すれば、そのアクセス権だけがIAMで制御できるようになる。

```diff_json
{
  "Sid": "Enable IAM policies",
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::111122223333:root"
   },
-  "Action": "kms:*",
+  "Action": "kms:Get*",
  "Resource": "*"
  }
}
```
Rails7をちょっと試す（Bootstrap編）

## はじめに

Rails 7.0.0 では、[Bootstrap](https://getbootstrap.com/) が簡単に利用できるようになっています。
今回は、Bootstrap を試してみたいと思います。

Bootstrap を使うためには、npm と yarn が必要です。

以下のバージョンで確認しました。

- Ruby 3.0.2
- Rails 7.0.0
- npm 8.3.0
- yarn 1.22.17

## Rails プロジェクトを作る

新しくRailsプロジェクトを作ります。このとき `--css` を指定します。
(データベースオプションを指定しているのに深い意味はありません。)

```shell
mkdir rails_sandbox
cd rails_sandbox
rails new . --database=postgresql --css=bootstrap
```

## scaffold を実行する

scaffold を使って簡単なCRUDを作ります。 `Bootstrap` の確認をしたいだけなので適当です。

```shell
bin/rails g scaffold User name
```

## DBを作成し migration を実行する

```
bin/rails db:create db:migrate
```

## Rails を実行する

動作を確認するため、Rails を実行します。 ~~`Procfile.dev` を指定して `foreman`~~ `bin/dev` を実行します。

```shell
bin/dev
```

## ブラウザからアクセスする

`http://localhost:3000/users` にアクセスします。

## Bootstrap が使えることを確認する

`Bootstrap` が使えることを確認するため、 `app/views/users/index.html.erb` を修正します。

`New User` のリンクをボタンっぽくしてみます。

```erb:app/views/users/index.html.erb
<p style="color: green"><%= notice %></p>

<h1>Users</h1>

<div id="users">
  <% @users.each do |user| %>
    <%= render user %>
    <p>
      <%= link_to "Show this user", user %>
    </p>
  <% end %>
</div>

<%= link_to "New user", new_user_path, {class: "btn btn-primary mx-1"} %>
```

ブラウザの画面は以下のようになります。
![bootstrap1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209941/0d884672-998f-5622-9e97-426cad695929.png)


## Bootstrap のバージョン

`package.json` を調べてみたところ、Bootstrap のバージョンは 5.1.3 になってました。

```shell
$ grep bootstrap package.json
    "bootstrap": "^5.1.3",
    "build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss ./app/assets/builds/application.css --no-source-map --load-path=node_modules"
```

## まとめ

今回は、Bootstrap が使えることを確認しました。
試したコードは、 https://github.com/suketa/rails_sandbox/tree/try_rails7_bootstrap になります。

## 参考情報

- [Bootstrap Buttons](https://getbootstrap.com/docs/5.0/components/buttons/)
はじめてのGoogle AIY Voice Kit③【ケーブル接続編】

# はじめに
前回のラズパイOSイメージ書き込み編の記事は[こちら](https://qiita.com/yuzuki6v6/items/4864d004264e7531246c)から:information_desk_person:
今回はケーブル接続編です。

# 接続方法
AIY Voice Kitの接続方法は、以下の二種類があります。

* 専用のAndroidアプリ「Google AIY Projects」を使用する
* モニター、マウス、およびキーボードを使用

今回は専用のアプリは使用しない方向で実施します。

# ケーブルを接続
## ものを用意
キットにはマイクロUSBケーブルが入っていますが、そちらは使用しませんでした。
今回は専用のアプリは使用せず、モニター画面に映し、キーボードとマウスに繋げたかったため、その分色々と用意する必要がありました。


###### 用意したもの
:white_check_mark: マイクロHDMI変換アダプタ（モニター接続用）
:white_check_mark: スイッチ付きマイクロUSBケーブル（電源用）
:white_check_mark: USBハブ（マウス・キーボード接続用）

:exclamation: マイクロUSBケーブルは、ACアダプタをコンセントに差し込んで接続すると
AIY Voice Kit本体の電源が入ってしまうため、そうならないようにスイッチ付きのケーブルを使用しています

## 接続！
①用意したものをAIY Voice Kit本体の各ポートに差し込みます。

* HDMIポート：マイクロHDMI変換アダプタ
* Dataポート：USBハブ
* Powerポート：スイッチ付きマイクロUSBケーブル

※マイクロUSBケーブルは、ACアダプタをコンセントに差し込みます

②マイクロHDMI変換アダプタにモニターのHDMIケーブルを差し込み、USBハブにマウスとキーボードを接続します。
![IMG_0663.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/921847/506f2c75-e85d-dcfa-5566-f27ee845402b.jpeg)
![IMG_0664.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/921847/1dd6d77c-2ad4-1304-8308-108976f46a73.jpeg)

③マイクロUSBケーブルのスイッチで電源をONにします。

④ラズベリーパイが起動しました！
![IMG_0662.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/921847/636821a8-0307-8526-2bb7-555fecd47fe6.jpeg)

#所感
モニター画面に映しキーボードとマウスに繋げるため、そもそも何が必要なのかが分からず苦労しましたが、その分接続できた時の喜びはひとしおでした。
ラズパイセットアップ編に続きます！
<br/>
Gitの基本操作その１

##1.はじめに

今回から数回にわたって、Gitでコミットをするところまでを説明していきます。
予定としては、  
１．ディレクトリの作成
２．リポジトリの作成
３．ファイルの作成・編集
４．ファイルをステージに入れる
５．ステージの内容をコミット  
という流れになります。今回は「1.ディレクトリの作成」について書いていきます。

##2.ディレクトリの作成

Gitを起動したら、任意の場所に移動してディレクトリを作成します。
ディレクトリを作成するコードは、以下の通りです。

```
$ mkdir git001
```
このコードを入力することで、ディレクトリが作成できます。

##3.ディレクトリ間の移動

先ほど、「任意の場所に移動してディレクトリを作成します」と説明しましたが、この記事を読んでいる方の中には、「ディレクトリの移動ってどうすればいいの？」と疑問に思った読者もいると思いましたので、ここでディレクトリ間を移動するコマンドを以下にいくつか紹介させていただきます。

```:現在のユーザーのホームディレクトリへ移動①
$ cd 
```

```:現在のユーザーのホームディレクトリへ移動②
$ cd ~
```

```:現在の階層にある任意のディレクトリへ移動
$ cd [ディレクトリ名(ex:git001)]
```

```:１つ上の階層（ディレクトリ）へ移動
$ cd ..
```

##4.次回

今回は「１．ディレクトリの作成」を説明したので次回は「２．リポジトリの作成」以降を説明していく予定です。
慣れてきましたら１回あたりの内容も増やしていく予定ですので、今後ともよろしくお願いします。
Ubuntu-Server 20.04でcron jobが動かない

# はじめに

ここ数年、サーバの管理というかサービス群はコンテナ化して運用するようにしている。
なのでサーバも基本的にDockerが動けばよい設定にしている。

Dockerを動かすOSは別に何でも良いのだけどUbuntu-Serverが構成がシンプルなので好んで使っており、最近はほぼほぼこれしか使わない。
OSのインストール時にもSSHサーバを入れるだけで後は何も入れない。しかしシンプルが故に手を加えなければならないところもある。
今回の議題もそれに関連しているかと思う(Ubuntu-Desktopとかなら問題にならないんじゃないのかな)。

さて、本題。
Linuxサーバをお守りしていると必然的に`cron job`(以下`cron`)の世話になる。
ローカルのコマンドから実行できるのに`cron`になると動かないというのは「`cron`あるある」でこれまでも嫌と言うほど体験してきた。
この場合は環境変数を読み込んでいないとかいろいろあるのでそこは他の情報を参照していただくとして。今回

「`Ubuntu-Server 18.04`で動く仕組みをそのまま`20.04`に移植したら動かない」

という現象に少し悩まされたのでその解消方法を述べる。

# やったこと

私の場合`cron`の設定はユーザ単位で行うのを基本としており、システムのバックアップなど`root`権限が必要な場合も同様である。
しかし設定ファイルが`/etc/`にまとまっているほうが管理しやすいので近年は`/etc/cron.d`を使うことが多い。

`/etc/cron.d`は以下のように実行ユーザを指定できる(この場合は`root`)。

```shell
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

30 1 * * * root /bin/sh /usr/local/bin/backup.sh 1>>/var/log/cron.log 2>>/var/log/cron_error.log
```

これでうまく運用していたのだが新しいサーバに移植するとどうも実行した形跡がない。  
`/var/log/syslog`を参照してもよくわからないのだ。

エラーをメールで飛ばすことも一般的だが`mailutils`をインストールしなければならないしエラーログのためだけに導入するのも躊躇する。

ところが同じジョブを以下だと動くことがわかった。

```shell
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

30 * * * * root /bin/sh /usr/local/bin/backup.sh 1>>/var/log/cron.log 2>>/var/log/cron_error.log
```

二つの違いは先のものが「午前1時30分に実行」。後のものが「毎時30分に実行」となり時間指定の有無が違う。
書き方によって動くということはスクリプト自体には問題がないことがわかる。

そうなると時刻が正しくとれていないことが予想される。

# 解決方法

## タイムゾーンの設定

`20.04`をインストールしたときのデフォルトのタイムゾーンがUTCになることは知っていた。なので

```shell
sudo apt-get install tzdata
sudo timedatectl set-timezone 'Asia/Tokyo'
```

という作業はすでに終えている。
「[Ubuntu 20\.04 LTSでタイムゾーンを変更する方法](https://viasnake.com/ubuntu-20-04-lts-change-timezone-cli/)」で書いてあるのも同じ内容である。

## サービスの再起動

`cron`のサービスを念の為再起動する。

```shell
systemctl restart cron.service
```
しかし直らない。
そこでよくよく`/var/log/syslog`を見るとログのタイムスタンプが`UTC`のままなのだ。

# じゃあここは

```shell
sudo /sbin/shutdown -r now
```

で無事`syslog`のタイムスタンプも直った。めでたしめでたし。
といいたいが、わざわざサーバの再起動が必要なのかという疑問にぶち当たる。

そして調べた結果。

# 正しくはこうしよう

`rsyslog`の再起動

```shell
systemctl restart rsyslog.service
```

# まとめ

`cron job`がうまく動かなときは以下を確認

* コマンドラインから実行して確認するが基本的に絶対パスで表記し、実行場所が特定のディレクトリに依存しないようにする
* `/var/log/syslog`の内容を確認`
* 環境変数や`PATH`は通っているか(`shell`スクリプトなら冒頭に`. /etc/profile`と読み込ませるのもあり)
* サーバのタイムゾーンの設定は正しくできているか
* 上記設定が`rsyslog`のタイムゾーンにも反映しているか

ってとこだろうか。まだまだいっぱいあるだろうけど。

今回の原因は`rsyslog`の再起動を忘れていたことだった。
なので正確には「動かない」ではなく「意図しない時刻に動く」設定になっていたということ。
また冒頭に書いた`18.04`ではうまく動いたというのは記憶が曖昧なので当時もゴニョゴニョしたかもしれない。