OmniAICreator/Qiita-1.07M huggingface dataset:
LPIC学習　第一章システムアーキテクチャ

####ハードウェアの基本知識と設定
＊ハードウェア：人間様の目に見える機械
＊ソフトウェア：人間様の目に見えない、PCに対して何らかの処理をしてくれるもの。PC上では、
　　　　　　　　ファイルの集まりに見える。そのファイルの中に色々な処理が書かれている
アーキテクチャー：ハードウェアの基本設計
ハードウェア一覧
CPU：演算処理を行う頭脳担当
メモリ：PCを動かすためにデータを記憶する役割（机）電源を切ると中身は消える（512MB〜1GB
）は最低でも欲しい
ハードディスク
おっきいデータや長期間保存するデータは、メモリじゃなくてこっちに格納される
PCにも内蔵されているけど、よくヨドバシで売っているのが、外付けハードディスク
入力装置：キーボードとかマウスとか
拡張カード：
USB機器：本当はUSBは規格（ざっくりとルール）のことで、巷でよくUSB、USBとほざいているのはUSB機器のことです。

電源入れたらまず電気がいくのは以下の２つのどっちか
①BIOS
入出力をつかさどるプログラム。人間が電源を入れないと起動しないのだから、最初に携わるのはここ
なので電源が押されたらブートローダーにアクセスし、OSのカーネルを起動させるフラッシュROMにプログラムが書かれている
②UEFI
＊BIOSの後継者。
　大容量のHDが認識できる
　GUIで可視化できる（結構かっこいい）

＊この２つは、入出力の門番。人間様の入力に直接関わる仕事をしている。パソコンを起動したとき、まず電気が入るのがこいつ。このときみんな寝てるので、まずOSの起動をするためのブートを起こしてやらなければいけない。

あとは、、
日付や時刻の設定もできる

