OmniAICreator/Qiita-1.07M huggingface dataset:
SWR Options

## これはなに？
SWRを使ってみて、optionsで色々と設定ができそうだなーと思ったのと、ドキュメントがあまりわかりやすくなかったので自分の言葉でまとめてみました


## オプション一覧
https://swr.vercel.app/docs/options

### `revalidateIfStale`
マウントした時に古いデータがあった際に自動で再検証するかどうかを制御している
デフォルト: **true**

何回取得しても値が変わらない場合はfalseにする(そしたらSWR使う必要あるのか？とも思った)


### `revalidateOnMount`
コンポーネントのマウント時に自動で再検証するかどうか
デフォルト:**false**
 enable or disable automatic revalidation when component is mounted

### `revalidateOnFocus`
画面に戻った際に自動で再検証を行うかどうか
デフォルト: **true**

強整合性が求められるのであればtrueなのかな？

### `revalidateOnReconnect`
ブラウザがオンラインになった際に自動で再検証するか
デフォルト： **true**

### `refreshInterval`
ポーリング間隔の指定、単位はミリ秒
デフォルト： **無効**

### `refreshWhenHidden`
画面が表示されていない際にリフレッシュをするか
※但し、`refreshInterval`が無効の場合は自動的に強制的にfalseになる
デフォルト: **false**

### `refreshWhenOffline`
ブラウザがオフラインの際にリフレッシュをするか
※但し、`refreshInterval`が無効の場合は自動的に強制的にfalseになる
デフォルト: **false**

### `shouldRetryOnError`
エラー時のリトライの有無
デフォルト： **true**

### `dedupingInterval`
重複したリクエストを削除する期間の指定
デフォルト: **2000**

### `focusThrottleInterval`
フォーカス時の再検証の期間指定
デフォルト: **5000**

### `loadingTimeout`
onLoadingSlowイベントを発行するためのタイムアウト
デフォルト: **3000**

### `errorRetryInterval`
エラーが起きた際のリトライの間隔、単位はミリ秒
デフォルト: **5000**

### `errorRetryCount`
エラー時のリトライの最大試行回数
デフォルト： **なし**

### `fallback`
https://swr.vercel.app/docs/with-nextjs#pre-rendering-with-default-data

### `fallbackData`
初期データの設定（hookによって異なる)

### `onLoadingSlow(key, config)`
リクエストの読み込みに時間がかかりすぎる場合のコールバック関数
「マークダウン記法の画像サイズ調整」が面倒くさいのでツール作った。


#マークダウン記法の画像サイズ調整のやり方

**Qiita**の記事を書いていて

「あれ…？マークダウン記法で画像サイズの変更ってどうやるの？」と思い、調べると

https://qiita.com/shti_f/items/b819d7fd8cb79ae29687

こちらの記事がヒット！

…なるほど。これ↓

```
![***画像のタイトル***](***画像のURL***)
```

を、こう↓

```
<img src="***画像のURL***" width="***画像サイズ***">
```

して**imgタグの形**にすれば良いわけですね。なるほど。

#でも、ちょっとめんどくさい

普通は**Qiita**に画像を、ﾎﾟｲっと**ドラックアンドドロップ**すれば自動的に

```
![***画像のタイトル***](***画像のURL***)
```