デバイス
　/dev に格納 (カーネルが認識しているデバイスは/procディレクトリに＊CPUとかメモリとかだった）ちなみにカーネルはbootにあるらしい　
　＊すべてのハード（機械）はデバイスファイルがないと動かない。というのを大前提におく
　まちが得やすいのがデバイスとドライバー
　機械はドライバーがないと動かない。ドライバーはカーネルの中に埋め込まれている（ファイルの中に機械語で書かれているらしい。）
　だからデバイスファイルは、デバイスドライバにアクセスするために必要

置き場
/dev
通常のデバイスファイル

/proc 
カーネルが認識しているデバイスは/procディレクトリに。
例）CPUとかメモリ情報が載っている
PCIデバイスの情報を表示する
lspciコマンドを使う
　
クラスドライバー
　絶対リナックスシステムで使うやん。デフォルトでインストールしているドライバ　（USBとか）
lsusbコマンド
　USBの詳細
udev
　今まではいちいち/devにデバイスファイルを格納しなければならなかったが
　いまは/sysに勝手にデバイスファイルができるらしいです

PCI
　マザーボードに付随しているデバイスの規格lspciなど

D-bus

デバイスドライバ
　書くデバイスに必要なプログラムだが、実はカーネルに組み込まれてる
　場所わかんねーからデバイスファイルで場所を示してあげる必要がある
　のでは？
lsmodコマンド 
   デバイスのロード（カーネルに必要なドライバーをがっちゃんこする）
modprobeコマンド
　デバイスを手動でロードする

####1-2 Linuxの起動とシャットダウン
まずOSを起動するにはブートローダという、OSを叩き起こすシステムが呼ばれる。具体的にはカーネルをメモリに読み込んであげる役割。
メモリは作業中のデータをばらまいている机なので、電源切ったらぶちされる（呼び直し）

dmegコマンドでカーネルが出力したメッセージが見えるんですが、カーネルが起動されてきたときにどんな処理をしたのかも筒抜けです。
/var/log/messeages
/var/log/dmeg
/var/log/boot.logにも書かれているのでcatコマンド使って見てみましょう

systermdでは、journalctl -kコマンドなので、間違えないようにする

システムをシャットダウンするには、shutdownコマンドを使いましょう
shotdown -h 時間　シャットダウン
shotdown -r 時間　システムを再起動
shotdown -k 時間

####1.3 sysVinitについて

sysVinitというのはinitが/etc/inititabファイルの設定にしたがって、システムに必要なサービスを起動していくやり方です！しかし、OSを起動する仕組み(デメリットは遅い！＊順番に起動していく)
例えば工場で、パーツできてないからライン止めるね、、いやほかの作業優先してやればよかったじゃん！ってなったからsystemmdとか、Upstartができた。

起動
↓
BIOS（
初期RAMディスク
　カーネルに含まれている。システムの軌道に最低限必要デバイスドライバが
　組み込まれているので、最初に行う）
ブートローダー
↓
initファイル
　ログインまでの処理を頑張る

ランレベルについて
　まず前提としてパソコンはいつもフルパワーで働いているわけではない。パソコンを立ち上げるとき、セーフモードって聞いたことあるとおもうけどレベルがきまっている、
runlevelコマンドで現在のrunlevelが見れちゃったりする
RHEL,CentOSの場合
0停止
1シングルユーザーモード
2マルチユーザーモード
3マルチユーザーモード
4未使用
5マルチユーザーモード
6再起動

initコマンド/tekinitコマンド
　ランレベルを変更することができる事前にwallコマンドでほかのユーザーに知らせられる
/etc/initab
   デフォルトのランレベルを手書きで直すことができる
systemdの処理の仕方
　Unitと呼ばれる処理単位serviceとかdeviceとかと呼ばれる
　そこでこれは何とかーってユニットをつけてって優先順位をつける
systemctl コマンド
　ユニットを使い分けるコマンド
/lib/systemd/system
ユニットの記述が書かれている
/etc/systemmd　
ユニットの設定を変更する
VSCodeで便利な拡張機能

VSCodeを再インストールする必要に迫られた為、自分用のメモとして便利な拡張機能一覧します。

前提：UnityでC#を使ったゲーム開発用です。

- Bracket Pair Colorizer
- C#
- C# Extensions
- C# FixFormat
- calc
- Code Spell Checker
- Debug for Unity
- Git Graph
- Japanese Language Pack for Visual Studio Code
- jumpy
- Mono Debug
- One Dark Pro
- PlantUMK
- TODO Highlight
- Unity Code Snippets
- Unity Tools
- vscode-icons
【Python】Workbook.removeメソッドを使ってシートを削除する

pythonを使用してExcelファイルの操作を勉強しています。
本日の気づき(復習)は、シートの削除に関してです。
pythonでExcelを操作するため、openpyxlというパッケージを使用しています。

「概要_1」「概要_1 (2)」「概要_2」「概要_2 (2)」・・・「概要_5」「概要_5 (2)」と言う名前のシートを持つ
ブック「資料」の「(2)」がついているシートだけ全て削除したいとします。

# Workbook.removeメソッド

```python:例
wb.remove(シート)
```

シート名ではなくて、Worksheetオブジェクトを渡すことでシートを削除してくれます。

```python:例）remove_sheet.py
from openpyxl import load_workbook

wb = load_workbook('資料.xlsx')

for ws in wb.worksheets:
    if ws.title.endswith('(2)'):
        wb.remove(ws)

wb.save('資料_変更後.xlsx')
```
今回は指定の文字列が全てシート名の後ろにあったため、
str.endswithメソッドを使用しました。
他にも以下のようなメソッドがありました。

- str.startswith():先頭が指定された文字列で始まっているかを返す。
- str.endwith():末尾が指定された文字列で終わっているかを返す。
- str.find():指定の文字列の位置を返す。
- str.isnumeric():すべてが数値かどうかを返す。

# 余談：in演算子
私はよくシートのコピーばかりをして
「概要_1」「概要_1 (2)」「概要_1 (3)」「概要_1 (4)」・・・「概要_1 (10)」
なんて状態になるのですが、
こういう場合のコピー分(2)～(10)だけを消したい場合はin演算子を使うと良いようです。

```python:例）remove_sheet.py
from openpyxl import load_workbook

wb = load_workbook('資料.xlsx')

for ws in wb.worksheets:
    if '(' in ws.title and ')' in ws.title:
        wb.remove(ws)

wb.save('資料_変更後.xlsx')
```
共通するものが何かを判断して、うまくコードを記述していきたいです！
JavaScript 勉強①

#変数
```let apple = 'Hello World!';```

let 変数を宣言するもの。後ろの言葉を変数にする

```js
apple = 'Hello World2!!';
```
Hello worldがHello World2!!になる

```py
console.log(apple);
```

Google Chrome の「検証」のコンソールで確認

#定数
定数は書き換えができない。文字列を変えたくないときに定数を使う。

```js
const bigApple = 'I'm Hungry Hello World!';
```
2単語続く場合は間の単語を大文字にする。

#配列
複数の文字列を入れて定義することができる

```js
let fruits = ['りんご','バナナ','レモン','スイカ'];
console.log(fruits[数字]);
```
プログラミングでは、カウントは０から始まる。

#ループ文
```js
let index = 0;　//カウントの設定
while(index < 3){　//もしもindexの値が3よりも低かったら{}内に書いてある命令を繰り返すという命令
	//　繰り返したい命令
	console.log(index);
	index++;　//数字を１足す
};
```

#配列とループ文を組み合わせる
```js
let index = 0;　//カウントの設定
while(index < fruits.length){　//.lengthは配列の数を指定する命令。（fruits.lengthは、4回繰り返す)
	//　繰り返したい命令
	console.log(index);
	index++;　//数字を１足す
};
```

#if /  else文
「もしこうだったらこうする」という命令で使える

```js
if(fruits.length > 3){
	console.log('あまい!!')
};
```
//(...)の条件を満たしたら、{...}が実行される

else //ifの条件に当てはまらなかった場合を設定する命令

#関数
①同じ命令を何度も使いたいとき
②任意のタイミングで命令を実行させたいとき

```js
const test = ()  => {
	//ここに実行したい命令を書く
}
```

（例）

```js
const test = () => {
  if(fruits.length > 5){
    console.log('あまい!!')
  } else {
    console.log('まずい');
  } 
};

test();
```

#引数
同じ命令を一部だけ変えて使い回したいときに引数が有効
（例）

```js
const test = (arg) => {
  if(fruits.length > arg){
    console.log('あまい!!')
  } else {
    console.log('まずい');
  } 
};

test(3); //「あまい!!」が表示される**
```

#オブジェクト
変数や定数は1:1なのに対して、オブジェクトは複数の値を持てる
つまり、オブジェクトはデータの塊。

```js
const paprika = {
	color: 'green',
	size: 'small',
};

console.log(paprika);
```

**document 表示しているページ全体のオブジェクト**

**event ユーザーがアクションをしたタイミングで何かをしたいときに使う**

**addEventListener 引数を２つ指定できる**
Bitbucket初めてのリモートリポジトリ

初投稿になります。
RailsチュートリアルでのBitbucketへリモートリポジトリする流れをメモします。


**１.公開鍵を作成する**
・下記のコマンドを実行すると鍵が現時点で存在するかどうか分かる。ない場合は何も出ない。

```
$ cd ~/.ssh
$ ls
authorized_keys2  id_dsa      (この辺に鍵が出る)
config            id_dsa.pub
```
・秘密鍵(id_rsa)
・公開鍵(id_rsa.pub)

鍵がない場合は鍵の作成が必要
ssh-keygenの後、パスワード設定を求められる。何も入力しなくてもOK

```
$ ssh-keygen
```

自分の公開鍵を表示（結構長い）
・出力したファイルの中身をコピー

```
$ cat ~/.ssh/id_rsa.pub
```
[詳しくはGitのサイトをご覧ください](https://git-scm.com/book/ja/v2/Git%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC-SSH-%E5%85%AC%E9%96%8B%E9%8D%B5%E3%81%AE%E4%BD%9C%E6%88%90)


**2.Bitbucketで設定**
・アバター画像をクリック、Personal settings、SSH 鍵、の順に選択。先程コピーした鍵を張り付け
・その後リポジトリを作成
・画面真ん中のURLをコピー　git@〜　みたいなやつ 
（注意 git clone git@〜 のgit cloneはコピーしない）
[Bitbucketはこちら](https://bitbucket.org/)

**３.ターミナルでの設定**

```
$ git remote add origin git@(BitbucketからコピーしてきたURL)

$ git push -u origin --all
```
これでOK!
Selenium 要素を取得できない場合②

## Google Chromeの開発ツールでは表示されているDOM要素がSeleniumで取得できない場合

前回の記事に追加で対処法をまとめます。

<１つの可能性>
取得したいDOM要素の上位階層に`iframe`タグがないか確認してみてください。

```
<iframe>
   <div>
      <span>ここへアクセスしたい</span>
   </div>
</iframe>
```
この状態で 
その`iframe`タグの要素へアクセスポイントを１度移す必要があります。

```
WebElement iframe_elem = driver.findElement(By.tagName("iframe"));
driver.switchTo().frame(iframe_elem );
```

これで `iframe`タグ要素以下の階層へアクセスできます。
1度`iframe`タグの要素へアクセスした後は、`iframe`タグの要素より下位階層を自動的に参照するようになります。

参照範囲をもとに戻したい場合は以下のコードでできるようです。

```
driver.switch_to.default_content()
```

参考ページ
[Seleniumでページ内のiframeを操作できない](https://qiita.com/hondy12345/items/c7359c300c73afd11d76)
Raspberry Pi PicoにThonnyじゃなくてVS Codeでコードを流し込んでみる


前回記事を書いたタイミングの翌日くらいにPico-Goがアップデートされたとのことで、普通に読み込むことが出来ました。

調べるとThonnyっていうIDEを使うっぽい手順が紹介されてますが、やはり慣れないので慣れてるVS Codeにしたいですよね。

MacBook Pro / Big Surの環境です。

## 事前準備

[【初見にオススメ】Raspberry Pi PicoをブラウザだけでLチカする(Web Serial API)](https://qiita.com/n0bisuke/items/c16f77c7017a2d4018dd)この記事を見てMicroPythonのファームをPicoに流し込んでおきましょう。

## Pico-Go 

VS CodeからPicoにコードを流し込むことができます。

> ![スクリーンショット 2021-02-06 18.47.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/7c408120-3020-59d4-605d-7b99711eadac.png "スクリーンショット 2021-02-06 18.47.33.png")

> https://marketplace.visualstudio.com/items?itemName=ChrisWood.pico-go

VS Codeにインストールします。

## コードを書いてみる

こんなコードを書いてみました。
1秒ごとに点滅します。

```blink.py
from machine import Pin

import time

led = Pin(25, Pin.OUT)

while True:
    time.sleep(1.0) #1秒待機
    led.value(1)

    time.sleep(1.0) #1秒待機
    led.value(0)
```


> ![スクリーンショット 2021-02-06 18.55.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/dce0b319-6168-5bd5-1ed5-a61a7441d2c3.png "スクリーンショット 2021-02-06 18.55.56.png")


## 接続

PCとPicoをUSBケーブルで接続した状態でVS CodeでPythonファイルを開くと下部のコンソールにこんな感じの表示が出れば接続されてます。

>![スクリーンショット 2021-02-06 18.56.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/b29ddeda-dbaa-6f08-f31c-83c5c4440bad.png "スクリーンショット 2021-02-06 18.56.14.png")

若干不安定なこともあるので再起動などしてみましょう。

## コードを実行してみる

cmd+shift+pでコマンドパレッドを開き、`Run Current file`を選択します。

> ![スクリーンショット 2021-02-06 18.57.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/7b171a82-08bc-71f4-1cd2-3d2cc3d9216a.png "スクリーンショット 2021-02-06 18.57.55.png")


`Runnning blink.py`と表示され、Lチカが実行できました。

> ![スクリーンショット 2021-02-06 18.58.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/830c3797-3788-a508-ba07-3900b49c524c.png "スクリーンショット 2021-02-06 18.58.23.png")

> [![Image from Gyazo](https://i.gyazo.com/1eafe135708c97ea6ef9a390c8bcc55f.gif)](https://gyazo.com/1eafe135708c97ea6ef9a390c8bcc55f)

## REPL実行も

もちろんREPL実行もできます。ちょっとしたコードを試すときはよいかもしれません。

> ![スクリーンショット 2021-02-06 19.02.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/fa89b070-1381-25d1-49d0-1370d4674615.png "スクリーンショット 2021-02-06 19.02.28.png")

## その他

再接続が怪しいときがあるので、USBケーブルの抜き差しなどをして再接続するとうまくいったりします。

`Upload current file only`などのuploadコマンドを使うことで永続的な実行ができるのかと思ったりもしたのですが、なぜかうまくいかないです。

> ![スクリーンショット 2021-02-06 19.03.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/7ca64fec-f705-c52b-df51-d6486fae0695.png "スクリーンショット 2021-02-06 19.03.56.png")

エラーは出てないですがLEDが光らず...

![スクリーンショット 2021-02-06 19.03.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/ba52887f-279f-cdab-80db-328feb7e78be.png "スクリーンショット 2021-02-06 19.03.43.png")

## 所感

VS Codeから簡単なコードを書いて、Picoで実行することが出来ました。

Pico-Goがまだ不安定な気がするので様子見ながらが良いかもしれません。
Salesforce Classic での Pardot を使用した対ビジネスマーケティングの自動化 ⇒ B2B Marketing Analytics のデータセットとダッシュボード ⇒ カスタマイズされたダッシュボードの作成

１．自社のビジネスのためにカスタムダッシュボードを作成する場合、議論すべき考慮事項は何ですか?
２．レンズを作成するときに決定するのは、次のどれですか?
３．ダッシュボードデザイナにレンズを追加するには、どうしますか?
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/6e0766a5-6bb1-5901-ce31-543013a211e1.png)
テスト前で暇だ！暇つぶしにALife(人工生命体)を動かしてみた

大学は現在、試験期間真っ只中！その流れに乗れない残念な農学系大学一年生です。世間は世知辛いですね。


皆さん、ALife（人工生命体）をご存知でしょうか？簡単に言えば、生命活動のシュミレーションをPC上に再現するものです。

「生命とは何か。」という根源的な学問分野でもあります。私が初めて、作って動かしたALifeは「ライフゲーム」です。ライフゲームの参考サイト：https://news.mynavi.jp/article/zeropython-9/

動かしたときの「おう、おう」感が忘れられません。これ、もしかしたら、農学分野に応用きくんじゃね？ということで、将来を見据えて趣味程度ですが暇な時に勉強しております。テスト勉強した方が絶対良いのですが。

<h1>んでもって、「ボイズモデル」やってみた
<p>ボイズモデルとは何か。それは、鳥っぽいものです。がんとか群れになって綺麗に飛ぶじゃないですか、あれです。
余談：ボイズモデルは1980年代のCG技術を大きく前進させました。以前のCGはあらゆる動きを人が決めなくてはならず、非常にだるかったんすよね。「バットマン　リターンズ」で使われたときは界隈で話題になったようです。

ボイズモデルは３つのルールを持っています。①「分離」周りの個体とぶつからないようにする。②「整列」周りの個体と同じ方向に飛ぶようにする。③「結合」群れの中心に向くようにする。
![ezgif.com-gif-maker.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001770/7c714e78-37e5-acfc-a3b7-bdc2174d2f83.gif)

<h1>眺めてたら、かわいく見えてきたから餌をあげてみた
<p>


![ezgif.com-gif-maker (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1001770/df0108b8-d61d-eb3a-fde7-18b7a06bcdcd.gif)


<h1>最後に
<p>本当はコードも併せて投稿したかったのですが、さすがに本のコードを掲載するのは筆者に申し訳ないのでやめました。気になる方は、「作って動かすALife」をご参照くださいませ。
WordPressのフロントページに最新投稿記事を表示させない！（Lightningテーマ）

WordPressの設定＞表示設定からフロント（TOP）ページに表示するページを設定できるのはご存知かと思います。

今回はテーマLightningを使用しているサイトでフロントに投稿ページも、固定ページも表示させたくない！
ってことがありましたのでその方法を記事にします。

#そもそも表示設定とは
WordPress管理画面のサイドメニューから、設定の中にあります。
![E986D151-1455-4E15-B37B-158EC3FC197B.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986728/19edc1c5-c8df-2320-b56a-2c7e7835ee5f.png)

「最新の投稿」と「固定ページ（投稿ページ）」を選択でき、どちらかチェックしなければなりません。

##Lightningでは不要だった
今回のサイトはブログサイトではないため、投稿ページをフロントに表示する必要がありませんでした。
フロント用に固定ページを作り、それを設定するのもありですが、、

**Lightningのウィジェットアイテムが魅力的すぎた！**
こちらの機能は割愛しますが、コンテンツブロックを組み合わせる感覚でページ作成できます。

このウィジェットでフロントページを構成しているため、デフォルトの「最新の投稿」または「固定ページ」は不要です。

#「投稿はありません」を消したい
先程の表示設定を「最新の投稿」にし、投稿記事が一つもなかった場合。
![C824F1DB-1E3F-4114-BE5A-83BB08C85987_1_105_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986728/9834325f-9e20-4384-5fcc-70b7b4dddae9.jpeg)

このように**「投稿はありません。」**と一文挿入されます。
投稿自体は表示されないけど、この一文が不要です。

###front-page.phpを編集
front-page.phpのファイルを編集して、「投稿はありません。」を削除します。
**※テーマのカスタマイズは必ず子テーマを作成し、バックアップした上で編集してください。**

font-page.phpの以下のソースコードを削除します。
![97C49F99-1916-4420-95B0-411D14B974B1_1_105_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986728/d4db269b-aefb-81e6-606c-4e303acedc72.jpeg)

このelseブロックは「投稿がなかった場合〜」の分岐処理です。
`<div class="well"><p>投稿はありません。</p></div>`
と、生成されます。

編集したファイルをアップロードします。
![B271BE2C-6FCF-4B50-A449-41B83D57B331.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986728/8393d371-5fca-9e43-15f3-eed213554c33.png)

**消えました!**

#投稿記事があれば表示されてしまうよ？
上記の方法では、投稿記事がなければ何も表示されませんが、
投稿記事があればもちろん表示されます。（最近の投稿記事一覧が表示）
今回のサイトの場合は固定ページのみの編成なので問題はありませんが、
投稿記事を作成するサイトももちろんあるかと思います。

##もう丸ごと消してしまう
そんな時はもう丸ごと消してしまいましょう笑
またまたfront-page.phpを編集します。
`<?php if ( 'page' == get_option( 'show_on_front' ) ) : ?>`
このifブロック直後のelseブロックを全て削除します。
![193218A1-51AF-40BF-9CA7-F3FAD0F6D83B_1_105_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986728/53ac4a2f-f48c-71ed-4e91-13e764f09310.jpeg)
![F1F3AE11-D4C4-4E58-BB3E-E33327DB1830.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/986728/b38c1807-cd4e-55c6-3035-364933bd0b28.png)

`if('page' == get_option('show_on_front'))`のifの評価は
管理画面の表示設定が「固定ページ」の場合trueを返します。
なので直後のelseブロックは「最新の投稿」に設定されている時です。

elseブロックを丸ごと消してしまえば「固定ページ」に設定している時だけ、フロントページに表示されるようになります。

**これで最新投稿一覧は表示されなくなり、「固定ページ」に設定を変えると固定ページは表示されます。**
備忘録・Google Apps Scriptでスタックしやすい原因と対策メモ

Google Apps Script(GAS)は便利な反面、Google Workspace(G Suite)特有の注意点が多かったりします。
コーディングだけだと気づきづらいポイントも多々あり、実務として取り組む中で詰まることが多かったポイントについて、個人用の備忘録としてまとめてみました。

## 権限が適切ではない
スプレッドシートやカレンダー、ドライブなどのサービスをGASで操作する、ということはユースケースとして多く発生すると思いますが
権限がなく、エラーになってしまう場合があります。

よくあるのが、**トリガーの権限が適切に設定されていない**ケースです。
管理者と開発者が分離している場合だと、トリガーは管理者が設定するケースもありますので
「管理者が必要なリソースに対して、適切な権限を持っているか」というポイントは、しっかりと考慮して設計し
必要があればマニュアルを作成して手順を共有する必要があります。

### 権限エラーが発生しやすいポイント
- ファイルの編集権限がない
- カレンダーの閲覧権限がない
- ドライブ・フォルダの編集権限がない

#### ファイル編集権限がない
スクリプトを実行するユーザーが、スプレッドシートなどのファイルに対して権限がない場合に発生します。

#### カレンダーの閲覧権限がない
Google Calendarの情報を取得するためには、**「実行するユーザーがカレンダーを表示できるようにする」**必要があります。
![d739a3c7887057135db949bd97c14341.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304628/4c2f7954-56f9-6523-d97c-005b4c187749.png)

例えば、管理者のカレンダーをGASプロジェクトの中で取得するためには、下記のうちいずれかの条件を満たす必要があります。

- 管理者がスクリプトを実行する
- 管理者権限でトリガーを設定し、トリガー経由でスクリプトを実行

![2f9421de8155a87a102e6c85e3aa4d45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304628/57da13ae-ca55-e93b-9ead-9c8e00501451.png)

また、管理者のカレンダーを開発者に共有された場合は、下記の条件でも取得可能です。

- 開発者がスクリプトを実行する
- 開発者権限でトリガーを設定し、トリガー経由でスクリプトを実行



## 範囲指定にマジックナンバーを使用している

> マジックナンバー（英：magic number）とは、人間語で書いたプログラムの元ネタ（ソースコード）の中に
> 直接埋め込まれた何らかの意味を持つ値のこと
> （引用元：https://wa3.i-3-i.info/word12868.html）

GASでスプレッドシートを使用している方であれば、`getRange`というメソッドを使用したことがあるかと思います。
スプレッドシートの範囲取得`getValues`や、スプレッドシートに値を入力する`setValues`などの前提として使用する範囲指定ですが
引数で使用する開始セル、範囲指定などでマジックナンバーを使用すると
スプレッドシートの構造を変える時に、後々マジックナンバーの値をすべて変える必要があるので
メンテナンスがだいぶやりづらくなります。

```javascript
// マジックナンバーを使用した場合
const targetRange = sheet.getRange(5, 1, 6, 5);

// マジックナンバーを使用しない場合
const startRow = 2;
const startColumn = 1;
const rangeRowCount = 6;
const rangeColumnCount = 5;

const targetRange = sheet.getRange(startRow, startColumn, rangeRowCount, rangeColumnCount);
```

コード量は増えますが、メンテナンスは定数部分の変更だけで良くなります。
マジックナンバーを使用しそうなポイントについては、必ず変数や定数を使いましょう。

GASの場合は異なるスクリプトファイル`.gs`であっても、関数外で定義していれば度のスクリプトファイルからでも読み込めます。
変数・定数は下記のように、別のスクリプトファイルとして管理するとメンテナンスしやすいです。
![e0c07aac61498ef8881dc8fd12c1cedb.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/304628/b673d429-c124-ba54-04ad-739004d0a6d9.png)
DataflowをPythonで使っているとbucket使用量が肥大化してお金が溶ける

# 要約

- PythonでDataflowを実行すると、実行完了後に GCSの `temp_location` のファイルが消えない不具合がある
  - Python実装のみの不具合（Javaで実行したときは勝手に消えるらしい）
- 毎日数十GBのBigQueryデータ投入タスクを行なっていると、すごい勢いでGCSの使用量が増え、多額の請求が発生する
- 今のところ、GCS bucket lifecycleでデータを自動削除するくらいしか回避方法がない


# 発生した問題

GCPの[Dataflow](https://cloud.google.com/dataflow)(Python)を使って、毎日数十GBのデータをBigQueryに投入するジョブを走らせていたところ、GCSの請求金額が**一日あたり$7/日ずつ**増えている事に気が付きました。

GCS bucketを調査すると、Dataflowで使っているbucketの使用量が爆速で増えてました。

- 220GB/日のペース

![5d6b9266-0239-4ce3-9e11-041b1d6c493a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54305/65956412-8735-29dd-07d8-2099135dbcb3.png)

# 原因
原因は、Dataflow実行時に [`--temp_location` で指定するフォルダ](https://cloud.google.com/dataflow/docs/guides/specifying-exec-params?hl=ja#setting-other-cloud-dataflow-pipeline-options)内に、生成された一時ファイルが永遠に残り続けていたためでした。

```bash
$ gsutil du -s -h gs://******-bucket/dataflow/tmp/** 
40.24 TiB    gs://******-bucket/dataflow/tmp/**
```

オプション名から、 `temp_location` フォルダに置かれる一時ファイルは、Dataflowジョブが終わった段階で勝手に消えるものと期待していましたが、実際には消えずに残っていました。

- ドキュメントでは「ジョブが失敗すると、Dataflow は一時ファイルやステージング ファイルを自動的にクリーンアップしないことがあります。」などと書かれている箇所があり、ジョブ成功時には勝手に消えそうな感じがする。
  - https://cloud.google.com/dataflow/docs/guides/troubleshooting-your-pipeline

`temp_location` フォルダのファイルが消えない件についてサポートに問い合わせたところ、「 **Javaでは消えるがPythonではなぜか消えない。バグっぽい** 」との回答を得たので、しばらくの間は `temp_location` のファイルを手動で消して、bucket使用量が肥大化しないようにしないといけなそうです。

- 当該の実装部分（サポート問い合わせで教えてもらったもの）
  - https://github.com/apache/beam/blob/v2.27.0/sdks/python/apache_beam/runners/dataflow/internal/apiclient.py#L474-L486

bucketの肥大化を防ぐには、[GCSのオブジェクトライフサイクル](https://cloud.google.com/storage/docs/lifecycle)を使うと良いです。

Dataflowを使用しているチームの方は、一度 `temp_location` が肥大化していないか確認していただけると良いかと思います。

# 備考

Q: どうして数十GBのBigQueryへのデータ投入で220GB/日も一時ファイルが作られていたんですか？
A: Apache Beamの [WriteToBigquery](https://beam.apache.org/releases/pydoc/2.27.0/apache_beam.io.gcp.bigquery.html#apache_beam.io.gcp.bigquery.WriteToBigQuery)の仕様のためです。
WriteToBigqueryは、BigQueryに書き込むデータを一旦無圧縮JSONとしてファイルに書き出すため、もとのCSVデータの10倍くらいのファイルを一時ファイルとして吐くようでした。

### 例：
元のデータが

```csv
c1,c2,c3,c4
1,"a","あ",true
2,"b","い",false
```
のようなテーブル（BeamのPCollection）のとき、WriteToBigqueryは

```json
{"c1": 1, "c2": "a", "c3": "あ", "c4": true}
{"c1": 2, "c2": "b", "c3": "い", "c4": false}
```
のようなファイルを一時ファイルとして吐いており、カラム名が毎行付与されていく＆無圧縮なのでファイルサイズがめっちゃでかくなってしまう。

- しかも、扱っているテーブルはカラム数が100個くらいあるテーブルが多く、値はカラ（null）であることが多いため、その分Beam→jsonでデータサイズが増えていました。
- 中間ファイルの形式は[無圧縮JjsonかAvroかを選べるらしい](https://beam.apache.org/releases/pydoc/2.27.0/apache_beam.io.gcp.bigquery.html#apache_beam.io.gcp.bigquery.WriteToBigQuery)のですが、どうせ一時ファイルだし何でもいいやと無指定でジョブを実行していたら、無圧縮jsonが永続的に保存され続ける状態になってしまっていました。
lookerのmeasureで、ユーザーのdimension選択に対応したwindow関数を実現する

# モチベーション

[Looker](https://ja.looker.com/)のviewで、どうしてもmeasure内にBigQueryのwindow関数を使いたくなることがあるかと思います。

```hoge_view.lookml
view: hoge_view {
  derived_table: {
    sql: select ~~~ （略）
  }

  dimension: d1_for_partition {(略)}
  dimension: d2_for_partition {(略)}
  dimension: d3_for_order {(略)}
  measure: m1 {(略)}
  measure: m_with_window {
    sql:
      first_value(${m1}) over (
          partition by ${d1_for_partition}, ${d2_for_partition}
          order by ${d3_for_order} desc)
      ;;
    required_fields: [d3_for_order]
  }
```
このとき、SQL内のpartition句に記述した `d1_for_partition, d2_for_partition` がexplore時に選択されていないと、
SELECTしていない列でpartitionしようとしてSQLエラーとなります。
ユーザーがexplore時に `d1_for_partition` を選択したときには ``d1_for_partition` のみで `partition by` する ＆＆両方選択したときは両方 `partition by` する ＆＆どちらも選択していないときはpartitionしないようなmeasureが定義できると便利そうです。

# 実現方法

```liquid
view: hoge_view {
  derived_table: {
    sql: select ~~~ （略）
  }

  dimension: d1_for_partition {(略)}
  dimension: d2_for_partition {(略)}
  dimension: d3_for_order {(略)}
  measure: m1 {(略)}
  measure: m_with_window {
    sql:
      {% assign partition_expr = '1' %}
      {% if hoge_view.d1_for_partition._in_query %}
        {% assign partition_expr = partition_expr | append: ",${d1_for_partition}" %}
      {% endif %}
      {% if hoge_view.d2_for_partition._in_query %}
        {% assign partition_expr = partition_expr | append: ",${d2_for_partition}" %}
      {% endif %}
      
      first_value(${m1}) over (
          partition by {{partition_expr}}
          order by ${d3_for_order} desc)
      ;;
    required_fields: [d3_for_order]
  }
```

partition by のあとに続くexpressionを、liquid expressionで生成しています。

- liquid式の `assign` と、lookerの `_in_query` を使用しています。
  - https://shopify.dev/docs/themes/liquid/reference/tags/variable-tags#assign
  - https://docs.looker.com/ja/reference/liquid-variables
  
- bigqueryのpartition expressionは、 `partition by d1_for_partition` と `partition 1,d1_for_partition` が同値なため、 `partition_expr` を `1` で初期化し、dimensionが選択されるたびに `append: ",${d1_for_partition}"` して、partition用の文字列を生成しています。

選択される可能性のあるdimensionの数だけ

```liquid
      {% if hoge_view.d1_for_partition._in_query %}
        {% assign partition_expr = partition_expr | append: ",${d1_for_partition}" %}
      {% endif %}
```
の部分が必要となるため、あまり多用するとlookmlがめちゃくちゃ長くなってしまいますが、それでもwindow関数がmeasureで使いたい場合はこうやってmeasureを定義するのが良さそうです。

- 具体的には、↓でどうしてもmeasureでwindow関数を使う必要があった。

https://qiita.com/gecko655/items/eddc687707886ea4174e



# 余談

Lookerのサポートの方にこのことを質問したところ…

![b0a485ae-7491-44b7-9480-8749f76f3019.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54305/8be65271-b7a2-553c-dab3-afaaba55ede3.png)


measureでのwindow関数の使用については、公式からのサポートは得られないと考えたほうが良さそうです。
自己責任でお使いください。
git pushでtagも同時にoriginへ反映する

# git pushするときにデフォルトでtagも反映する方法

[git-config/2.4.12](https://git-scm.com/docs/git-config/2.4.12)以降でpush実行時の--follow-tagsをデフォルト設定できる。

~~~bash
> git config --global push.followTags true
~~~

tagを同時反映したくないときは--no-follow-tagsでpush。
ものぐさすぎる気もしますが。
AppBarへのアイコンボタン追加

下の写真のようなAppBarのツールバーに、アイコンボタンを表示する方法に関して解説していきます。
プロジェクト作成時に生成されるサンプルアプリケーションを元に作成します。

エディターはAndroid Studioです。
私の開発環境は以下です。

```
flutter --version

Flutter 3.22.0
Tools • Dart 3.4.0 • DevTools 2.34.3
```

例として今回は初期コードを改良して、増減ボタンを付与したカウントアプリの作成を行います（AppBarをわかりやすくするため色を変更しています）。


![スクリーンショット 2024-05-17 20.05.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607760/6ff71145-6fa6-6592-1e17-2346d21db908.png)


# アイコンボタンの追加

まず、下記のようにScaffoldの中にあるAppBarにコードを追加します。

```dart
      appBar: AppBar(
        title: Text(widget.title),
        actions: [
          IconButton(onPressed: () => {}, icon: const Icon(Icons.add)),
          IconButton(onPressed: () => {}, icon: const Icon(Icons.remove))
        ],
      ),
```

`icon:`で表示したいアイコンを選択できます。
constを使用するとパフォーマンスの向上に繋がるようなのでconstを使用して定義しています。

`onPressed`はアイコンをタップしたときの動作です。
こちらは後ほど追加します。

これで実行をすると、+と-のアイコンが表示されるようになります。


# 値の増減

では次に、今作成したアイコンをタップし値が増減できるようにします。
`_incrementCounter()`と`_decrementCounter()`をそれぞれ作成し、`onPressed:`に追記します。

```dart
class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  void _decrementCounter() {
    setState(() {
      _counter--;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        actions: [
          IconButton(onPressed: () => _incrementCounter(), icon: const Icon(Icons.add)),
          IconButton(onPressed: () => _decrementCounter(), icon: const Icon(Icons.remove))
        ],
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              '+ボタンで値が上昇、-ボタンで値が減少します:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}
```
これで下の写真のように、値の増減ができるようになります。

![スクリーンショット 2024-05-17 20.08.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/607760/b14accd1-fb59-87b7-9686-72736d55dd8b.png)

お疲れ様でした。
ここまでご覧いただきありがとうございます。
[Laravel] Book::all->get() 、DB::table('books')->get() どちらを使うか

#### EloquentとQuery Builderのどちらを使うか
どちらもDBからデータを取得するために使用します。
＊EloquentとQueryBuilderの説明は長くなるのでここでは省きます
＊他にRawSQL（SQLの直書き）でデータベースを取得する方法もありますが、この記事では、よく使用されるEloquentとQueryBuilderに焦点をあてます。

Eloquent vs QueryBuilder。
どちらを、どのような場面で、いつ使うべきなのでしょうか？どう使い分けるのでしょうか？

結論、ケースバイケースで使い分けるべきと考えます。
シンプルなCRUD処理はEloquent、複雑なクエリはQueryBuilderと使い分けることが基本であると言われます。

一概に言えないですしチームの方針や、設計にも左右されると思います。適材適所で両方使っているプロジェクトも多いのではないでしょうか。

まず下記、EloquentとQueryBuilderのサンプルコードでは同じ結果が得られます。

```sample.php

class BookController extends Controller {
    //Eloquent
    $books = Book::with('author')->get();

    //Query Builder　（DB facade）
    $books = DB::table('books')
    ->join('author', 'author.id', '=' , 'book.author_id')
    ->get();
}

```

パフォーマンスの観点だと、（たいていの場合）ORMよりQuery Builderの方がレスポンスは早いと言われれています。 RawSQL（SQLの直書き）であればさらに早いです 。

Eloquentは使わずにQuery Builderを使った方が良いのではないでしょうか？
上述に戻りますが、それではなぜEloquentも使用されているのでしょうか？

下記にEloquentとQuery Builderのメリット、デメリット、最後に考え得るユースケースをまとめます。


####Eloquentのメリット、デメリット
メリット
・簡単、扱いやすく、シンプルに書けてビジネスロジックに専念できる。またDBの知識が浅くとも書ける
・コードの可読性が高く、メンテナンスしやすい
・テーブル名が変更となった際、Eloquent modelを1行変更すれば良いので対応が楽
例）

```change_tablesample.php
class Book extends model {
 //テーブル名を「books」から「my_books」に変更。
 protected $table = 'my_books';
}
```

・scopeを用いてSQLのロジックを使い回せる

例）

```scope_sample.php
//fetch all published book
Book::where('status', 'published')->get();

//scopeでロジックを分離
public function scopePublished($query)
    {
        return $query->where('status', 'published');
    }

//scopeを使用
Book::published()->get();

```

デメリット
・QueryBuilderに比べパフォーマンスが落ちる
・複雑なクエリを書く場合（複数のjoin, sort等）対応が難しい
・データベースの知識が浅いままになってしまう。

####QueryBuilder（DB facade）のメリット、デメリット
メリット
・複雑なJOINや大量のデータを扱う際は、Eloquentよりパフォーマンスが上がる
・コードをチェーンで繋げて書けるためRawSQLより書きやすい
・SQLに書き慣れている人は比較的スラスラ書ける

デメリット
・コードの可読性が下がる
・RawSQLにはレスポンスが劣る

####まとめ
個人的な意見も含まれますが、以上のメリット、デメリットを鑑みシンプルにまとめると下記のようなユースケースに別れるのではないでしょうか。
１）、シンプルなクエリやCRUD、浅いリレーションを扱うのであればEloquentを使う
２）、大量なデータや複雑なクエリ、リレーション、Eloquentがサポートしていないクエリを扱うのであれば、QueryBuilder（DB facade）を使う

上記したサンプルコードのようなシンプルなクエリであればEloquentを使うのが一般的かと思います。
また一つのプロジェクトの中でEloquentを使う箇所もあれば、QueryBuilde（DB facade）を使う箇所もあったりするのが一般的ではないでしょうか？

大事なのは適材適所でより良い方法を選択できるスキルです。（私はまだまだです←）

またORMに慣れてすぎてしまうとデータベースの内部構造を知らないままで開発ができてしまいます。
特に初学者にとってこれは成長の妨げになってしまうので、そういった観点からも両方（さらにはRaw SQLも）しっかり扱えるのが望ましいと考えます。

私もLaravelでの開発が長くなってくると、ちょいちょい生のSQLの書き方忘れてしまいます、、、（特にサブクエリやUNIONなど）

最後に。間違いや、他の考え等あればコメントいただけますと幸甚です。

#####参考
[Comparison of performance between Raw
SQL and Eloquent ORM in Laravel](http://www.diva-portal.org/smash/get/diva2:1014983/FULLTEXT02)

[Laravel Eloquent Builder Vs Scopes](https://www.paulund.co.uk/eloquent-builder-vs-scopes)

[Laravel Eloquent vs query builder - Why use eloquent to decrease performance [closed]](https://stackoverflow.com/questions/38391710/laravel-eloquent-vs-query-builder-why-use-eloquent-to-decrease-performance)
【Java】日本語が文字化けして？？？になる

JSPをブラウザで表示したところ、日本語が？？？？？になってしまっていた。
原因はEclipseでサーブレットを生成したときに、doGet()の中にデフォルトで書いてあるこれ。
response.getWriter().append("Served at: ").append(request.getContextPath());
#解説
**原因はgetWriter()です。**
getWriter()は簡単に言うと、文字をエンコードして、出力するための入れ物を取得するメソッド。
何も設定していないと、「ISO-8859-1」を使ってエンコードする。（**だから文字化けする**）
#対策
１つ目は、原因となるコードの前にエンコーディング方式を指定する。（以下のコードを記述。）
```
response.setCharacterEncoding("UTF-8");
```
２つ目は、このコードを削除する。
この場合、JSPのpageディレクティブでエンコード方式が指定されていることが前提です。
.NET Core MVCでDB接続ができなくて困った話。

#.NET Core MVCでDB接続ができなくて困った話。
###困った経緯
.NET Core MVCを使ってのWebアプリを製造中に起きました。
DBの内容を表示させようとWeb.config,ソースを下記のように記載し
デバッグを行うとエラーが発生しました。
調べて出てきたソースと同じように書いているのに接続できず1週間ぐらい困っていました。

**Web.config**

```
<configuration>
  <connectionStrings>
    <add name="接続文字列"
         connectionString="Data Source=サーバー名;Persist Security Info=True;User ID=ユーザー名;Password=パスワード"/>
  </connectionStrings>
</configuration>

```
**ソース**

```
//接続文字列の取得
var connStr　ConfigurationManager.ConnectionStrings["接続文字列"].ConnectionString;
var conn = new SqlConnection(connStr);
//DBへの接続
conn.Open();
```

###解決策
Web.configは使わず、appsettings.jsonに接続文字列を記載しこっちを使うと接続できました。
appsettings.json,ソースは下記のような感じです。

**appsettings.json**

```
"接続文字列": "Data Source=サーバー名;Persist Security Info=True;User ID=ユーザー名;Password=パスワード"
```
**ソース**

```
　　　　　　 var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional: false);
            var configuration = builder.Build();
            IDbConnection connection = new SqlConnection(configuration.GetValue("接続文字列", ""));
            // データベースの接続開始
            connection.Open();
```


###感想
無事DB接続ができてよかったでした。
Web.configで接続できなかった原因はいまだに不明です。
Python3: Firebase Auth のトークンの作成

次の記事を参考にしました。
[PythonでFirebase Authenticationのトークン取得とFastAPIでトークン検証](https://qiita.com/DeliciousBar/items/d6845d329e37f21a6d4f)
参考ページ
[Relyingparty: verifyPassword](https://developers.google.com/identity/toolkit/web/reference/relyingparty/verifyPassword)

```py:get_token.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
#	get_token.py
#
#					Feb/06/2021
#
# ----------------------------------------------------------------
import sys
import os
import  requests
from dotenv import load_dotenv
# ----------------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
email = sys.argv[1]
password = sys.argv[2]

sys.stderr.write("email = " + email + "\n")
sys.stderr.write("password = " + password + "\n")

dotenv_path = '.env'
load_dotenv(dotenv_path)

api_key = os.environ.get("API_KEY")
uri = f"https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key={api_key}"
data = {"email": email, "password": password, "returnSecureToken": True}

result = requests.post(url=uri, data=data).json()

token = result['idToken']

print(token)
sys.stderr.write("*** 終了 ***\n")
# ----------------------------------------------------------------
```

```text:.env
API_KEY = "AIzaS....."
```

実行スクリプト

```text
./get_token.py ppp@example.com hello888 > token01.txt
./get_token.py qqq@example.com hello999 > token02.txt
```

関連ページ
[Node.js: Firebase Auth のトークンの作成](https://qiita.com/ekzemplaro/items/4fd37b0b247c719c8cf0)
ssh接続切断後に"chrome not reachable"エラーが出る

#環境
gcp,centos7,python3.6,chromedriver88

#問題：ssh接続切断後に"chrome not reachable"エラーが出る

ssh接続でバックグラウンドでpythonを走らせてスクレイピングしたい
>nohup python スクレイピング.py &

これで行ける。最新のログを出力し続ける際は

>tail -f nohup.out


その際にスクレイピングpyでchromedriverを立ち上げた後、ターミナルのssh接続切断後に"chrome not reachable"エラーが出るのでこれを回避する


#解決策

~~nohup でpythonを実行する前に~~
~~sudo su ユーザー名で一度自分のユーザーにログインし直す~~



追記
screenコマンドを使って別sessionを作ってそこで実行する

#なんで？
わからん 勘としては最初にSSH接続したユーザーのセッション内でchromedriverを起動すると、その端末が落ちた（SSH接続を切断した）ときにchromedriverが巻き添えを食って落ちる気がするので、ユーザーからログインを挟みそこからchromedriverを起動することで、端末を起動しているセッションと別セッションの扱いになるので落ちない、気がする
レビューイ、レビューアの時に大切にしている事

最近、レビューアにもなり、レビューイにもなります。
そこで、私が大切にしているレビューに関する事について書きます。

これからレビューアになる人、レビューイになる人。
の参考になれば嬉しいです。

本内容は、私の経験に基づく、主観的な意見になります。

※ レビューイとは、レビューを受ける人。
※ レビューアとは、レビューをする人。

#レビューの目的とは？
責任者に理解してもらう（承認）ため、という側面もあります。

が、一番重要なことは、見落としている部分を複数人で見つけて、

**より良いものを作る**

と考えています。

# レビューイとして大切にしていること
## その１
**指摘に感謝を忘れない**
レビューアは、漏れている、考えが浅い部分を見つけてくれます。
その部分を直せば、より良いものになります。
なので、指摘に感謝の気持ちを忘れないようにしています。

## その２
**なぜ？と聞かれ、答えられないことは、見せない（書かない）**
意味の無い、論拠の無いコード、文章を書くと、レビューアが困惑します。
困惑とは、このコード、文章は、どういう意図で書いたのか？を考えさせてしまいます。

## その３
**パッと見て分かるミスは、見せない（書かない）**
レビューする時間は限られています。
パッと見て分かるミスがあると、そちらに時間が割かれてしまいます。

# レビューアとして大切にしていること
## その１
**指摘する際は、客観的な論拠を示す**
指摘は、相手の受け取り方により、考えを否定されたとも、取られかねません。
このため、指摘の論拠を明確にし、また、論拠は、主観的ではなく、客観的である必要と考えます。
客観的であると、相手も納得し、指摘を検討してくれると思います。

## その２
**主観的な意見は、主観的ということを伝えつつ、相談するように心がける**
主観的な意見は、趣味の領域にもなりかねません。
主観的な意見は、主観的であることを伝えつつ、相談するようにします。

## その３
**リリースしないと見つからない事から確認する**
レビューする時間は、限られています。
最優先で見つけるべきことは、リリースしないと気付かないことと考えています。
Node.js: Firebase Auth のトークンの作成

次の記事を参考にしました。
[firebase authのユーザー情報からトークン情報を取り出す簡単なスクリプトをnodeでつくる](https://qiita.com/TanakanoAnchan/items/ce548c637bffb3c16687)

必要なライブラリーのインストール

```bash
sudo npm install -g firebase
```

```js:get_token.js
#! /usr/bin/node
// ---------------------------------------------------------------
//	get_token.js
//
//					Feb/06/2021
//
// ---------------------------------------------------------------
'use strict'

var fs = require("fs")
const firebase = require('firebase/app')
const firebaseAuth = require('firebase/auth')
// ---------------------------------------------------------------
console.error ("*** 開始 ***")
const email = process.argv[2]
const password = process.argv[3]
console.error ("email = " + email)
console.error ("password = " + password)

const json_config = "config.json"
const json_str = fs.readFileSync (json_config,'utf8')

const firebaseConfig = JSON.parse (json_str)

showFirebaseIdToken(email, password)

console.error ("*** 終了 ***")

// ---------------------------------------------------------------
// firebaseにアクセスしトークンを取得、表示する
function showFirebaseIdToken(email, password) {
  // firebase appの初期化
  const app = firebase.initializeApp(firebaseConfig)

  // メール認証
  firebase.auth().signInWithEmailAndPassword(email, password)
    .then((result) => {
      const firebaseAuthUser = result.user
      firebaseAuthUser.getIdToken(true)
        .then((idToken) => {
          console.log(idToken)
        })
        .catch((error) => {
          console.log(error)
        })
    })
    .catch(function (error) {
      console.log(error)
    })

	console.error ("*** showFirebaseIdToken *** end ***")
}

// ---------------------------------------------------------------
```

```json:config.json
{
  "apiKey": "AIzaS.....",
  "authDomain": "hogehoge-foobar.firebaseapp.com",
  "databaseURL": "https://hogehoge-foobar.firebaseio.com",
  "projectId": "hogehoge-foobar",
  "storageBucket": "hogehoge-foobar.appspot.com",
  "messagingSenderId": "532........",
  "appId": "1:532.......:web:xxxxxxxxxxxxxxx",
  "measurementId": "G-xxxxxxxxxxx"
}
```

実行スクリプト

```bash
export NODE_PATH=/usr/lib/node_modules
#
./get_token.js ppp@example.com hello888 > token01.txt
./get_token.js qqq@example.com hello999 > token02.txt
```

作成された token は次のページで検証できます。
[JWT](https://jwt.io/)
SQLiteのWith句で再帰を使って日付のデータを生成する

SQLiteでイベントの発生を示すデータを集計したいとします。
単純に日付で`group by`するとイベントが発生した日付のみでgroup化されたデータが集計され、日付が飛び飛びになってしまいますね？

こういうときに日付の一覧のデータとjoinすれば問題ないのですが、そのためにtabelやviewを作るのもな〜というときに今回のテクニックが使えます。

# やりかた
例として2021年02月の日付一覧を生成します。

```sql

WITH RECURSIVE calendar(calendar_date) AS (
    SELECT
        -- ここが始まりの日付
        DATE('2021-02-01') AS calendar_date
    UNION ALL
    -- UNION ALL 以下が再帰処理部分
    SELECT
        DATE(calendar_date, '+1 day') AS calendar_date
    FROM calendar
    WHERE
        -- ここが終わりの日付
        calendar_date < DATE('2021-02-28')
)

-- ここが実際のSELECT文
SELECT calendar_date FROM calendar;
```

`calendar_date`は`2021-02-01`から始まり、再帰的にselectが実行されるたびに`calendar_date`に`+1day`されて、それが`2021-02-28`まで続きます。その結果が全て`UNION ALL`で連結されて`calendar`という一つのテーブルになります。

# 結果
![スクリーンショット 2021-02-06 17.30.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47882/85dd2a8b-06f1-a5f8-2fe3-39c672127211.png)

これはRedash上の表示です。BIツール上などで日付の一覧を用意するのに便利ですね。


# 参考
- [SELECT文だけでカレンダーデータを作成する](https://sqlite-date.com/calendar)
- [The WITH Clause](https://sqlite.org/lang_with.html)
【Rails】flashの使い方について



##はじめに
今回は、メッセージアプリでメッセージを作成して送信ボタンを押した際に
”投稿しました”という表記をさせる想定&目的としました。

##メッセージ機能とは
入力フォームに名前、住所、質問等を書いて送信ボタンを押した際に
ユーザに送信完了を知らせるメッセージが出てくるとことがあると思います。
そのメッセージを表示させているのがflashになります。
その他にも確認、エラーメッセージで使用されています。

##flashの基本的な形
下記”メッセージ”部分に、表記させたい内容を書きます。
redirect_toでアクション実行後に、指定したルーティングを実行させます。

```
flash[:notice] = "メッセージ"
redirect_to ルーティング
 
または

redirect_to ルーティング, notice: "メッセージ"
```

##flash コントローラ部の記述
下記は、コントローラ部の記述になります。
この場合、表示されるメッセージは、”投稿しました” になります。

```app/controllers/posts_controller.rb
def create
  post = Post.create!(post_params)
  flash[:notice] = "投稿しました" 
  redirect_to posts_path
end
```

##flash ビュー部の記述
flash[:notice] = ”投稿しました” は、変数名[:key] = "value" というハッシュのような構造になっています。
そして、each doの先頭についているflashは変数名になります。
flashのkeyをeach doでflash_typeに送っているイメージになります。
これによってメッセージが表示されます。

```app/views/layouts/application.html.erb
<!DOCTYPE html>
#省略
<body>
  <% flash.each do |flash_type, mssage| %>
    <p><%= mssage %></p>
    <hr>
  <% end %>
  <% yeild %>
</body>

```

##redirect_to ルーティング, notice: "メッセージ" に書き換えた場合
先ほど説明した項目”flashの基本的な形”で記述があったように
flash部分をredirect_to ルーティング, notice: "メッセージ"の形に書き直すこともできます。

```app/controllers/posts_controller.rb

def create
  post = Post.create!(post_params)
  redirect_to posts_path, notice:"投稿しました"
end
```

##最後に
私はプログラミング初学者で、教材学習を進めています。
その際、早く終わらそうと思って、なんとなくプログラムを書いている場合が多々あります。
時には一度立ち止まって、これってどうなっているのかな？と考えてみたり
Qiitaの記事を書くことで、理解が深まればと思い書きました。
もし、この記事が参考になれば幸いです。

ありがとうございました。
【基本情報技術者】2進数を16進数に変換

2進数1010.01を16進数に変換

【解説】
まず2進数1010.01を4桁ごとに区切ります.
ここでは計算しやすいように、小数点で区切り、
計4桁になるように0を付け加えます。

今回であれば1010-.0100　という感じです.

16進数は２の4条なので
4桁目は値×8
3桁目は値×4
2桁目は値×2
1桁目は値×1
となり、
それぞれ出た数値を文字列的に並べれば16進数へと変換できます。

今回の場合だと

1010
1 * 8 = 8
0 * 4 = 0
1 * 2 = 2
0 * 1 = 1
= 11 = B

0100
0 * 8 = 0
1 * 4 = 4
0 * 2 = 0
0 * 1 = 0
= .4


これを並べると B.4となり、この数値が16進数に変換した1010.01となります。

*補足
16進数は、0123456789ABCDEFの16桁で表します
RustでAWS S3のファイルをダウンロードして、reqwestでpostしてみる

# 追記(2021/08/26)
rustro_coreの0.47.0からByteStreamからSyncがなくなりました。なので一旦ローカルにファイルを作成してPartを作る必要があります。その部分は以下のようになります。

```rust
let file = std::fs::File.open("test.jpg");
let tokio_file = tokio::fs::File::from_std(file);
let stream = tokio_util::codec::FramedRead::new(tokio_file, tokio_util::codec::BytesCodec::new());
let Part::stream(reqwest::Body::wrap_stream(stream))
```

# 目的
AWSからダウンロードしたファイルを、HTTPのPOSTメソッドでアップロードしたいです。ローカルにファイルとして保存してアップロードするのはダサいので、ストリームで流してみました。

[httpbin.org](https://httpbin.org)はこちらが送ったデータをレスポンスで返してくれるので、自身が送ったパラメーターのデバッグなどで便利です。

# プログラム
```rust:Cargo.toml
[package]
name = "upload_test"
version = "0.1.0"
edition = "2018"

[dependencies]
reqwest = { version = "~0.10.10", features = ["json", "stream"] }
rusoto_core = "~0.45.0"
rusoto_s3 = "~0.45.0"
serde_json = "~1.0.0"
tokio = { version = "~0.2", features = ["macros"] }
```

```rust:main.rs
use rusoto_core::Region;
use rusoto_s3::{GetObjectRequest, S3Client, S3};

use reqwest::multipart::{Form, Part};

type Error = Box<dyn std::error::Error>;
type Result<T, E = Error> = std::result::Result<T, E>;

#[tokio::main]
async fn main() -> Result<()> {
    // S3からファイルをダウンロードする
    let client = S3Client::new(Region::ApNortheast1);
    let mut object = client
        .get_object(GetObjectRequest {
            bucket: "my_bucket".into(),
            key: "target.txt".into(),
            ..Default::default()
        })
        .await?;

    // ダウンロードしたファイルをマルチパートのストリームに渡す
    let body = object.body.take().unwrap();
    let part = Part::stream(reqwest::Body::wrap_stream(body));
    let form = Form::new().part("file", part);

    // POSTしてみる
    let http_client = reqwest::Client::new();
    let res = http_client
        .post("http://httpbin.org/post")
        .multipart(form)
        .send()
        .await?;
    let json: serde_json::Value = res.json().await?;
    println!("{:?}", json);
    Ok(())
}

```

```text:target.txt
予定表～①💖ﾊﾝｶｸだ
```

```text:結果
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "file": "予定表～①💖ﾊﾝｶｸだ"
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "227",
    "Content-Type": "multipart/form-data; boundary=b7ff64c9dbddec30-c7bfea0ddcf8be1d-96b51cb6af4a31c7-c3e7a3069bab2189",
    "Host": "httpbin.org",
    "X-Amzn-Trace-Id": "Root=1-601e515e-0fa5de530c1ea26d188d69e6"
  },
  "json": null,
  "origin": "xxx.xxxx.xxx.xxx",
  "url": "http://httpbin.org/post"
}
```
【基本情報技術者】2進数を8進数に変換

2進数1010.01を８進数に変換

【解説】
まず2進数1010.01を3桁ごとに区切ります.
ここでは計算しやすいように、小数点で区切り、
計3桁になるように0を付け加えます。

>>今回であれば001-010-.010　という感じです.

８進数は２の3条なので
3桁目は値×4
2桁目は値×2
1桁目は値×1
となり、
それぞれ出た数値を文字列的に並べれば8進数へと変換できます。

今回の場合だと

001
0 * 4 = 0
0 * 2 = 0
1 * 1 = 1
= 1

010
0 * 4 = 0
1 * 2 = 2
0 * 1 = 0
= 2

.010
0 * 4 = 0
1 * 2 = 2
0 * 1 = 0
= .2

これを並べると 12.2となり、この数値が８進数に変換した1010.01となります。

*補足
8進数は01234567の８桁で表します
AWS SAM CLI Layerを使ってビルド高速化

##問題
* ビルドする時のnpm installが重い。

##解決

* 依存モジュールはLayerとして切り出し。
* Lambda関数側は依存モジュールをdevDependenciesに入れてエディタ上はパスを通す
    （ビルドするとdevDependenciesはnode_modulesに入らないから、デプロイするLambda関数が肥大化する心配はない）。
* 開発中はキャッシュオプションをつけてLayerのビルドは短縮。
   ```sam build HelloWorldFunction -c```


##Layerについて

Layerは/opt下に配置されるイメージ。
なのでパスが通っているところにライブラリが置かれれば良い。

例えば、ランタイムがNode.jsの場合は、/opt/nodejs/node_modulesとかにデフォルトでNODE_PATHが通っている。
だから、nodejs/node_modulesにライブラリを落としてLayerとしてデプロイすればよい。
**ディレクトリ名がnodejsなのがポイント。**
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime

SAMの場合は、ローカルディレクトリをLayerとして指定すれば、よしなにビルド、デプロイしてくれる。



##使う側

普通にインポート

~~~
const { format } = require("date-fns");
exports.lambdaHandler = async (event, context) => {
  try {
    const d = format(new Date(2014, 1, 11), "yyyy-MM-dd");
    console.log("d=" + d);
~~~


エディタ上で参照できるようにdevDependenciesに追加する。


```$ npm install date-fns lodash --save-dev```


##layer側

ディレクトリ名はデフォルトでは**nodejs**としないとだめ。
（多分NODE_PATHを自分で設定すれば別）
ライブラリを落とす。

```
$ mkdir nodejs
$ cd nodejs
$ npm init
$ npm install date-fns lodash
```

##テンプレート
Layerを定義し、Functionから参照。
ContentUriにローカルディレクトリのパスを記載。
**Metadataが無いとLayerをビルドしてくれない。**
Importエラーになってハマった。
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-resource-layerversion.html
デフォでやらない理由があるんだろうか。
いろいろカスタマイズはできそう。
https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/building-layers.html


~~~
  MyLayer:
    Type: AWS::Serverless::LayerVersion
    Properties:
      Description: Layer description
      ContentUri: "nodejs/"
      CompatibleRuntimes:
        - nodejs12.x
    Metadata:
      BuildMethod: nodejs12.x

  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: hello-world/
      Handler: app.lambdaHandler
      Runtime: nodejs12.x
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
      Layers:
        - !Ref MyLayer
~~~

##ビルド

Layerもビルドしている旨表示されます。

```$ sam build```



##ローカル実行

local Layerのイメージを作りだした。

~~~
$ sam local invoke HelloWorldFunction
Invoking app.lambdaHandler (nodejs12.x)
MyLayer is a local Layer in the template
Building image..
~~~

Layerに入っているライブラリを使えた。成功。

~~~
INFO    d=2014-02-11
~~~

##キャッシュオプションでビルド

キャッシュがヒットするとこうなる。

```
$ sam build -c

Starting Build use cache
Valid cache found, ...
```



Layerは修正しないから、キャッシュがヒットするからライブラリ落とすこともなく早い。
直した関数だけCache is invalidになる。

```
sam build HelloWorldFunction -c

Cache is invalid, running build
...
Valid cache found, copying previously built resources from layer build
```

####キャッシュがヒットしない場合
node_modulesを比較するから、キャッシュがヒットしない場合は、
node_modulesをきれいにする。

```$ npm prune```
テーブルにエイリアスを設定する方法【CakePHP4】

# CakePHP4で使用するテーブルにエイリアスを設定する

```
// 使い方
$query = $this->setAlias('モデルのエイリアス名')->find()
           ->select([
             'モデルのエイリアス名.値',
           ])
           ->enableHydration(false)
           ->first();

// 使用例
$query = $this->setAlias('User')->find()
           ->select([
             'id' => 'User.id',
             'name' => 'User.name'
           ])
           ->enableHydration(false)
           ->first();
```
OpenSUSEでKindleを使う

# LinuxでもKindle使いたい
プライベートではLinuxデスクトップをメインで使用しているのですが、これでもKindleで技術書とか読みたいことが多々ある。KVMで仮想環境もあるしそっちでも良いのだけれど、WineでKindleが動いたら幸せそうなのでやってみた。

#先行記事
https://www.hiroom2.com/2017/08/16/opensuse-14-kindle-ja/

ここでやっているけど、言われたとおりにやってもバージョン違いで動かなかった。

#手順
## Wineインストール
``sudo zypper -n in wine wine-32bit``

## Winetricsを使ってインストール
``WINEPREFIX="$HOME/prefix32" WINEARCH=win32 wine wineboot``

``WINEPREFIX="$HOME/prefix32" WINEARCH=win32 winetricks``

ちなみに、``WINEPREFIX``はWine環境を分ける場合に付ける必要があるので、全部一緒くたで良いなら省いても良い。

このあと、出てきたメニューからKindleをインストールする項目を選べば良い。

途中で警告っぽいポップアップが大量に出るが、とりあえず全部OKで問題なし。

## フォント設定
Wineあるあるだが、UIの日本語表記が全部化けて豆腐になってる。筆者は10年以上Linuxデスクトップを使い続けているが、昔からあまり改善されない点だ（こう言うとお前がやれと言われるのがLinux界隈なのだが・・・）。

豆腐ばっかりじゃ使えないけど、日本語フォントで正常に表示されるように頑張るのも面倒なので、UIを英語にする。

![GetImage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390159/f2844eea-d9ce-94ab-b7a9-2ae7e0dad96c.png)

ツールバーの左から2番めをクリックして、出てきたメニューの一番下をクリックするとオプションメニューが表示される。ここでなんとかLanguageを「English」にできれば、上記の図みたいに文字化けが治る。

豆腐ばっかりからこのメニューを出す芸当は、おそらく日々Amazonに金を注ぎ込んでいる人じゃないと厳しいだろう。

#使用感
少し触ってみたが、エラーで落ちるといったことは無さそうだ。Kindle Cloud Readerでは読めないコンテンツが多いので、こうやってなんとか動くと非常に助かる。

みなさんもLinuxデスクトップ使いましょう。GeekoかわいいよGeeko。
サブクエリをfrom句で利用する方法【CakePHP4】

# CakePHP4でサブクエリをFrom句で利用する

```
// サブクエリの作成
$subquery = $this->Model名->find()
              ->select([
                  'エイリアス名' => '値'
              ]);
        
// サブクエリを利用してデータを取得する    
$query = $this->Model名->find()
           ->select([
         　　  'サブクエリ名.値'
           ])
           ->from([
               'サブクエリ名' => 'サブクエリを格納した変数'
           ])
          ->enableHydration(false)
          ->all()
          ->toArray();

// 使用例
$subquery = $this->Users->find()
           ->select([
             'user_name' => 'name'
           ]);

$query = $this->Users->find()
           ->select([
             'subquery.user_name'（エイリアスを付けても取得可）
           ])
           ->from([
             'subquery' => $subquery
           ])
           ->enableHydration(false)
           ->all()
           ->toArray();
```
【Ruby】クラスメソッドを動的に定義する

### はじめに
クラスメソッドを動的に定義したかったのですが、検索しても求めている情報が見つけられなかったので、この記事が同じ情報を求めている方の参考になれば嬉しいです。

### TL; DR
- クラスメソッドの動的定義は[Object#define_singleton_method](https://docs.ruby-lang.org/ja/latest/method/Object/i/define_singleton_method.html)を使う
- インスタンスメソッドの動的定義は[Module#define_method](https://docs.ruby-lang.org/ja/latest/method/Module/i/define_method.html)を使う

### クラスメソッドの動的定義
過去に書いた以下のコードを見ていて、「これメソッドの動的定義を使えばもっとシンプルに書けるのでは？」ということに気づきました。
今のままだと飲み物の種類が追加される度に3行のメソッドを追加していくことになるので種類に比例してコード量が膨れ上がってしまいます。

```drink.rb
class Drink
  attr_accessor :name, :price

  def initialize(name, price)
    @name = name
    @price = price
  end

  def self.coke
    self.new("coke", 120)
  end

  def self.redbull
    self.new("redbull", 200)
  end

  def self.water	
    self.new("water", 100)	
  end
end
```

修正したコードがこちら。

```drink.rb
class Drink
  attr_accessor :name, :price

  def initialize(name, price)
    @name = name
    @price = price
  end

  def self.define_drink(name, price)
    define_singleton_method(name) do
      new(name, price)
    end
  end

  define_drink("coke", 120)
  define_drink("redbull", 200)
  define_drink("water", 100)
end

Drink.coke
# => #<Drink:0x00007fbd5f854908 @name="coke", @price=120>
```

`self.define_drink`メソッドを追加し、その中で`define_singleton_method`を呼び出して、引数に作成するクラスメソッド名を指定することで、`self.coke`、`self.redbull`、`self.water`の各メソッドを動的に定義しています。

### インスタンスメソッドの動的定義
インスタンスメソッドを動的に定義したいときは`define_method`を使います。`define_method`の引数に渡した文字列が動的に定義するメソッド名になります。

```drink.rb
class Drink
   def self.define_drink(name, price)
     define_method(name) do
       p "#{name}は#{price}円です"
     end
   end
   define_drink("coke", 120)
end

Drink.new.coke
# => "cokeは120円です"
```
Stripeの「trial_end」などのタイムスタンプを作成する方法

こんにちは～

今日は特に細かい説明はせずに、同じ悩みを抱えている方たちにすぐにタイムスタンプを作成できるような記事を書いていこうかなともいます。

イメージとしてはこれ。

```
"trial_end": 1612063389,
```

また、タイムスタンプから〇年〇月〇日の取得方法も説明します。

それでは見ていきましょう！

#現在の時刻およびUNIX時間への変換#

``1612063389``のようなタイムスタンプを作るには、UNIX時間をというものを作成していきます。

とりあえずコード書いていきます。

```index.js
// 現在の時刻
const nowDate = new Date()
// 指定日時のタイムスタンプ
const trialEndDate = new Date(nowDate.getFullYear(), nowDate.getMonth() + 1, nowDate.getDay())
// UNIX時間への変換
const unixTrialEndDate = Math.floor(trialEndDate.getTime() / 1000).toString()
```

``trialEndDate``では、例えば１ヶ月後とか１年後とか自分の用途に合わせて変更してください。

``trialEndDate.getTime()``を``1000``で割ることで指定した日時のUNIX時間を作成することができます。

#UNIX時間から日時への変換#

次はUNIX時間から``〇年〇月〇日``のように変換していきます。

```index.js
const time = new Date(unixTrialEndDate * 1000)
time.toLocaleDateString() //例：2021年3月6日
```

このようにしてUNIX時間を作成し変換を行うことができます。

今日はこんな感じで終わります。

以上、「Stripeの「trial_end」などのタイムスタンプを作成する方法」でした！

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい！！

Thank you for reading