こう↑なるのに、そこから打ち直さないとダメなのは**地味に面倒くさい**…ので、[**前に作った文章校正ツール**](https://readability-checker.khufrudamonotes.com/)のプログラムをちょっと改造して…

**「マークダウン記法の画像テキストを貼り付けると、imgタグの形へ置換してくれるツール」**

↓を作りました。

https://readability-checker.khufrudamonotes.com/markdown-to-img

#作ったツールの使い方
こんな感じで、左側のテキストエリアに貼り付けるだけで（右側に）変換してくれます。
（**width**の指定は、変更できます。）

![「画像のマークダウン記法」を「imgタグ」へ置換するツールのデモ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486558/75eb181c-9bc3-6325-a0a5-f34accdb6324.png)

**複数の画像**を一気に変換もできるので、打ち直すよりラクで早いはずです。

主に自分用ですけど、よかったらどうぞ。↓

https://readability-checker.khufrudamonotes.com/markdown-to-img
ABC176 C - Step を解いた

https://atcoder.jp/contests/abc176/tasks/abc176_c

![abc176c_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/16559bef-bbdc-ea8c-3bbe-beed753331b3.png)
![abc176c_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/d133fe43-a6a6-4028-9e05-57e15764840b.png)
![abc176c_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/db923496-de6e-de9a-d3be-187d054e8e39.png)

```abc176c.py
N = int(input())
lis = list(map(int,input().split()))
score = 0
ref = lis[0]

for i in range(1,N):
    if ref > lis[i]:
        score += ref-lis[i]
    else:
        ref = lis[i]

print(score)#110ms

```

for i in range(N) で リスト内の値を呼び出すより、
for a in lis というように直接代入する方が速い。
また、if , elif を使うよりは if,else で完結した方がより速い。

```abc176c_r1.py
def solv0():
    _ =int(input())
    A = list(map(int,input().split()))
    cnt = 0
    max_hight = 0
    for hight in A:
        if max_hight < hight:
            max_hight = hight
        elif max_hight > hight:
            cnt += max_hight-hight
    return cnt

def solv1():
    _ =int(input())
    A = list(map(int,input().split()))
    cnt = 0
    max_hight = 0
    for hight in A:
        if max_hight <= hight:
            max_hight = hight
        else:
            cnt += max_hight-hight
    return cnt

#print(solv0()) #86ms
print(solv1()) #80ms
```
【Ruby】あと何日かを求める

##環境
Ruby 3.0.2

##前提
12月10日までに100記事目標にしている自分に対して、今の投稿進捗を教えてくれるbotに台詞を追加したい。
（実際のbotの書き方とは少し異なる）
あと何日か、1日何記事ペースで書いたらいいのかをrubyで書くときに少し詰まったので記事に残す。
現在27記事投稿済み。
```Date.today```は2021年10月15日とする。

##完成形

- マジックナンバーは定数化する

```ruby
require 'date'

today = Date.today
limit = Date.new(2021, 12, 10)
target_articles_count = 100
articles_count = 27
off_days_count = 18

# 残りの日にちから土日祝の18日分を引く
remaining_days_count = (limit - today).to_i - off_days_count
per_day = ((target_articles_count - articles_count) / remaining_days_count.to_f).round(1)

p "本日までの記事投稿数は#{articles_count}記事になりました！！\n目標まであと#{target_articles_count - articles_count}記事、1日#{per_day}記事ペースで目標達成！\n引き続きがんばれ〜！"
```

##残り何日かを求める

```ruby
today = Date.today
limit = Date.new(2021, 12, 10)
remaining_days_count = (limit - today).to_i
p remaining_days_count
#=>56
```

to_iしないと分数の形になる

```ruby
remaining_days_count = limit - today
p remaining_days_count
#=>56/1
```


##
勤務シフト表の作成 基礎(4)【GLPK】

[**<--目次へ**](https://qiita.com/ki073/items/508b3c20eb03078703dd)

2021.10.16 変更あり

[勤務シフト表の作成 基礎(3)]
(https://qiita.com/ki073/items/f321a5ad33b8d385a2ea)からの続きです。そちらも合わせてご覧ください。

先に書いた「shift11.mod」は業務の割り当て回数は設定通りにできますが、日程的に偏りがでる可能性がありますので、その対策です。

少し大きなデータ（下記）を用意しました。「勤務シフト表の作成 基礎(1)」のコメント欄あるデータを使用させていただきました。

(2021.10.16 変更)

## 業務の割り当てが分散するように

ある連続した期間に業務Aと業務Bを１回ずつ割り当てる制約を加えてみました。
連続した5日間(period1の値)に1回(maxFreqInPeriod1の値)を超えて割り当てないようにしています。「shift11.mod」の「solve;」の上に追加すると機能します。
業務ごとに別々に制約が適用されます。出勤希望が条件を満たさない(例えば5日間に2回の希望)があった場合でも希望を優先して割り当てられますが、自動的に割り当てる勤務については連続した5日間に1回以下を満たすようになっています。
period1やmaxFreqInPeriod1を変更すことで条件を変えることができます。
下の方に「最大連続勤務日数」の設定がありますが、必要があればそちらも変更してください。

```GLPK:period1
param period1 := 5;
param maxFreqInPeriod1 := 1;
s.t. constrainP1{s in Staff, d in firstDate..lastDate-(period1-1), b in Duty}:
    sum{d1 in d..d+(period1-1) : not (s,d1,b) in FixOnDuty}assignShiftSchedule[d1,s,b]+min(sum{d1 in d..d+(period1-1) : (s,d1,b) in FixOnDuty}1,maxFreqInPeriod1)<=maxFreqInPeriod1;
```

次が、業務を区別せずに連続した日に割り当てられない制約条件です。連続したした2日間(period2の値)に1回(maxFreqInPeriod2の値)以下になるように割り当てられます。業務を区別しないことを除いて上と同じです。

```GLPK:period2
param period2 := 2;
param maxFreqInPeriod2 := 1;
s.t. constrainP2{s in Staff, d in firstDate..lastDate-(period2-1)}:
    sum{d1 in d..d+(period2-1), b in Duty : not (s,d1, b) in FixOnDuty}assignShiftSchedule[d1,s,b]+min(sum{d1 in d..d+(period2-1), b in Duty : (s,d1, b) in FixOnDuty}1, maxFreqInPeriod2)<=maxFreqInPeriod2;
```

この２つを加えることで、両方の条件を満たすシフトが完成します。

条件を満たす組み合わせが見つからなかった場合には「LP HAS NO PRIMAL FEASIBLE SOLUTION」と出力されて止まってしまいます。period1などを小さくするなど条件を変えてみてください。

###使用したデータ

「shift11.mod」のdata;より下を以下に書き換えてください。

```
data;
# 期間の最初の日付と最後の日付
param firstDate :=  1;
param lastDate := 29;

# 業務名
set Duty := "業務A" "業務B";

# スタッフ名
set Staff := "Aさん" "Bさん" "Cさん" "Dさん"
    "Eさん" "Fさん" "Gさん"
;

# 日付、業務別の必要出勤人数 defaultを1とし、それ以外の場合は後で指定
param necessary_nStaff default 1 :=
;

# スタッフ、業務別の最大割り当て勤務日数
param max_nWorkingDays default 4 :=
"Eさん"  "業務A" 5
"Cさん"  "業務B" 5
;
# スタッフ、業務別の最少割り当て勤務日数
param min_nWorkingDays default 4 :=
;

# 休日希望
set FixOffDuty :=
("Aさん", *)
("Bさん", *) 
("Cさん", *) 4 6 11 18 25
("Dさん", *) 5 12 15 1927
("Eさん", *) 1 6 20 25 29
("Fさん", *) 1 5 11 19 22 26
("Gさん", *) 4 8 13 22 27
;

# 出勤希望
set FixOnDuty :=
("Aさん", *, "業務A")  2 12 16 22
("Aさん", *, "業務B")  4 13 18 26
("Bさん", *, "業務A")  6 13 20 26 
("Bさん", *, "業務B")  2  9 21 25
;

# 最大連続勤務日数
param maxContinuousWorkDay default 5:=
;

# 期間前の連続勤務日数
param daysFromOffDutyBeforeFirstDay default 0:=
;
```
GitHub Actionsを使ってGithub PagesにOpen APIのRedocドキュメントを出力したメモ

# 概要

Github Pagesがorganizationでprivateに対応していたので
業務で使ってるOpen APIの定義をpushされた時に自動デプロイするようにしてみました

swagger-viewerとかで見るのがめんどかったので

## ソースコード

https://github.com/novr/redoc-sample

## 構成

```bash
├── .github
│   └── workflows
│       └── gh-pages.yml
├── docs
│   └── index.html
├── package-lock.json
├── package.json
└── petstore.yaml : from https://github.com/OAI/OpenAPI-Specification/blob/main/examples/v3.0/petstore.yaml
```

## workflows

```yaml:gh-pages.yml
name: github pages

on:
branch
  push:
    branches: [ main ]

  # 手動実行できるようにするため
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
        with:
          ref: main # 手動実行時のブランチを指定しておく

      - uses: actions/setup-node@v2
        with:
          node-version: '16'
          cache: 'npm'

      - run: npm ci

      - name: bundle
        run: npm run bundle

      - name: pages deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docs
```

workflowが実行されると`gh-pages`ブランチが作られるので
[first-deployment-with-github_token@actions-gh-pages](
https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-first-deployment-with-github_token)を参考に

Settings > Pagesを開いて
![スクリーンショット 2021-10-15 16.56.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228438/37dde7fb-cca5-44b8-d903-4854db49fe89.png)

Sourceに`gh-pages`を設定する
![スクリーンショット 2021-10-15 16.57.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228438/7852b18f-ed01-4f8e-56f9-22620a791fb4.png)

再度workflowを実行する
![スクリーンショット 2021-10-15 17.00.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228438/4fd1d77a-d98b-9742-08f2-ffe73c67980f.png)

デプロイを確認する

https://novr.github.io/redoc-sample/

## つまった所

### npm install

```
      - uses: actions/setup-node@v2
        with:
          node-version: '14'
          cache: 'npm'

      - run: npm install -g redoc-cli

      - name: bundle
        run: redoc-cli bundle petstore.yaml
```

`npm install -g`したらいけるだと思ったらエラー

```
Error: Dependencies lock file is not found
```

### nodeのバージョン

https://github.com/actions/setup-node#usage

参考にして`node-version: '14'`にしたらエラー

```
internal/modules/cjs/loader.js:905
  throw err;
  ^

Error: Cannot find module 'react-is'
Require stack:
...
```

`node-version: '16'`にしました。
Terraform × GCP × GCS で急に Terraform が 400 エラーで実行できなくなった

## 前提
 - Terraform のバージョン `1.0.x 系`
 - 対象クラウド `GCP`
 - Backend Type: `gcs`

## 症状
Terraform が実行できなくなった

`plan` 実行結果

```
╷
│ Error: Error loading state: Failed to open state file at gs://xxx-terraform-state/hoge/default.tfstate: googleapi: got HTTP response code 400 with body: <?xml version='1.0' encoding='UTF-8'?><Error><Code>UserProjectInvalid</Code><Message>User project specified in the request is invalid.</Message><Details>User project specified in the request is invalid.</Details></Error>
│
│
╵
```

`terraform init -reconfigure` 実行結果

```
╷
│ Error: Failed to get existing workspaces: querying Cloud Storage failed: googleapi: Error 400: User project specified in the request is invalid., invalid
│
│
╵
```


## きっかけ、心当たり
`Catalina` -> `Big Sur` へのアップデート（...？）


## 調査
 - gcloud コマンドは実行できる（e.g `gcloud alpha storage ls` とか）
 - `gcloud config set project xxx` でプロジェクトは正しくセットされている 
 - `terraform init -reconfigure` で直るという記事が数件ヒットしたが、私は直らず
 


## 解消方法
`gcloud auth application-default login` を実行したところ、直った

ユーザーADC というものが期限切れになったから、っぽい。

[Backend Type: gcs - Terraform by HashiCorp](https://www.terraform.io/docs/language/settings/backends/gcs.html#running-terraform-on-your-workstation-)
ABC170 C - Forbidden List を解いた

https://atcoder.jp/contests/abc170/tasks/abc170_c

![abc170c_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/3b93ff42-568f-f6f6-a6f1-29431e2d771b.png)
![abc170c_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/617b26a2-a790-616f-80b3-f1a0544edd16.png)
![abc170c_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/3bf837d8-35d9-715c-c6fc-d33aa9432086.png)

**正とは限らない** らしいので、
マイナスも入れて for 文を回してみた。

```abc170c.py
X,N = map(int,input().split())
P = list(map(int,input().split()))
P = set(P)

num = float("inf")
ans = 0
for i in range(-201,201):
    diff = abs(X-i)
    if i not in P:
       if diff < num:
           ans = i
           num = diff

print(ans)
```
【JEST・React】JESTのSnapshot test（スナップショットテスト）の.snapファイルを更新するコマンド

# コマンド

フロント（JSX）への更新が入り、JESTのスナップショットテストがエラーになるようになった場合に、
下記のコマンドでスナップショットの.snapファイルを更新できます。

```zsh
jest --updateSnapshot
```

# この記事を書いた理由

フロントへの修正が入ってスナップショットテストがエラーになる度、Jestのスナップショットの更新ページで上記のコマンドを探すことを繰り返していたので、備忘録として書きました。

上記コマンド実行後、意図した通りに.snapファイルが更新されているか確認するのも大事だと思います。

上記コマンドをREADMEに書くなどすると、作業効率改善しそうです。

### 参考

[JEST｜スナップショットの更新](https://jestjs.io/ja/docs/snapshot-testing#%E3%82%B9%E3%83%8A%E3%83%83%E3%83%97%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%E3%81%AE%E6%9B%B4%E6%96%B0)
ABC168 C - : (Colon) を解いた

https://atcoder.jp/contests/abc168/tasks/abc168_c

![abc168c_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/eab32f65-1dd9-bce7-b3c7-f59f3bc4aef6.png)
![abc168c_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/f26f05c5-9430-ee08-2505-f7d983c96f61.png)
![abc168c_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/12c75ec9-6fba-9cd4-6aa7-fac1af35e34b.png)

ポイントは時針も分単位で動く事でしょうか。

```abc168c.py
from math import *

A,B,H,M = map(int,input().split())

degH = H*60*0.5+M*0.5
degM = M*6
deg  = abs(degH-degM)

ans = A**2+B**2-2*A*B*cos(radians(deg))
print("{:.9f}".format(sqrt(ans)))
```
laravelのコード読んでみた Facade編①

## はじめに
### 動機
laravelでログを書き込む時に、`Log::info('ログです')`のような感じでLogファサードを使用しているが、そのログをフォーマットして出力したいと思いリファレンスを見ていたところ、Monologフォーマッターの説明で、下記の記載があった。

独自で`Monolog\Formatter\HtmlFormatter`のようなクラスを作成し指定すればいけそうだと思ったが、formatterに指定したクラスがどのように呼び出されているかが気になった為、読めるところまで読んでみました。
(下書きが消えてしまった為、分割で投稿しようと思います、、、)

>monologドライバーを使用する場合、MonologLineFormatterがデフォルトのフォーマッターとして使用されます。ただし、formatterおよびformatter_with設定オプションを使用して、ハンドラへ渡すフォーマッタータイプをカスタマイズできます。

https://readouble.com/laravel/8.x/ja/logging.html

https://readouble.com/laravel/8.x/ja/facades.html

## 実際に読み始めてみる
今回は、任意の場所で `Log::info('ログ')`の形式で呼び出されたという想定で読んでいきたいと思います。
まず、`Log::info()`に関してですが、Logクラスのstaticメソッドの`info()`が呼ばれているのでクラスを見にいきます。

**表示するクラスは内容を全部記載すると長くなる場合があるので、必要な部分だけ抜粋して表示します。**

```php:Illuminate\Support\Facades\Log.php
namespace Illuminate\Support\Facades;

class Log extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'log';
    }
}
```

Logクラスには`info()`メソッドが存在しないので、親クラスであるFacadeクラスを見にいきます。
Facadeクラスにも`info()`メソッドが存在しません。
ですがマジックメソッドである、`__callStatic()`が定義されているので、処理はこちらに移ります。
詳しくは下記を参照してください。


>__callStatic() は、 アクセス不能メソッドを静的コンテキストで実行したときに起動します。

https://www.php.net/manual/ja/language.oop5.overloading.php#object.callstatic

```php:Illuminate\Support\Facades\Facade.php
<?php

namespace Illuminate\Support\Facades;

abstract class Facade
{
    /**
     * Handle dynamic, static calls to the object.
     *
     * @param  string  $method
     * @param  array  $args
     * @return mixed
     *
     * @throws \RuntimeException
     */
    public static function __callStatic($method, $args)// $method = 'info', $args = ['ログです']
    {
        // $instance を取得
        $instance = static::getFacadeRoot();

        // $instanceがなければ例外を投げる
        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        // 取得したインスタンスに対して、メソッドを呼び出す。
        // 今回で言うと、 $instance->info('ログです');
        return $instance->$method(...$args);
    }
}

```
ちなみに`...$args`という記述の`...`は、phpのスプレッド演算子になります。
詳しくは下記を参照してください。

https://www.php.net/manual/ja/functions.arguments.php#functions.variable-arg-list


ここまでで、軽くイメージは掴めたと思います。

`Illuminate\Support\Facades\Log.php`に対してstaticメソッドとして呼び出すことで、
Logファサードのメソッドを利用しているが、実際には親クラスである`Illuminate\Support\Facades\Facade.php`が取得（解決）したインスタンスに実装されているメソッドを呼び出していると言う感じです。

## まとめ
- ファサードがどんな感じでメソッドを実行しているかをなんとなく理解した
- Qiitaの下書きの自動保存がしっかりと機能しているかをしっかりと確認すること。(そもそもQiitaで直接書かない方が良いのかも、、、)

## 次回
- 次回はファサードがどのようにインスタンスを取得するかをまとめていく予定です。

https://qiita.com/arinco_/items/3e247b39dd0021281865
【Laravel】テーブルのidをUUID（ランダム）に変更しようとしてやめた時のメモ

## 状況

Laravel:6.2
DB:MySQL

会員制SNS風サイトの開発。
当初はuserテーブルとarticleテーブルの主キーを連番のidにしていたが
ユーザーや記事の個別ページのURLにidを使用するにあたり
主キーをUUIDに変更しようと思った。
（"example.com/user/3"みたいな感じで少ない会員数がurlの数字でバレるのに抵抗があった）

以下、かなり個人的な判断や事情が含まれるため参考になるか不明ですが
自分用のメモも兼ねて書かせて頂いています。

## やってみたこと

#### カラム型を変更しようとした

まず、テーブルに入ってるデータ消したくなかったので、
ロールバックはせずにカラム型変更用のマイグレーションファイルを作成して対応しようと思った。

カラム型を変更するには
doctrine/dbalというライブラリのインストールが必要らしい。
Laravel 6.* ではdoctrine/dbalの対応バージョンが3.* ではなく2.* らしいので
バージョン指定してインストールする

それとUUIDを使うのにもパッケージをインストールしなければならないので下記コマンド実行
`composer require goldspecdigital/laravel-eloquent-uuid:^6.2`
（バージョンはlalavelのバージョンに合わせて適宜変更する）

各インストールが終わったのでマイグレーションファイル作成して
`$table->uuid('id')->primary()->change();`
などとしてマイグレーションを実行するもエラー。

調べてみると、UUIDはchangeには対応してない様子。
カラム型変更はあきらめる。

#### テーブルを作り直そうとした

上記の通りカラム型変更では対応できなかったので
テーブルのデータを消して改めてmigrateしようと思った。

ロールバック後にマイグレーションファイルのuserテーブルとarticleテーブルを
`$table->bigIncrements('id');`
から
`$table->uuid('id')->primary();`
に変更してmigrateしてみた。しかしエラーが出た。

原因として、今回uuidにしようとしたuserテーブルのidは、articleテーブルのほうで
`$table->bigInteger('user_id')->unsigned();`
`$table->foreign('user_id')->references('id')->on('users');`
といった感じで関連付けされてるので、articleテーブルのuser_idカラムを
userテーブルのidと同じ形式（つまりUUID）にしなければいけないらしい。

ここで今更ながら、「本当にUUIDを使わなければならないのか？」と疑問に思う。


## UUIDの使用をやめた理由

改めて考えてみた結果、
uuidは長すぎる
5e4e81de-45ae-468b-b33c-4c6109ded561（こんな感じになる）
ので、urlに使うには不格好なのではないかと感じ始める。

また、
「ちなみにUUIDをプライマリキーにする人もいますが、
外部キーとして使用することもある場合は、別にUUID用のカラムを作った方が良きです。
リレーれション先でも128ビットの数値をデータとして持っておくのは非効率ですからね。」
という意見も目にし、主キーにUUIDを使用するのはやめることにする。

参考にさせて頂いた記事
https://qiita.com/howaito01/items/b529af6f4d3112b91818）

## 最終的な対応

主キーとは別にurl表示用のランダム値（に見える）のカラムを作ることにした。

そもそも
連番にしたくなかった理由は
「urlを連番の数字にすると、会員数や投稿数が簡単にわかってしまうから」
だったので

「idに9999をかけて16進数に変換した値」等でも充分だと思った。
一見では規則性はわからないし、idを元にしているので重複することもないため。

処理としては、
userなりarticleなりの新しいレコードを登録してからでないと、そのレコードのIDがわからないので
下記のような感じで、一度saveしてからdisplay_id（url表示用のランダム風id）を設定し、もう一度save。

```php:
$article->title = $request->title; 
$article->user_id = $request->user()->id;
article->save();
$article->display_id = "a-".dechex($article->id * 9999);
$article->save();
//dechexは10進数を16進数に変換する関数
```
ABC164 C - gacha を解いた

https://atcoder.jp/contests/abc164/tasks/abc164_c

![abc164c_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/7a849153-a360-fef5-8379-b95555b1e162.png)
![abc164c_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/ee8e7dc8-daf1-318f-9a2f-5981f3e762f6.png)
![abc164c_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/7426ab21-e041-31e4-e2ab-5258b35d1940.png)

set を使えば良いと思う。

```abc164c.py
N = int(input())
lis = []
for _ in range(N):
    S = input()
    lis.append(S)
print(len(set(lis)))
```

以下のような極解があった

```abc164c.py
print(len(set(open(0)))-1)
```

そもそも open とは??

https://qiita.com/neko_the_shadow/items/521d1361820c42547741

そしてどんな入力仕様なのか

https://qiita.com/kyuna/items/8ee8916c2f4e36321a1c

使う上での懸念点、問題点は?

https://teratail.com/questions/250555

自分の環境では上手く open(0) が動かなかったので、
こんなのがあるんだ位で、今回は留めておく。
今回のような問題であればスグに解けるので
優先順位は高くないと思った。
ABC162 C - Sum of gcd of Tuples (Easy) を解いた

https://atcoder.jp/contests/abc162/tasks/abc162_c

![abc162c_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/9905bae0-59da-d6f3-447c-01966e4d65a0.png)
![abc162c_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/573002de-3fcc-3d35-c322-70f4ccd282cd.png)

```abc162c.py
from math import gcd
K = int(input())

score = 0
for a in range(1,K+1):
    for b in range(1,K+1):
        for c in range(1,K+1):
            score += gcd(gcd(a,b),c)
print(score)
```
KotlinでCSVならkotlin-csvが素敵というお話

　さてCSV。自分で実装するには意外と複雑なのがCSV。

　データ内にダブルクォートを入れる場合はダブルクォートでエスケープするとか、ダブルクォートで囲ったら改行コードもデータにできるとか。

　そういう意外と面倒なCSV、使いやすいライブラリはございませんでしょうかと、色々使ってみたところ[kenta.koyama](https://github.com/doyaaaaaken)さんの[kotlin-csv](https://github.com/doyaaaaaken/kotlin-csv)が凄く素敵で素晴らしかったというお話。

```
About
Pure Kotlin CSV Reader/Writer
```

　**Pure kotlin!**　ああ、なんと美しい響きでしょう。

# ではbuild.gradleから

```build.gradle
implementation 'com.github.doyaaaaaken:kotlin-csv-jvm:1.1.0'
```

## 今回のテストコードを動かすための依存関係
```build.gradle
testImplementation "org.spekframework.spek2:spek-dsl-jvm:2.0.17"
testRuntimeOnly "org.spekframework.spek2:spek-runner-junit5:2.0.17"

testImplementation 'com.google.truth:truth:1.1.3'
testImplementation 'com.google.truth.extensions:truth-java8-extension:1.1.3'
testImplementation 'com.google.testing.compile:compile-testing:0.19'
```

# 実際のテストコード

```CsvReadTest.kt
import com.github.doyaaaaaken.kotlincsv.dsl.csvReader
import com.google.common.truth.Truth
import org.spekframework.spek2.Spek
import org.spekframework.spek2.style.specification.describe
import java.nio.charset.Charset
import java.nio.file.Path
import kotlin.io.path.createTempDirectory
import kotlin.io.path.createTempFile
import kotlin.io.path.writeText

class CsvReadTest: Spek({
    describe("kotlin-csvを使ったCSVファイル読み込み") {

        // 取り敢えず文字列をパースして表示してみる
        context("シンプルに読み込みと表示") {
            val data = """
                ナリタブライアン,マヤノトップガン,"カンマ,入り","改行
                入り"
                "キタサンブラック","コントレイル",,"ダブル""クォートいり"
            """.trimIndent()

            csvReader().readAll(data).forEach {
                println(it.joinToString(";"))
            }
        }

        // 文字コード指定でMS932のファイルを読み込んで見る
        context("MS932ファイル読み込みテスト") {
            temporaryDirectory { dir ->
                csvReader {
                    charset = "MS932" // 文字コード指定を試すため今回は敢えてMS932で読み込み
                }.readAll(
                    createTempFile(dir).apply {
                        writeText("""
                            ナリタブライアン,マヤノトップガン,"カンマ,入り","改行
                            入り"
                            "キタサンブラック","コントレイル",,"ダブル""クォートいり"
                        """.trimIndent(), Charset.forName("MS932"))
                    }.toFile()
                ).joinToString("[CRLF]") {
                    it.joinToString(";")
                }.let {
                    // 検証
                    Truth.assertThat(it).isEqualTo("""
                        ナリタブライアン;マヤノトップガン;カンマ,入り;改行
                        入り[CRLF]キタサンブラック;コントレイル;;ダブル"クォートいり
                    """.trimIndent())
                }
            }
        }
    }
}) {
    companion object {
        // こういうの探せばありそうですけどね, JUnit使ってた時は@TempDirとか使ってましたね
        fun temporaryDirectory(closure: (Path)->Unit) {
            val dir = createTempDirectory()
            runCatching {
                closure(dir)
            }.onFailure { throw it }
             .also { dir.toFile().deleteRecursively() }
        }
    }
}
```

　色々試した上で、こちらを使わせていただくことになりました。

　Pure Kotlin! **世界の全てをkotlinで** が合言葉の私の胸に、ズンズンズン、クリティカルヒット（やった！）でございしたとさ。
ABC161 C - Replacing Integer を解いた

https://atcoder.jp/contests/abc161/tasks/abc161_c

![abc161c_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/034fd462-5db2-23f5-c626-705b7994f8eb.png)
![abc161c_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/9324a006-74f9-efc5-9b0e-fa6049a9b78c.png)
![abc161c_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/737344/1b725bd2-2a95-9440-ba7e-60b5e2dfc2ac.png)

条件にある 10^18 はさすがに for で
全部試すわけにはいかない。

```abc161c.py
N,K = map(int,input().split())

num = N%K
if num == 0:
    print(0)
else:
    print(min(num,abs(num-K)))#23ms
```

久しぶりにやってみた。
ちょっと最適化できるようになった。

```abc161c.py
N,K = map(int,input().split())
base = N%K
print(min(base,abs(base-K)))#26ms
```
[JavaScript]  無名関数とラムダ式

# 無名関数とは
**無名関数**（匿名関数）とは、関数名なしで定義できる関数。記述が短くて済むというメリットがある。

```javascript:無名関数
const func = function (a,b){
    return a + b;
}
```
この無名関数をより簡潔したのが<font color="Red">ラムダ式</font>。

#ラムダ式の書き方
無名関数では「**function (引数) {処理内容}**」と書く部分を**ラムダ式**では「**(引数) => {処理内容}**」と定義できる。
上記の無名関数のコードをラムダ式で書くと以下となる。

```javascript:ラムダ式
const func = (a,b) => {
    return a + b;
}
```
また、「**=>**」を使った書き方の関数を「<font color="Red">アロー関数</font>」とも呼ぶ。

さらに引数や処理によっては、より短く記述することもできる。

####■ 引数が1つの場合
引数が1つの場合、**丸括弧()が省略可**。

```javascript
num => {
    console.log(num);
}
```

####■ 処理が1つの場合
処理が1つの場合、**中括弧{}が省略可**。

```javascript
() => console.log('Hello!');
```

####■ 処理がreturn文のみの場合
処理がreturn文のみの場合、**returnと中括弧{}が省略可**。

```javascript
(a,b) => a + b;
```
【CSSメモ】リストの箇条書きの記号を設定したい時

list-style-type: ;

list-style-type（リスト・スタイル・タイプ）
種別：プロパティ
役割：HTMLにあるリストの種類を設定。リストの箇条書きの記号を決めることが出来る。
設定できるリストの記号は↓

```none:記号なし```
使用例：list-style-type:none;

```disc: ●つまり黒丸がつく```
使用例：list-style-type:disc;

```circle:○。つまり白丸```
使用例：list-style-type:circle;

```square(意味は死角): ■がつく```
使用例：list-style-type:square;
AppLinksのサポート先を複数登録した場合の挙動

# 何が問題か

曖昧さ回避ダイアログが出てしまう。
![Screenshot_2021-10-15-16-20-37-857_android.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/e5737e50-ea22-6aec-9a76-3f27eb11ec64.jpeg)


AppLinksを設定済みで、SHA256証明書はyyy.page.linkと、xxx.page.linkに連携してます。

Android8の場合はアプリ設定画面にて"Open supported links"の設定がデフォルトで"open in this app"に設定されるのですが、
Android11の場合は"ask every time"となってしまうようです。

## Android11

毎回ダイアログで開くアプリを聞かれる
![Screenshot 2021-10-14 133304.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/d8f5d9f7-7cbc-ab49-ee35-1e2db736b7d6.jpeg)


## Android8.0

これならサポートされたリンクをダイアログなしで開いてくれる
![Screenshot 2021-10-14 115042.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/9bca9360-9eab-b512-2f9e-ad2cba6056d5.jpeg)

## 原因

intent-filterに2ドメインをサポート対象としたときに
Android11についてはデフォルトでサポートリンクを開かず毎回ユーザに聞く設定になってしまうようでした。

両サポートではなく、stgとprodで一方のみのhostを指定しサポートすることで、
デフォルトでサポートリンクを開けるようになりました。

### 改善前（両方サポートした場合）

```xml
<data
  android:host="xxxxstg.page.link" 
  android:scheme="https"/>

<data
  android:host="xxxxapp.page.link" 
  android:scheme="https"/>
```

### 改善後(動的にホストを変えてサポート)

```xml
<data
  android:host="@string/dynamic_link_host" 
  android:scheme="https" />
```
Cypressの環境構築と自動テスト実行方法について

#はじめに
アウトプットが苦手な自分が練習の一環として記事を書いてみる。

第八回目です。

前回の投稿から約1か月経ってしまいました。
~~正直書くネタが浮かばなかったです。~~

最近、業務でCypressを触る機会があり記事のネタになりそうだったので、
今回はCypressの`環境構築とテストの実行方法`について書いていきます。

# Cypressとは？
Cypressとは、WebのUIテストを自動化できるツールです。Javascriptでテスト用のスクリプトを書いて使用します。

ブラウザの自動テストは「Selenium」が有名のようですが、あちらはテストを書くこと以外にもスクレイピング等の用途で使うことができるようです。

Cypressはテストを書くことに特化しており、要素の表示を待ったりページ遷移の完了を待ったりといった機能が標準であります。
他にも「動作が軽い」「環境構築が楽」「CI連携が楽」といった特徴があります。

# 環境
- Windows 10 Pro 64ビット
- Git Bash (Cypressインストールに使用)
- node.js 10.17.0

# インストール
※事前にnode.jsをインストールする必要があります。

テストを実行するプロジェクトのディレクトに移動し以下のコマンドを実行します。

```
$ npm install cypress --save-dev
```
3～5分程度でインストールが完了します。

# Cypress起動
Cypressの本体はnode_modulesのbinファイルにあります。
以下のコマンドでそれをopenします。

```
$ ./node_modules/.bin/cypress open
```

コマンドを実行すると以下のようになります。
- Cypressの管理画面が表示される
- `プロジェクト/cypress`フォルダが生成され、配下にサンプルのテストスクリプトが配置される **※初回起動時**
- `プロジェクト/cypress.json`が生成される **※初回起動時**

管理画面には` cypress/integration `配下にあるテストスクリプトの一覧が表示されます。

![WS000001.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1783771/fa3f40b7-2aa0-5581-50dc-dbce80cc1be0.jpeg)

# テスト実行
管理画面で実行したいスクリプトを押下するとブラウザが立ち上がり以下のようにテストが実行されます。
![WS000003.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1783771/2bfc25ef-9981-e59f-4f00-bedbf6aa6b68.jpeg)

# CLIで実行
CypressはGUI以外にもCLIから実行することができます。
以下のコマンドから実行することができます。

```
$ npx cypress run -s cypress/integration/*
```
CLIで実行するとheadlessモードで起動されるため、ブラウザが立ち上がらずに実行します。
その代わりに`cypress/videos`配下にエビデンスとしてテスト実行時の動画を録画してくれます。

# フォルダの役割
最後にcypress配下に生成される各フォルダの役割についてまとめます。

## integration
テストスクリプトを格納するフォルダです。
ここに配置したフォルダやファイルがCypressの管理画面に表示されます。

## plugins
Develop環境やStaging環境など、環境変数を記載する際に使用します。

## fixtures
テストで使用する共通パラメータを記載したファイルを格納します。
テストスクリプトで`cy.fixture('ファイル名')`と書くことでパラメータを取得できます。

## screenshots
スクリーンショットした画像が格納されるフォルダです。
テストスクリプトで`cy.screenshot()`と書くことでキャプチャすることができます。

## videos
CLIで実行した際に実行時の動画が格納されるフォルダです。

## downloads
テストでダウンロードしたファイル等が格納されるフォルダです。

## supports
カスタムコマンドが作成できるフォルダです。
`cypress/support/commands.js`内に記載することで使用できます。

# まとめ
以上です。

Cypressはレイアウト系の目視確認や入力チェックの数が多い試験では使用することでヒューマンエラーを回避することができるため試験の工数が大幅に削減することができると思います。

また環境構築が楽でUIもわかりやすく入りやすいと思いますので、
一度Cypressを試してみてはいかがでしょうか。

参考になれば幸いです。

# 参考サイト
https://docs.cypress.io/guides/overview/why-cypress

https://microayatron.com/setting-up-cypress-environment/
WEBブラウザを自動操作する方法(ノーコード)

様々なツールがクラウドへと移行している現在、ブラウザ上で行う作業というのが非常に多くなってきました。
そんな中、WEBブラウザ上で行っている定常的な作業を自動化したり、ボリュームのある入力作業を一括で行いたいというニーズが増えています。
これまでWEBブラウザの自動化というと、PythonとSeleniumを使った自動化が主流でした。しかし、環境構築が必要だったり、プログラムの勉強が必要だったりしますので、目的の自動化を実現するまでに時間がかかります。

# クラウド型RPAという選択肢
近年、この分野で注目を集めているのが、『クラウド型RPA』です。一般的なRPAツールと異なり、ブラウザを自動操作するロボットを作成できるサービスの事を指します。
クラウド型RPAは様々な物が提供されていますが、基本的にはサービス側が用意したクラウドサーバ上でロボットが稼働するため、こちらで環境構築する必要がありません。また、コードを書く必要のないサービスもあり、プログラム未経験者でもブラウザ操作の自動化を簡単に実現できるようになっています。

# おすすめのサービス
とりあえず、ブラウザ操作を自動化したいという方におすすめなのが『クラウドBOT』(https://www.c-bot.pro/) です。
![メインイメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459104/70c4dc90-9852-5698-41be-f03f643c53f5.png)
クラウド上に展開したブラウザを自動操作するという仕組みを採用しているので、環境構築が不要です。また、ノーコードを掲げており、実際にプログラムを書く必要はありません。
ブラウザの画面を操作しながら操作を選んで記録していくようなインターフェースになっています。
![イメージ1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/459104/237d1f64-8def-df34-3c7c-346bac5afbd3.png)

## 良い点
### 自動化構築までが早い
ロボットの作成から実行まで全てがクラウドサービス上で可能になっていますので、会員登録(無料)を行えば、すぐに使い始める事ができます。ロボットも比較的簡単に作成できるので、目的を実現するまでの道のりが短いです。

### 無料で使える
クラウドBOTはフリープランがあり、無料で使うことができます。ロボットの実行時間が月30分までになっていますが、簡単な自動化であればフリープランで十分利用できると思います。

# まだまだ開発を継続中
クラウドBOTは2021年10月現在も機能開発を行っており、これからどんどん機能強化されていくと思います。現時点では画像やCSVなどのファイルの取り扱いが開発中となっています。

# ファイル操作の自動化に対応(2022年1月)
これまで、テキストデータのコピーとペーストが中心でしたが、CSVファイルや画像ファイルを扱えるようになりました。無料プランで使用できます。
画像も含めて情報のコピー＆ペーストができるようになりましたので、求人情報や商品情報の自動転載などに活躍できそうです。

https://www.c-bot.pro/ja/content/file_operation

# 社内設置型のWebシステムが自動の操作対象に(2022年6月)
アイ・オー・データ機器の提供するLANDISKを経由して、社内に設置されているWebシステムや社内IPからのみ接続を許可されているクラウドサービスなども自動操作できるようになりました。

https://www.c-bot.pro/ja/content/landisk



# 最後に
今回はWEBブラウザの自動操作を簡単に実現したいという方向けにサービスの紹介記事を書いてみました。integromatとクラウドBOTを利用して様々な業務の自動化を進めていますので、具体的な使い方などは今後少しずつ記事に残していきたいと思います。
TestCafe入門

## はじめに

はじめてTestCafeを触ったので、その環境構築の方法と簡単なソースコードを説明していきます。
ただし私自身あまりJavaScriptには精通しておらず、とりあえず動くものを書きましたので、あしからず。

### 前提条件

今回使用するツールは下記の通りです。
Visual Studio Code（以下VSCode）とNode.jsについてはすでにインストール済みとします。


|        Tools       | Version      |
|:-------------------|:------------:|
| Windows            | 10           |
| VSCode             | 1.60.2       |
| Node.js            | 12.18.2      |
| TestCafe           | 1.16.1       |

テスト対象は[HOTEL PLANISPHERE](https://hotel.testplanisphere.dev/ja/plans.html)を使用します。

## TestCafeとは

TestCafeは、E2Eでブラウザテストを自動化するためのフレームワークです。

### 特徴
- オープンソースのため、無料で使用可能
- 言語はJavaScriptやTypeScript
- 環境構築が容易
- 待機処理などをよしなにやってくれる


## 環境構築

特徴にも書いたように、TestCafeの環境構築はとても容易です。
コマンドプロンプトで下記コマンドを実行してください。

>  npm install -g testcafe

図のようにインストールされれば、TestCafeの環境構築は完了です。
![testcafe install_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121319/332ee031-6bdc-87c5-4cca-c544b9f22683.jpeg)

念の為、ちゃんとインストールされたかを確認しましょう。
下記のコマンドを実行してください。

> testcafe -v

これでバージョンが表示されれば正常にインストールされています。

## テスト実行

### テストコードを書いてみる

今回はログインテストをしてみます。
手順は下記のとおりです。
1. ログインページに移動する
2. メールアドレスとパスワードを入力する
3. ログインを押下する
4. マイページに遷移しているかを確認する

これを実装すると

```javascript:test.js
import { Selector } from 'testcafe';

// サイトの指定
fixture('HOTEL PLANISPHERE')
    .page('https://hotel.testplanisphere.dev/ja/plans.html');

//テストケース1
let testcase1 = 'ログインテスト';
test(testcase1, async t => {
    //要素の取得
    const loginButton = await Selector('#login-holder');
    const email = await Selector('#email');
    const password = await Selector('#password');
    const login = await Selector('#login-button')

    //テスト開始
    await t
        //それぞれの動作
        .click(loginButton)
        .typeText(email, 'sakura@example.com')
        .typeText(password, 'pass1234')
        .click(login);
     //確認
    await t
        .expect(Selector('body > div > div:nth-child(1) > div > h2').innerText).eql('マイページ');
});

```

テストケースを増やしたい方はテストケース1の後ろにlet testcase2 = ~~のように続ければOKです。
テストケース

### テスト実行

テスト実装ができたら、次は実行をしてみましょう。
今回はGoogle chromeで実行をしてみます。
下記のコマンドを打ってみましょう。

> testcafe chrome ./test.js

コマンドを実行するとブラウザが立ち上がって、テストが実行されます。
![chrome.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121319/fdda1786-d5eb-69a6-1004-a94667a4b15f.png)

テスト実行が終わったあとはテスト結果が表示されると思います。
下記図はPassした場合です。
![テスト結果_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121319/c94eb702-33b6-fd4d-0c27-688843a563bb.jpeg)

テストがFailした場合は、下記のようにどこで失敗したのかがわかります。

![テスト結果-fail-_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121319/b7679770-dae7-8704-9aad-33c67a5b0e23.jpeg)


## おわりに
さらに詳しいテストコードを書きたい方は[TestCafeのリファレンス](https://testcafe.io/documentation/402666/reference/test-api/selector)を参照してみてください。
pybulletをインストールするときのエラーと対処法

pybulletをインストールする機会があり、その際のエラーの対処法をメモしておく。
#環境

- macOS Big Sur バージョン11.6
- Python 3.8

#やったこと

###pipでインストール

```
pip install pybullet
```
するとエラーと同時にこんな表記が

>You have not agreed to the Xcode license agreements, please run 'sudo xcodebuild -license' from within a Terminal window to review and agree to the Xcode license agreements.

どうやらターミナルから規約に同意してくださいということらしい。

###Xcode licenseに同意する

```
User$ sudo xcodebuild -license
```
すると
>You have not agreed to the Xcode license agreements. You must agree to both license agreements below in order to use Xcode.

>Press the 'return' key to view the license agreements at '/Applications/Xcode.app/Contents/Resources/English.lproj/License.rtf'

エンターキーを押すと規約が表示されるのでスペースキーで最後まで移動。最後に同意を求められるのでagreeと入力する。

もう一度 `pip install pybullet`でインストール完了。
ラズパイにphp導入したのにphpmyadminがブラウザ起動しない件（非IT人材のサーバー）

ラズパイでサーバー作ろう計画を立てています。
ずっととん挫させていた。。。

MarinaSQLを入れてさぁ始めるぞ、という時に
phpmyadminが開かないのだ。

2週間ほどもたもたしながら、来週の月曜までのタスクが片付いたので本日取り組んでいたところ
1時間ほど検索したり試したりと、のたまわった結果
下記URLの内容があっているな。ということが判明。
https://www.out48.com/archives/924/
https://db.danman.jp/phpmyadmin-raspberry-pi-os/

2つのURLは基本同じことを書いているが、個人的一つ目が分かりやすかった。
しかしそれは、単純に自分がディレクトリを間違っただりする。

phpMyAdminをブラウザ上で立ち上げるようにするための手順は
非常に簡単なので、もし自分に次があった時用に記録する。

条件
Rpi OS・Apache・marinaSQL・PHPインストール済み。
（それぞれ2021年時点で推奨のバージョン）
手順
１、ターミナルを立ち上げる。
２、Apache2のディレクトリに移動し、confファイルを編集する。

```
pi@raspberrypi:~$
pi@raspberrypi:~$ sudo -su  //root開けばいい。
root@raspberrypi:~$
root@raspberrypi:~$ cd /etc/apache2 //Apacheフォルダにアクセス
//（コピーしてバックアップした方がいいかも？）
```
３、confファイルを編集する手順。

```
root@raspberrypi:/etc/apache2# ~$ sudo vi apache2.conf
ここから開いた状態
(挿入モードにして一番下の行に書きコメントを入力）
（shift + g　⇒　i カーソル動かして一番下であるかチェックしとく）
Include /etc/phpmyadmin/apache.conf
```
これで、apache動いてるときはphpmyadminが読み込まれる。
つまりブラウザ上でphpmyadminが使われるということ。
confファイルって便利ね。


４、最後に、下記コマンドを入力してENTER。（Apacheの再起動）

```
sudo /etc/init.d/apache2 restart
```

http://[localhost]/phpmyadmin/
[]の中はラズパイのIPでも良し。

以上である。
2日ほど前から「Property store is required.」でGASの実行が失敗したので原因を調べてみた

二日ほど前からGASでトリガー実行しているツイッター検索のスクリプトが以下のエラーで実行失敗するようになりました。

![err.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35574/0de46f55-8f5d-47c0-d934-e73ecf13c5f9.jpeg)

そこで原因を調べてみた話です。

## １．スクリプトで使っていたライブラリ

私のスクリプトの場合は、ツイッター検索するためにOAuth1というライブラリを使っていました。

[GitHub - googleworkspace/apps-script-oauth1: An OAuth1 library for Google Apps Script.](https://github.com/googleworkspace/apps-script-oauth1)

## ２．やってみたこと（１）

ネットをググると、別のライブラリを使っている方の記事で以下のように書かれていました。

```
スクリプトエディタのChrome V8を搭載したApps Scriptランタイムを無効化することで解決する。
```

早速以下のように無効化してみるとうまく実行できました。

![muko.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35574/ce341e47-f4ee-a997-7e50-36366b76e7ac.jpeg)

## ３．やってみたこと（２）

２でうまく実行できましたが最新の「Chrome V8を搭載したApps Scriptランタイム」で動かない理由がわからなかったので、OAuth1のバージョンを調べました。

私のスクリプトで使っているOAuth1のバージョンは16でしたが、最新の18を見ると…

![ver.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35574/096d67ed-df79-c931-c893-5299f6b4d54d.jpeg)

Chrome V8でのバグが修正されていました…汗

## ４．結局

* スクリプトエディタのChrome V8を搭載したApps Scriptランタイムを再度有効化
* OAuth1のバージョンは18を選択

これで、普通に動きました。

もしかしたら、同じ状況の人がいるかもしれないので記録に残しました。

多分いないと思いますが…
React カスタムフック 覚書

# React カスタムフック覚書

```ts
////// useCustomHook.ts
import { useReducer, useCallback } from 'react';

type State = {
  name: string;
  isLoading: boolean;
  errorMessage?: string;
};

type Handlers = {
  onSubmit: (name: State['name']) => void;
};

enum ActionTypes {
  OnSubmit = 'on-submit',
  OnSuccess = 'on-success',
  OnFailure = 'on-failure',
}

type Action =
  | { type: ActionTypes.OnSubmit, name: State['name'] }
  | { type: ActionTypes.OnSuccess }
  | { type: ActionTypes.OnFailure, errorMessage: Required<State['errorMessage']> };

const reducer: React.Reducer<State, Action> = (state: State, action: Action) => {
  const patch = (diff: Partial<State>): State => ({ ...state, ...diff });
  switch (action.type) {
    case ActionTypes.OnSubmit:
      return patch({
        isLoading: true,
        effect: {
          cmd: Cmd.hogeHoge, // api叩いたり
          args: [action.name],
          successActionCreator: () => ({ type: ActionTypes.OnSuccess }),
          failureActionCreator: (error: Error) => ({ type: ActionTypes.OnFailure, errorMessage: error.message }),
        },
      });
    case ActionTypes.OnSuccess:
      return patch({
        isLoading: false,
        effect: undefined,
      });
    case ActionTypes.OnFailure:
      return patch({
        isLoading: false,
        effect: undefined,
        errorMessage: action.errorMessage
      });
  }
};

const createInitialState = (name: State['name']): State => ({
  name,
  isLoading: false,
});

const useCustomHook = (name: State['name']): [State, Handlers] => {
  const [state, dispatch] = useReducer(reducer, name, createInitialState);
  const handlers: Handlers = {
    onSubmit: useCallback((name) => {
      dispatch({ type: ActionTypes.OnSubmit, name });
    }, []),
  };
  return [state, handlers];
};

export default useCustomHook;

```

```ts
////// useCmd.ts 頭良すぎでわけわからん このコードは別の方が作成
import { useEffect, Dispatch } from 'react';

export type Effect<F, Action> = F extends (...args: infer Params) => Promise<infer Result> ? {
  cmd: F;
  args: Params;
  successActionCreator: (result: Result) => Action;
  failureActionCreator: (e: Error) => Action;
} : never;

const useCmd = <F, Action>(dispatch: Dispatch<Action>, effect?: Effect<F, Action>) => {
  useEffect(() => {
    if (effect) {
      effect.cmd(...effect.args)
        .then(result => dispatch(effect.successActionCreator(result)))
        .catch((e: Error) => dispatch(effect.failureActionCreator(e)));
    }
  }, [dispatch, effect]);
};

export default useCmd;
```
CocosPodsで導入した特定のライブラリの'IPHONEOS_DEPLOYMENT_TARGET'を変更する

# 環境
- Xcode 13.0
- CocosPods 1.10.2

# 概要
CocoaPodsで導入したライブラリの `IPHONEOS_DEPLOYMENT_TARGET` を変更する際、


```rb:Podfile
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '{任意のOSバージョン}'
    end
  end
end
```

上記のような対応をすることがあるかと思いますが、全てのライブラリに変更が及んでしまいます。

そこで今回は、特定のライブラリのみに別の値を設定したい場合の対応を紹介します。

# 対応方法
`Build Settings` の `Product Name` を判定して分岐処理を加えると対応できます。

```rb:Podfile
post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config| 
      if config.build_settings['PRODUCT_NAME'] == '{特定のライブラリ名}'
        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '{任意のOSバージョン}'
      else
        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '{任意のOSバージョン}'
      end
    end
  end
end
```

もしくは、全てのライブラリの変更が必要ない場合、`target.name` を判定して処理をするのもいいかもしれません。

```rb:Podfile
post_install do |installer|
  pods_target = installer.pods_project.targets.find { |target| target.name == '{特定のライブラリ名}' }
  pods_target.build_configurations.each do |config|
    config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '{任意のOSバージョン}'
  end
end
```
[SQL学習③] WHEREに関連した演算子　（LIKE・BETWEEN・IN・ANY・ALL）

<h2>WHERE句は　真か偽か</h2>
Where句には、「真か偽になる式」しか書けない。
テーブルを参照するにあたって、
一行ずつ順番に「真」なのか「偽」なのかで処理を分けていくから。


<h2>NULLの判定</h2>

![スクリーンショット 2021-10-13 9.27.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/f3c0fd5c-398b-1151-ab6c-15722e012739.png)

アイスのジャンルは、NULLです。
NULLとは「未定義」「何もないこと」です。

NULLの判定は以下の通りになります。


```
SELECT *
FROM 商品表
WHERE ジャンル　IS NULL

・・NULLであることを判定する
　式　IS NULL
・・NULLでないことを判定する
式　IS NOT NULL
```

正直、NULLかどうかの判定を比較演算子を使ってやりたくなってしまいます。
（ジャンル　＝ NULL みたいな）

<h2>比較演算子＝でNULLの判定はできない！！！！</h2>
比較演算子は「値と値を比較するもの。」
そのため、「値ではないNULL」は比較できない。


<h2>Like演算子</h2>
部分一致の検索ができます。
![スクリーンショット 2021-10-13 9.27.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/f3c0fd5c-398b-1151-ab6c-15722e012739.png)

```
SELECT　＊ FROM 商品表
//ケーキの前後に何かついてても良い（部分一致）
WHERE 商品名　LIKE '%ケーキ%'

その他
'%ケーキ'は　チーズケーキを取ってこれる
'ケーキ%'はチーズケーキを取ってこれない。（ケーキスペシャルは取ってこれる）
'_ケーキ'は、頭に任意の１文字がある文字列を取ってくる。
```

<h2>BETWEEN演算子</h2>
ある範囲に値が収まってるかどうかを判定できます。
商品表から200円以上、500円以下のものを取得します。

```
SELECT *
FROM 商品表
WHERE 販売価格　BETWEEN 200 AND 500
```

<h2>IN / NOT IN　演算子 </h2>
合致するもの、しないものを一度に持ってこれます。

![スクリーンショット 2021-10-13 9.27.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/f3c0fd5c-398b-1151-ab6c-15722e012739.png)

商品名が　プリン　と　アイスのものを引っ張ってきます。

```
SELECT *
FROM 商品表
WHERE 商品名　IN　（'プリン','アイス'）

//　NOT INで書くと、
WHERE 商品名　NOT IN　（'チーズケーキ','くるくる','フルーティー'）
```

<h2>ANY・ALL　演算子</h2>
いまいち、使い所がわからないなあという感じがします。

```
・・(　　)内のどれかと一致するものを持ってくる
販売価格　IN （200,350,500)

・・（　　）内のどれかより小さいものを持ってくる
販売価格　< ANY （200,350,500)

・・（　　）内の全部より小さいものを持ってくる→１００円のアイスが該当
販売価格　< ALL （200,350,500)
```

<h2>複数の条件</h2>
複数の条件式を用いて判定を行いたい場合には、
AND OR演算子を使用します。

![スクリーンショット 2021-10-13 9.27.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1633266/f3c0fd5c-398b-1151-ab6c-15722e012739.png)

```
UPDATE 商品表
SET    ジャンル　＝ 'ケーキ'
WHERE  商品名　　＝'チーズケーキ'
AND    商品ID = 1
```
フロントエンド業務でも便利なshellコマンド


たまには小ネタ系です。

フロントエンドだと`ls`　とか `cd` といった基本的なコマンド以外shell使わないな〜という人も多いかもしれませんが、
覚えておくと作業効率どどんと上がるものがたくさんあるので、私自身よく使うものをいくつか紹介します。

## ターミナルで見ている階層をfinderで開く
リポジトリの画像をギャラリーで一気に見たい!というケースって結構ありませんか？
そういった場合はFinderで見ちゃった方が早いですよね。

```
cd 該当のリポジトリ
open .
```

これで毎度ディレクトリを探して開かなくてOKです。


## ディレクトリ階層をreadmeに一瞬で書き出したい!

ディレクトリ階層を書くときに出す謎の記号たち、手動で入力するよりコマンドの方が早いです。
https://qiita.com/kanuma1984/items/c158162adfeb6b217973
treeを上記の手順でインストールしたら、

```
$ tree -L 3 
```
と入力するだけで3階層下までのディレクトリ階層を出力してくれます。


## コミットしてない分全部消し去りたい
特にGUIツールを使われている方は、消えないuntrackのファイルに悩んだことはありませんか？
（Sourtreeでいう？マークのファイルです）

```
$ git clean -fd
```

で諸々滅却できます。使用注意です。

## ファイル名を指定してどこにファイルがあるか探したい

```
$ find 検索するディレクトリ -name 検索するキーワード
```

これでどこにファイルのパスがあるかをすぐに調べることができます。

## コマンドの実行計測をしたい
次々に新しい技術が出てくるフロントエンド業界では、実際に使ってみて計測する、ということが大事になってきます。

```
$ time 何かしらのコマンド
```

で、コマンドの実行速度を計測することができます。

例えば、

```
$ time npm run build
```

といった具合にコマンドを入力すると、

を実行すると、

```
npm run build  21.47s user 3.57s system 81% cpu 30.832 total
```

と出力されます。

## システム全体の負荷を確認する
パソコンがやたら重い...! といった際に現状を把握できます。

```
$ top
```
【JavaScript】変数と参照の振り返り⑧　分割代入と値・参照の比較


#はじめに

Udemyの[【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)の講座の振り返りです。

前回の記事

https://qiita.com/nao0725/items/d9ac00ba64da2f1955a7

#目的

* 変数についての理解を深める

#本題
###1.参照と分割代入

````基本構文.
let{a,b} = object;
````

オブジェクトから特定のプロパティを抽出して宣言を行う場合に使われる。

この場合、aとｂがオブジェクトの初期値として扱われる。


####例

````javascript

const a = {
  prop: 0
}
// 分割代入
// プロパティの名前と変数の名前は一致させる
let { prop } = a;
prop = 1;
// ここで出力されるのは0と1となる
// 分割代入により変数propの値に1が代入されている
// 最初に定義した{prop}には影響を受けない
console.log(a, prop);
``````

####関数と分割代入

````javascript
// 関数fnを定義
function fn(obj){
  // 分割代入
  let { prop } = obj;
  // 変数の値を変更
  prop = 1;
  // 結果を出力
  console.log(obj, prop);
}
// { prop : 0}と1と出力される
fn(a);
````

関数内で特定のプロパティを使用したい場合は以下のように書き換えることができる

```javascript
// 関数fnを定義
// 下記のようにすると渡ってきたオブジェクトがこの時点で分割代入で展開されて、propという変数が使用できるようになる
// オブジェクトの中身は変更されない
function fn({ prop } ){
  // 変数の値を変更
  prop = 1;
  // 結果を出力
  console.log(a, prop);
}
// { prop : 0}と1と出力される
fn(a);
`````

####オブジェクトが多階層だった場合

````javascript
// cがprop1を格納しており、さらにprop2を格納している状態
const c = {
  prop1: {
    prop2: 0
  }
}
// 分割代入
let {prop1} = c;
// prop1を出力すると{prop2: 0}と出力される
console.log(prop1);
// その中身のprop2の値を変更すると
prop1.prop2 = 1;
// 結果はprop1の中身のprop2の値が1になる
// cからprop1の値を抽出した時点で、参照自体が保持される
console.log(c, prop1);
``````

###2.参照の比較と値の比較の違い

````javascript
// オブジェクトを定義
const a = {
  prop: 0
}

const b = {
  prop: 0
}

// aとbは同じオブジェクトであるか確認する
// 等価性を下記で確認するとfalseになる
// その理由は変数a,bに入っているのはオブジェクトへの参照であるため
// オブジェクトへの参照同士を確認して参照先のオブジェクトが変わってくるのでfalse
console.log(a === b);
// 抽象的な等価性も同様
console.log(a == b);

// 確認するにはオブジェクトの中にあるプロパティを比較する必要がある
// 下記のように書くとtrue
console.log(a.prop === b.prop);
``````

```javascript
// cにaを代入した場合
const c = a;
// 下記はtrueとなる
// 参照先を12行目で渡しているため
console.log(a === c);
`````


* プリミティブ型では**値**の比較
* オブジェクトでは**参照**の比較

が行われる

今日はここまで！

#参考にさせて頂いた記事

* [【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)
AWSルーティングテーブルによるアクセス制御

*初めてawsの記事を書くので、用語や概念を間違える可能性があります。*

社内で開発したAPIを叩いてたまにこう思うのです、
>社内のネットワークからアクセスできるAPIのエンドポイントとアクセスできないエンドポイントがある。この違いはなるだろう？

色々調べたら、これはawsのルーティングテーブルで外部からアクセス可能なsubnetを分けているからです。

例えばこの図のようにリージョンやAZとVPCを立てたとします。ピンクの枠はACL。
（画像はAWS Technical Essentialsコースより）
![スクリーンショット 2021-10-15 13.52.28のコピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81957/5617f3c1-75d1-2b2d-5421-921889697395.png)

VPCを立てた時にVPC内部のsubnet同士が通信できることを想定されて、デフォルトにMain route tableが作られます。

```
Destination: 入ってくるIPアドレス(CIDR)
Target: そのアクセスをどこにルーティングさせるのか
```

インタネットからのアクセスはgatewayを通してVPCに回します。subnetのinboundでそのアクセスが通るかどうかを決める。
10.1.0.0/16からのアクセスをlocalにルーティングするから他のsubnetにもアクセスできます。
0.0.0.0/0からのアクセスを全てgatewayに回します。**（subnetがgatewayを通してネットにアクセスできるようにするため）**

## 開発環境と本番環境のアクセス制御
最初の問題に答えすると、
開発環境のAPIはVPCは社内IPのアクセスを許可し、適切なsubnetに回しました。
本番環境ではその規制が厳しく、同じVPC内のアクセスしか許されてないから社内のネットからでもアクセスできないです。
Snowflakeでクエリ履歴からクエリを検索する方法

あのテーブルを作ったときのINSERT文はなんだっけと調べるときに調べる方法です。

```sql
use role ACCOUNTADMIN;

select * from snowflake.account_usage.query_history where query_text ilike 'insert%ano_table%';
```
商談の状態に応じて信号アイコンを表示する

# 概要
商談の進捗状況に応じた信号アイコン（赤、黄、青）で色分け表示し、一覧表示時などで可視化する。
※商談以外でも同様の方法でステータス等を可視化することができます。

|画像|ファイル名|ラベル|表示条件|
|:-:|:-:|:-:|:-|
|![red.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/3c5b5a74-0c6b-464b-18e5-adf3dae3d6b3.png) |red.png|遅延|完了予定日の14日前を過ぎた場合|
|![yellow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/d6c429be-aa8f-c8b1-29ea-be802fd1d6f1.png) |yellow.png|やや遅延|完了予定日の30日前を過ぎた場合|
|![green.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/31616794-ebd1-03a1-d704-71c21077674c.png)|green.png|予定通り|上記以外（30日以前）|

※本記事では信号の3色に色分けしているが、数式をアレンジすることによりバリエーションを増やすこともできる。

# 手順

### 1. アイコン画像の登録（静的リソース登録）

1. アイコンに使用する画像（red.png、yellow.png、green.png）をzipファイルでアーカイブし、1つのファイルにまとめる（alerticons.zip）。
1. アイコンに使用する画像を静的リソースとして登録する。  
設定 → カスタムコード → 静的リソース より、静的リソース画面を表示し、zipファイルをアップロード・登録する（この時、キャッシュコントロールは「非公開」に設定する）。
![Image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/339580/29d89d35-9558-38b3-0d4e-71dbd7efdc10.png)

### 2. アイコン表示項目の作成（カスタム項目（数式）の作成）

1. アイコン表示するオブジェクトに対して、カスタム項目（数式）を作成する。
    * 数式の戻り値のデータ型は「テキスト」を選択する。
    * 以下の数式を登録する。

```数式（文中のコメントは消去すること、インデント等は省略可）.
IF(
	( CloseDate - TODAY() ) <= 14, 　// 遅延の表示条件
	IMAGE( "/resource/alerticons/red.png", "遅延", 15, 15 ) & " 遅延",
	IF( 
		( CloseDate - TODAY() ) <= 30,  　// やや遅延の表示条件
		IMAGE( "/resource/alerticons/yellow.png", "やや遅延", 15, 15) &" やや遅延",
		IMAGE( "/resource/alerticons/green.png", "予定通り", 15, 15) &" 予定通り"
	)
)
```
※ IMAGE関数の第2パラメータにて、/resource/ を指定すると静的リソースのファイルを参照できる。
※ /resource/（zipファイル名）/（ファイル名）でアーカイブされたファイルを参照できる。
※ IMAGE関数の第2パラメータ（"遅延"などの文字列）はALTテキスト文字列（画像）。
※ IMAGE関数の第3、4パラメータ（15, 15）は 高さ、幅のパラメータで、画像をリサイズする場合に指定する（省略可能）

# 完成イメージ
（準備中）