OmniAICreator/Qiita-1.07M huggingface dataset:
初見だけど爆速でできた！Botからユーザにダイレクトメッセージを送る方法(Webex API編)


React、node.js、Google Cloud Platform (App Enigine)で[SORTFUL](https://sinceretech.app/ "SORTFUL")というチームビルディングゲームを作る際に出てきた疑問を、備忘録として記事にまとめています。

<h1>はじめに


SORTFULは参加者それぞれにダイレクトメッセージで「数字」を送るところから始まります。
![soortful1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388662/57718e8a-af03-0a99-bb7f-b98864c79504.png)

この「メールアドレス情報から、ダイレクトメッセージを送る」というのが、今回の目的です。
Webexとslackを使いたいので、以下のような感じになります。

Webex
![Webex6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388662/31a9aeb7-a2d3-07fa-d659-2d5ce4976b73.png)

slack
![slack8.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388662/a17c5d38-d388-6c4c-efae-4a7c126500ea.png)

本記事はwebex のapiについて書いていきます。

<h1>webex for Developersに登録する

まずは[webex for Developers](https://developer.webex.com/ "webex for Developers")にSign Upします。
※Webexアカウントをお持ちの方は、そのアカウントでログインできます。

![Webex1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388662/9f4caaad-59c5-8fd0-1790-ee19c7120758.png)

メールアドレスの他、Google、Microsoftアカウントでも登録できます。
無料です。クレジットカードの登録などもありません。

<h1>WebexでBotを作る

![Webex2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388662/ae68a375-a920-7c70-9f4f-86aa1a32c431.png)

"Start Building Apps"からはじめます。

![Webex3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388662/8ea8c6c7-c7be-3b90-7187-000b80341fc2.png)

今回はメッセージを送りたいのでBotを選びます。

![Webex4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388662/bdd9e445-72e9-542d-03ec-5aebb636708c.png)

Botの名前、ユーザーネーム、アイコン、説明をそれぞれ入力したら、

![Webex5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2388662/a6f8ac0b-13bf-d6b6-b78c-9188a2b2027b.png)


できあがりです！
このBot access tokenが必要なるので、コピーしておきましょう。

<h1>Botからメッセージを送る

メッセージの送り方は[こちら](https://developer.webex.com/docs/api/v1/messages/create-a-message "webex for developers")を参考にしています。

今回は、メッセージを送りたい人がwebexアカウントに登録しているメールアドレスを使って、Botからダイレクトメッセージを届けます。
webexのAPIはtoPersonEmailという引数があるので、メールアドレスさえわかれば（その人がwebexアカウントを持っていれば）ダイレクトメッセージが遅れるのがいいですね。

以下はnode.jsでaxiosを使ってhttps://webexapis.com/v1/messages にpostしてます。
（これを書く前にPostmanなどでテストしてみるとわかりやすいかと思います。）

```sendWebexMessage.js
const axios = require("axios");

async function sendMessage() {
  const url = "https://webexapis.com/v1/messages";
  const data = {
    toPersonEmail: `${メールアドレス}`,
    text: `送りたいメッセージ`,
  };
  const param = {
    headers: {
      "Content-Type": "application/json",
      Authorization: "Bearer " + トークン,
    },
  };

  try {
    const res = await axios.post(url, data, param);
    if (res.status === 200)
      console.log(`The message sent successfully`);
  } catch (error) {
    console.log(error);
  }
}
```

これで完成！
ちなみに、↑ではトークンをベタ書きしてますが、本当は隠した方がいいので、環境変数とかに入れてくださいね。
環境変数の隠し方は[コチラ](https://qiita.com/sinceretechnologies/items/9c7ff72fea1369d5a17d "GAEでAPIキーを隠したいとき")にメモしてます。
Laravelのリソース【個人的なお勉強アウトプット】

## 参考図書
https://www.amazon.co.jp/dp/B07CLLW4MX/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

## Laravelのリソースとは？
Laraveのリソースというのは、CRUD関係の機能一式をセットにして登録、扱えるようにしたもの。
リソースとしてルート情報を登録すると、CRUD関連のアクセスがすべて一括して使えるようになる。
ただし、アクセスのアドレスやアクションメソッドは最初から決められたかたちになる。また一式まとめて作成されるため、それらのメソッドは最初からすべて作成しておく必要がある。

## コントローラーの用意
`php artisan make:controller コントローラー名 --resource`
各種アクションが用意されているコントローラーファイルが生成される。

### リソースのアクション
|    |    |
| ---- | ---- |
|  一覧表示  |  index  |
|  新規作成  |  create、store  |
|  レコード表示  |  show  |
|  更新処理  |  edit、update  |
|  削除処理  |  destroy  |

## ルート情報の記載
`Route::resource('rest', `RestappController')`
Go言語 チャネル

## チャネル
チャネルとは、平行実行されているGoroutineから値を受け渡す機能である。

## Code 
```Go
// ある数以上の素数を求める関数
func primeRoutine(targetLine int, primeChan chan int) {
	i := 0
	for {
		// 値のインクリメント
		i++
		// 素数チェック
		if checkPrime(i) && targetLine < i {
			// チャネルにデータ送信
			primeChan <- i //[キーポイント]
		}
	}
}

// メイン関数
func main() {

	// 求める素数の基準値
	targetLine := 100000

	// チャネルの作成
	primeChan := make(chan int)

	// ゴルーチンで素数を求める
	go primeRoutine(targetLine, primeChan)

	// チャネルからデータを受信
	primeResult := <-primeChan //[キーポイント]

	// 値の出力
	fmt.Println(primeResult)
}
```

## Output Sample
~ $ go build -o prime main.go   
~ $ ./prime   
100003  

## GitHub
https://github.com/GL-Kageyama/Go_Channel
Scalaの勉強はじめました 7 〜関数型プログラミング編 型パラメータ〜

## はじめに
N予備校「Scala応用」コースを受講しています。

## 型パラメータ

```scala
class Cell[T](var value: T) {
  def put(newValue: T): Unit = {
    value = newValue
  }
  def get(): T = value
}
val cell = new Cell[Int](1)
```

型を引数のように扱えるようになる。
呼び出し側で実際の型を指定する。

## 非変
型パラメータが同じ場合のみ、代入可能なもの
```scala
val cell: Cell[Int] = new Cell[Int](1)
```
他の型では成立しない性質。

## 共変
```scala
class G[+T]
```
型パラメータの前に + をつけるとその型パラメータ(あるいはそのクラス)が共変になる。
代入しようとする型パラメータ`T1`が`T2`のサブクラスであれば代入可能になる。
`AnyVal`なら `Int`,`Long`,`Double`など。


## 境界
### 上限境界
:::note
型パラメータがどのような型を継承しているかを指定する
:::

```scala
abstract class Show {
  def show: String
}

class ShowablePair[T1 <: Show, T2 <: Show](val t1: T1, val t2: T2) extends Show {
  override def show: String = s"(${t1.show},${t2.show})"
}
```

### 下限境界
:::note
型パラメータがどのような型のスーパータイプであるかを指定する
:::

```scala
abstract class Stack[+T]{
  def push[E >: T](element: E): Stack[E]
  def top: T
  def pop: Stack[T]
  def isEmpty: Boolean
}
```

## まとめ
関数の再利用、一般化するにあたって型パラメータが便利なのはわかるんですが、なかなか難しい。リファクタリングの段階で使う、というのがまだわかりやすいのかなと思ったりします。
[Salesforce]Service Cloud VoiceとOmni-Channelを一緒に使うには

# Service Cloud Voiceとは
Salesforce上でコールセンターを構築するための仕組み。
SalesforceはAWSが提供するコールセンターの基盤(Amazon Connect)と提携しているため、Salesforce上で利用開始の設定を施すだけでAmazon Connectのアカウントも自動的に準備してくれる仕組みが備わっている。
※正確にはAmazon Connect以外の基盤も使えるが、この記事ではAmazon Connectを利用する前提で説明します。

# Omni-Channelとは
電話、チャット等、様々なチャネルからやってきた問い合わせを、以下のいずれかのルールでSalesforceユーザに割り当てる仕組み。
1. 問い合わせに対応するために必要なスキルを持っている人のうち誰かに割り当て
   (例：テクニカルサポートのスキルを持つ社員、英会話のスキルを持つ社員)
2. 特定のキューに所属する人のうち誰かに割り当て

# では、両者を混ぜて使うには…？
当然ながら、Service Cloud VoiceとOmni-Channelは一緒に使うことができます。詳細な使い方は公式の教材である「[Service Cloud Voice Learning Path](https://learnservicecloudvoice.herokuapp.com/)」が参考になります。
が、この教材を読み解く際に要注意ポイントがいくつかあるので、以下で説明していきます。

※勉強用の環境(Developer Edition)では試せないため、実際の動きを試すには本当に契約した環境が必要になるようです。私も実際に試せたわけではないため、公式の各種ドキュメントから読み解いてまとめています。もし誤りに気付いた方はご指摘頂けると幸いです。

# 解説

### 電話が来た時の一般的な処理の流れ
コールセンターに電話が来たら、まずはAmazon Connectの「コンタクトフロー」の処理がキックされます。**いきなりSalesforceの処理がキックされるわけではないので注意！**
その後の流れは以下の図の通りです。なお、必ずしこもこの図の通りなわけではなく、1や2をスキップすることも考えられます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573815/83824b71-3b77-cf29-3d34-80a17890f8ba.png)

### 用語の整理
上記の通り、Service Cloud VoiceはAmazon ConnectとSalesforceに跨って処理が実行されます。
両者には「フロー」「キュー」と呼ばれる名前の似た用語が存在するために、混乱されがちなので、以下に整理します。

|用語|実装場所|説明|参考資料|
|:----|:----|:----|:----|
|コンタクトフロー|Amazon Connect|IVR、Lambda、Salesforceへの転送といった大枠の処理を司るフロー|[Using Service Cloud Voice Contact Flows](https://developer.salesforce.com/docs/atlas.en-us.voice_developer_guide.meta/voice_developer_guide/voice_contact_flows.htm)|
|オムニチャネルフロー|Salesforce|どのスキル(orキュー)のユーザに対応させるかを判定するためのフロー|[オムニチャネルフローの作成](https://help.salesforce.com/s/articleView?id=sf.omnichannel_create_a_flow.htm&type=5)|
|キュー(Amazon Connect)|Amazon Connect|Salesforceへの転送待ちに使われる箱。この箱に着信(コールセンター用語では「<ruby>呼<rp>（</rp><rt>こ</rt><rp>）</rp></ruby>」という)を入れると、オムニチャネルフローでスキルまたはキューを用いて選定されたオペレータに着信が届く。|[Contact Flow: Queue Transfer](https://developer.salesforce.com/docs/atlas.en-us.voice_developer_guide.meta/voice_developer_guide/voice_contact_flows_queue_transfer.htm)|
|キュー(Salesforce)|Salesforce|オムニチャネルで、[スキルベースのルーティング](https://trailhead.salesforce.com/ja/content/learn/modules/omni-channel-lex/understand-sbr)ではなく[キューベースのルーティング](https://trailhead.salesforce.com/ja/content/learn/modules/omni-channel-lex/understand-qbr)を採用した場合に使う、着信を受ける人たちの集まり。例えば、「ご苦情担当係」のようなキューを作るイメージ。<br />※ちなみに、このキューは[所有者にセットするために使うキュー](https://trailhead.salesforce.com/ja/content/learn/projects/set-up-case-escalation-entitlements/create-case-queues-assignment-rule)と同一。|[キューベースのルーティングの概要](https://trailhead.salesforce.com/ja/content/learn/modules/omni-channel-lex/understand-qbr)|

### より詳しい実装方法を学ぶには
- 全体感をつかむには
[Service Cloud Voice Learning Map](https://learnservicecloudvoice.herokuapp.com/)
- Amazon Connect側の実装方法
[Service Cloud Voice Implementation Guide](https://developer.salesforce.com/docs/atlas.en-us.voice_developer_guide.meta/voice_developer_guide/voice_intro.htm)を参照
- Salesforce側の実装方法
[オムニチャネル のHelp](https://help.salesforce.com/s/articleView?id=sf.omnichannel_intro.htm&type=5)を参照
RESTfulとは？【個人的なお勉強アウトプット】

## 参考図書
https://www.amazon.co.jp/dp/B07CLLW4MX/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

## RESTfulとは？
Webは画面に表示される部分を持たないものがある。サーバーにアクセスして必要な情報を取得したり、情報を送って保存したり。
こうしたWebサービスは利用者がブラウザからアクセスして利用するのではなく、他のプログラムがアクセスして利用するためのもの。
RESTはREpresentational State Transferの略。分散型システムを構築するための考え方。
インターネットのあちこちにある分散したプログラムが相互にやり取りしながら動くシステムを考えたときのひとつの答えがREST。
RESTはHTTPメソッド（GET,POSTなど）を使って決まったルールに従ったアドレスにアクセスすることで、必要な情報を取得したり保存したりできる。
HTTPだから他のサーバーからアクセスして情報を取り出したりできる。
このRESTにもとづいて設計されているプログラムをRestfulであると表現される。

RESTfulでは、CRUDすべて同じアドレスで、アクセスに使うHTTPメソッドの違いによって処理を分けるのが一般的。
|  HTTP  |  CRUD  |
| ---- | ---- |
|  GET  |  Read  |
|  POST  |  Create  |
|  PUT  |  Updata  |
|  DELETE  |  Delete  |
Lambda@Edge で path が異なる同名の Cookie を渡した場合の挙動について

# はじめに

Lambda@Edge で Cookie の値を判断して処理をしたい。
そこで、以下の AWS 公式に記載されているコードを参考にして Cookie を dict に変換するような関数を書き利用していた。

```python
def parseCookies(headers):
    parsedCookie = {}
    if headers.get('cookie'):
        for cookie in headers['cookie'][0]['value'].split(';'):
            if cookie:
                parts = cookie.split('=')
                parsedCookie[parts[0].strip()] = parts[1].strip()
    return parsedCookie
```

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-examples.html#lambda-examples-redirect-to-signin-page


しかし、ある時を堺に更新したはずの Cookie の値が取得できず、ずっと古い Cookie の値が取得されるようになってしまった。 ただし、特定の環境のみであり、ごく一部の環境でのみこれが発生した。


# 原因

直接の原因は以下の２点が同時に発生していたためである。

- Cookie のパスを `Path=/` から `Path=/private/` 以下に変更した
- `Path=/` の Cookie がブラウザ上に残り続けていた (Expired=Sessionだったが、ブラウザを閉じていなかった)

まず、同名かつ Path が異なる Cookie が2つあるため、[RFC6225](https://www.rfc-editor.org/rfc/rfc6265#page-26) に基づいて **`Path=/private/` のデータが Cookie の先に来るように（ほとんどすべての環境では）送られる**。
実際にLambdaでは以下のような Cookie を確認した (Cookie名はtokenを利用)。

```
[{
  'key': 'cookie',
  'value': 'token=<pathが/private/のvalue>; token=<pathが/のvalue>'
}]
```

今回のケースでは `Path=/` が更新されていない古いデータであり、引き渡されたデータの後に来ている。
しかし、**AWS で提供されているコードは「Cookie名が同じ場合は後からきたデータで上書きする」実装になっている。** そのため、今回の記事のような問題が発生していた。


# まとめ

Cookie 名を変えた場合は古いデータがキャッシュとして残っているケースがあり、それが予期せぬ挙動を引き起こすことが分かった。
今回は開発レベルなので特に問題はなかったが、実運用では Cookie の Name を同じままで Path のみを書き換える場合、古い Cookie をどうするかはちゃんと考えた方が良いだろう。


# 参考

https://qiita.com/y_tochukaso/items/a4e815674be61ca68707
INSERT ... ON DUPLICATE KEY UPDATE 構文なのに、カラムが増える

# 結論

- auto-increment と相性が悪いため、必要なければ設定しない。

# 原因

- MySQL は INSERT 時点で、auto-increment が走り、その差分が次の処理(UPDATE)へ受け継がれてしまう。← ??
- auto-increment が必要ないならば、そのカラムを削除する。


# 参考
- https://stackoverflow.com/questions/23516958/on-duplicate-key-auto-increment-issue-mysql
- https://dev.mysql.com/doc/refman/5.6/ja/insert-on-duplicate.html
Content-Dispositionのデコードに見るリーダブルコードとは

## Content-Dispositionのデコード例

## なにやってるのかわからんコード 

```javascript
let headerLine = axiosResponse.headers['Content-Disposition'] || axiosResponse.headers['content-disposition'];
const fnameArr = headerLine.split(';');
let filename = fnameArr[fnameArr.length - 1].split('=')[1].replace('"', '').replace('"', '');
if (filename.indexOf(utf8Name) !== -1) {
    filename = decodeURI(filename.replace(utf8Name, ''));
  }
return filename
```

## リーダブルなコード

```javascript
var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(res.headers["content-disposition"]);
    if (matches != null && matches[1]) {
      fileName = matches[1].replace(/['"]/g, '');
    }
return filename
```
Firestoreでドキュメントの有効期限付けつつ、特定のフィールド値を持つドキュメントのみをクエリできるようにする

Firestoreについて、ドキュメントに有効期限を付けつつ、フィールドが特定値を持つドキュメントのみ取得できるようにしたいことがあります。

セキュリティルールと複合クエリを使って実現できたので、その方法をまとめました。

<!-- more -->

## 注記
- 処理対象のコレクションとしてdataをdataコレクションのフィールドとしてexpirationDate, someValueと定義しています。expirationDateはドキュメントの有効日時、someValueは何かしらの値としています。
- Firebase Authenticationによるログインかどうかのルールについては記載を省略しています。

## 結論
以下のいずれかをセキュリティルールを設定することで実現できます。

### パターン1: 特定のフィールド値について、型チェックを使う

```
match /data/{dataId} {
  allow list: if
    resource.data.expirationDate > request.time
    && resource.data.someValue is int;   // someValueの型に応じて、適切な型を指定する
}
```

`expirationDate > リクエスト日時` かつ `someValue == 整数値` でクエリする限り、ドキュメントを取得できます。これにより、リクエスト日時がexpirationDateより新しい場合は権限不足でデータのクエリをブロックできます。複合クエリでは複数フィールドで不等号を使えないため、someValueをユーザ側が知っているときのみクエリできる、という機構を作ることができます。

### パターン2: 特定のフィールド値について、値を制限する

もしフィールド値について値域が決まっていれば、以下のルールが利用できます。

```
match /data/{dataId} {
  // someValueは2桁の整数とする
  allow list: if
    resource.data.expirationDate > request.time
    && resource.data.someValue >= -99
    && resource.data.someValue <= 99
}
```

もちろん、クエリ時に上記値域外の値でクエリすると権限エラーとなり、ドキュメントを取得できません。

## 注意事項
1. セキュリティルールに記載する各フィールドについて、複合クエリのindexを作る必要があります。
2. 有効期限のexpirationDateについてクエリするとき、右辺値はfirestoreへのリクエスト日時(request.time)より新しい必要があります。もしリクエスト日時よりも右辺値が古い場合、セキュリティ条件の`resource.data.expirationDate > request.time`を満たせず権限エラーになります。


## クエリ例
JavaScript(Firestore v9)においては、以下の通りクエリできます(importは省略しています)。

```javascript
import { collection, getFirestore, where } from "firebase/firestore";

const db = getFirestore();
/*
  requestDateはfirestoreへの実際のリクエスト日時より新しい必要がある。
  暫定的に5秒加算している
*/
const bufferMilliSeconds = 5 * 1000;
const requestDate = new Date(Date.now() + bufferMilliSeconds);

const collection = collection(db, "data");
const querySpecificData = query(
  collection,
  where("expirationDate", ">", requestDate),
  where("someValue", "==", 123456789)
));
const querySnapshot = await getDocs(querySpecificData);

/* 以後、querySnapshotを使った処理 */
```

## セキュリティルールで試行錯誤したこと
### NG1: request.resource.dataとresource.dataで比較しようとした

以下の通り、セキュリティルールを設定しました。

```
match /data/{dataId} {
  allow list: if
    request.resource.data.someValue == resource.data.someValue;
}
```

[リファレンス](https://firebase.google.com/docs/firestore/security/rules-conditions?hl=ja#data_validation)にもあるのですが、`request.resource.data`は書き込みオペレーション実行後のドキュメントを指し、update, createでのみ有効です。readやdeleteでは定義されないので、使うことができません。

### NG2: allow getとクエリが絡むルールを併用しようとした

以下の通り、セキュリティルールを設定しました。

```
match /data/{dataId} {
  allow get: if
    resource.data.expirationDate > request.time
}
```

セキュリティルールはドキュメントにアクセスする前に判定されます。ドキュメント取得前には対象ドキュメントで`resource.data.expirationDate > request.time`かどうか判定できないため、権限エラーとなります。
Goのshadowingでハマる前にVSCodeの設定を見直すべし

# GoにおけるShadowとは？

こちらの記事に詳しいですが、要は**変数の再定義**のことです。
予期せぬバグの温床になるので気をつけましょう。

https://yourbasic.org/golang/gotcha-shadowing-variables/

`go vet --shadow GO_FILE` 
で検知できるのですが、IDEでリアルタイムに検知したいところですよね。

というわけで以下です。

# VSCodeで--shadow機能を有効化する

UserでもWorkspaceでもどちらでも良いので、`settings.json` に以下を追記しておきましょう。


```json
{
  "gopls": {
    "analyses": {
      "shadow": true
    }
  }
}
```

# Golandで--shadow機能を有効化する

この方の記事に詳しいです。

https://zenn.dev/uuusu/articles/6554631b4c08ba
brew cask installError: Could not find PKG source file 'multipass-1.8.1+mac-Darwin.pkg', found 'multipass-1.8.1 mac-Darwin.pkg' instead.

brew caskでmultipassをインストールしようとすると下記のエラーが発生して

`Error: Could not find PKG source file 'multipass-1.8.1+mac-Darwin.pkg', found 'multipass-1.8.1 mac-Darwin.pkg' instead.`

```
$ brew install --cask multipass
==> Downloading https://github.com/canonical/multipass/releases/download/v1.8.1/multipass-1.8.1+mac-Darwin.pkg
######################################################################## 100.0%#=#=#
==> Installing Cask multipass
==> Running installer for multipass; your password may be necessary.
Package installers may write to any location; options such as `--appdir` are ignored.
==> Purging files for version 1.8.1 of Cask multipass
Error: Could not find PKG source file 'multipass-1.8.1+mac-Darwin.pkg', found 'multipass-1.8.1 mac-Darwin.pkg' instead.
```

file名の`+`が` `に変更されてエラーが発生してるみたい
file名を`+`方に手動で修正して解決

```
$ cd ~/Caches/Homebrew/downloads/
$ # f64部分は変更して
$ mv {f64}--multipass-1.8.1\ mac-Darwin.pkg {f64}--multipass-1.8.1+mac-Darwin.pkg
```
Azure PipelinesでNugetがないとか、error NETSDK1064: パッケージ〜〜が見つかりませんでした。とか言われる

いい加減にCI/CDに取り組もうといろいろ試行錯誤中…
単一のリポジトリなら楽なんだけど、複数ってなるといろいろ躓く。大変。

## pipelines.ymlを頑張って書く
最初はテンプレから起こす。

## 複数リポジトリからのビルド
次はこれ。
例えば、メインプロジェクトAがフォルダーが違うプロジェクトBを参照しているような時

<pre>
├── A
│   ├── ~~~~
├── B
│   ├── ~~~~
</pre>

公式にあるように複数リポジトリの定義を追加

``` yaml
resources:
  repositories:
  - repository: sampleLib
    type: git
    name: sampleLib/sampleLib
    ref:  master

steps:
- checkout: self
- checkout: sampleLib
```

複数リポジトリにする場合は`checkout: self`もしておかないとメインがCheckoutされないので注意
参考）https://github.com/microsoft/azure-pipelines-yaml/blob/master/design/multi-checkout.md

### はまったところ
セルフホステッドのAgent使っていると、途中のビルド失敗などでチェックアウトしたソースが残ってたりする。
そのため変な場所にある`*.sln`を読み込んだりしてビルドに失敗する。
そういう場合はAgentのワークフォルダ（`_work`）をまるごと削除してしまう。（リネームでもOK）
変なファイルを読み込んでいないかはビルドログを詳細に見ればわかる。

## nugetでとれていないパッケージがある？
`Build`ステージで失敗する。
単一のときは失敗しなかったのになんで？

```
##[error]C:\Program Files\dotnet\sdk\6.0.201\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(267,5): 
Error NETSDK1064: パッケージ Microsoft.AspNetCore.Components.Analyzers、バージョン 3.1.6 が見つかりませんでした。
NuGet の復元により、削除された可能性があります。それ以外の場合、NuGet の復元が最大パス長の制限のために一部分しか完了していない可能性があります。
```

Nugetの復元がうまくいってない？と思って、以下のコードを追加した（がこれもうまくいかなかった）
参考にしたURL）https://github.com/MicrosoftDocs/azure-devops-docs/blob/main/docs/pipelines/packages/nuget-restore.md

``` yaml
- task: DotNetCoreCLI@2
  displayName: dotnet restore
  inputs:
    command: restore
    projects: '**/*.csproj'
    feedsToUse: 'select'
    vstsFeed: '<projectName>/<feedName>'
    includeNuGetOrg: true
```

これを入れるとむしろ別のエラーが…

```
2022-04-11T15:14:47.5379548Z          NuGet.Configuration.NuGetConfigurationException: 
NuGet.Config が妥当な XML ではありません。
パス: 'C:\Users\hoge\source\vsts-agent-win-x64-2.202.0\_work\1\Nuget\tempNuGet_82.config'。
```

これもググれば出るような方法をやったがだめ。
なので上記のtaskは消した。

## うまくいった方法
https://docs.microsoft.com/ja-jp/dotnet/core/tools/sdk-errors/netsdk1064
なんとかこのページに行き着いた。
やはりNugetの復元まわりみたい。なんで上のtaskだとうまくいかないのかわけわからん。

もう一行ビルドタスクを追加して、restoreさせた。
（restoreだけ追加したらビルドされずrestoreだけさせたので、別のタスクとして定義）

``` yaml
- task: VSBuild@1
  inputs:
    solution: '$(solution)'
    msbuildArgs: '/t:restore'
    platform: '$(buildPlatform)'
    configuration: '$(buildConfiguration)'
```

`msbuildArgs`のところに`/t:restore`を指定している。
これでうまくいった。やっとできた。
DocFxでローカルドキュメントを作成

[DoxFx](https://dotnet.github.io/docfx/)で
```docfx build```
でドキュメントを作成しても、
```docfx --serve```
でローカルサーバを立てないとindex.htmlから閲覧することができません。なんでや・・・

```docfx build -t statictoc```
これでローカル環境でも閲覧することができるようになりました。
文系出身3年目社員がDBスペシャリストに合格した勉強方法

# はじめに
　2021年秋期のデータベーススペシャリスト試験に合格したので、勉強方法をまとめてみました。

# スペック（勉強時）
経済学部卒の入社3年目のSEです。
普段の業務では、要件定義から保守まで全般的にかかわっていました。
DBに関しては、物理設計以外はかかわっていました。

2019年度に応用情報技術者試験に合格しています。
[文系新入社員が応用技術者試験に合格した勉強方法](https://qiita.com/ygikrps_/items/d6dfeed8d2cf970e3587)

# 結果
| 午前1 | 午前2 | 午前3 |  午前3 | 
| ----- | ----- | ----- |  ----- | 
| 免除  | 80点  | 83点  | 90点  |

# 午前2対策
午前に関しては、時間があるときに過去問を行っていました！
最新年度から順に行い、合格点を無理なく取れるなら試験当日の移動時間にもう1年度する程度で問題ないかと思います。
私の場合は、2年度を前もって行い当日に1年度行いました。

[データベーススペシャリスト過去問道場](https://www.db-siken.com/dbkakomon.php)
ユーザー登録すると間違った問題をあとから解けるのでおすすめです。

# 午後1対策
午後2対策をとっていれば、基本的に問題ありません。
ただ、試験時間が午後2より午後1の方がギリギリになるかと思います。
主に午後2対策を進めて、午後1は過去問を進めて時間感覚を養うようにすれば問題ありません。

# 午後2対策
午後2は主に概念設計と物理設計の2問が出題されます。
現状はどちらかに絞って勉強して問題ないかと思います。
私は、業務でER図の作成を行っていたので概念設計を中心に勉強いたしました。
業務でER図の作成をしている方は取っ付きやすいかと思います。

概念設計か物理設計に加えてSQLを使えるようになっておく必要があります。
SQLといっても基礎的なSQLを理解していれば問題ありません！
入門本を1冊やり込めば大丈夫です。

# 使用したテキスト
午前対策
[データベーススペシャリスト過去問道場](https://www.db-siken.com/dbkakomon.php)

午後対策
[情報処理教科書 データベーススペシャリスト 2022年版](https://www.shoeisha.co.jp/book/detail/9784798175980)

# 最後に
応用情報技術者試験の際も感じましたが、実際取得して必要な資格かと思えば必要ではないと感じています。。。
ただ、合格するだけで自信につながり仕事のモチベーションが上がるのでその面では取得して良かったです！

私の場合、午前1免除がなくなるとのことなので勿体無く感じて受講いたしました。
免除が無くなる方で、業務でDBを使用する人は受けてみても良いと思います！

来年度の試験に向けて少しでもお役に立てれば幸いです。
[Autonomous Database]Flashback Queryを試す

ADBでもFlashback機能が使用できます。
※ADBで使用可能/不可 のFlashback機能については[こちら](https://qiita.com/billtench/items/fd5794ad5563057b5c39)

今回は、ADBで**Flashback Query**を試します。

|Flashback Query（SELECT AS OF）とは|主な使用例|
|:--|:--|
|過去のある時点で存在していたデータを取得|・失われたデータのリカバリ<br>・誤ったコミット済みの変更の取り消し<br>・現在と過去の特定時点のデータを比較など|


# テストデータ
事前に次のようなテスト・データを用意しています（例：SCOTTユーザーのEMP表）
```sql
select * from emp;

     EMPNO EMPNAME
---------- ----------
       100 TANAKA
       200 SAITO
       300 YOSHIDA
```

# テスト・データを変更
```EMP表```の```EMPNO 300```データ「YOSHIDA」を「**ASAKURA**」に変更します

```sql
update emp set empname='ASAKURA' where empno=300;
commit;
```

```sql
select * from emp2;

     EMPNO EMPNAME
---------- ----------
       100 TANAKA
       200 SAITO
       300 ASAKURA　★
```

# 過去のデータの確認方法
5分前のEMP表のデータを確認してみると、変更前の「**YOSHIDA**」を確認できます。

```sql
select * from emp AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '5' minute) ;
```
```sql
    EMPNO EMPNAME
---------- ----------
       100 TANAKA
       200 SAITO
       300 YOSHIDA　★「ASAKURA」ではなく、変更前の「YOSHIDA」です
```

# 誤ってコミットした変更済のデータを、別表としてリストア
表名を変えて過去のデータに戻してみます（例：**EMP2** としてリストア）
10分前のデータにもどしてみます。
EMP2 を問い合わせると。変更前の「**YOSHIDA**」を確認できます。

```sql
create table emp2 AS
select * from emp AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '10' minute) 
;
```

```sql
SQL> select * from emp2;

     EMPNO EMPNAME
---------- ----------
       100 TANAKA
       200 SAITO
       300 YOSHIDA　★変更前のデータ
```



# 参考
* 19c:データベース開発ガイド
    * [19 Oracle Flashback Technologyの使用](https://docs.oracle.com/cd/F19136_01/adfns/flashback.html#GUID-03D1CAAE-D940-444A-8771-B1BC636D105D)
Streamlit Cloudで画像認識AIアプリをデプロイする(2022.4.12)

# 背景：Streamlitを使い始めました
前回は、Streamlitを使って、colabからアプリのUIを確認する事が出来ました。今回はリモートサーバーである(?)Streamlit Cloudにデプロイして、スマホから使用したいと思います。
<b>前回の記事</b>
[Streamlitを使ってスマホから開発環境を作る(2022.4.11)](https://qiita.com/tan0ry0shiny/items/6e22287b05e0e0556126)
# 目的：streamlit cloudにデプロイする
# 資料
1. [（4/14まで公開）【Streamlit+Colab】人工知能Webアプリを手軽に公開しよう！ Section4 【Live! AIRS-Lab #87】](https://www.youtube.com/watch?v=mBrBBG4AwLY&t=3655s)
# 結果：スマホのカメラで撮った画像を分類出来ました。
デプロイ出来たのでスマホからでも使えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496939/f6c1cdca-9067-16bd-3397-f179350ce8a7.png)
寿司を撮ってみました。結果は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2496939/855a7dee-755c-1669-bd2f-1c5896b2bda1.png)
CIFAR10に寿司の画像が無い様で、変な回答をされました。ただ、AIを実装したアプリが簡単に実装出来たのにはビックリしました。
# 方法
今回も、参考にしたYouTubeの大まかな説明をさせて頂きます。詳細は我妻先生がYouTubeで分かり易く説明されています。

ざっくりとした項目は以下になります。

1. Stremlit Cloudへの登録
1. AIモデル(model_cnn.pth)の作成
1. 実行ファイル等(model.py, app.py, requirements.txt)の作成
1. ローカルから実行して確認：我妻さんの資料が分かり易いのでご覧下さい。（[我妻(2020.4),"ai_webapp/section_4/02_webapp.ipynb",GitHub](https://github.com/yukinaga/ai_webapp/blob/main/section_4/02_webapp.ipynb)）これだけだと分かり辛い方は、我妻先生の無料公開されている動画（[【Streamlit+Colab】人工知能Webアプリを手軽に公開しよう！](https://youtube.com/playlist?list=PLcd5jOpoEDGA-gVwmAlzM29J3VSJuHzcV)）が分かり易いと思います。
1. Streamlit Cloudへデプロイ（参考3「[Streamlit SharingとGitHubでStreamlitアプリを公開する方法](https://sakizo-blog.com/524/)」）：結局、我妻さんの動画の公開期間後の追記となりました。いつもどおりGoogle先生にお聞きしたところSakizoさんの記事を提案して頂き、無事、再現出来ました。

ngrokの登録やトークンの発行などがまだの方は[我妻先生の動画](https://youtu.be/mBrBBG4AwLY)や[前回の記事](https://qiita.com/tan0ry0shiny/items/6e22287b05e0e0556126)を参照して下さい。

# 注意点

1. ローカル(colab)から実行する際、エラーとなる事がありました。その時は数回リロードすると動きました。
1. ローカルから実行する際はChromeに注意されます。詳細は前回の記事[Streamlitを使ってスマホから開発環境を作る(2022.4.11)](https://qiita.com/tan0ry0shiny/items/6e22287b05e0e0556126)をご覧下さい。
1. その他で今回、引っかかった事は特になかったと思います。動画の指示通りにすれば実行できました。ただ、公開が明後日までですので、詳細な説明はそのうち追記したいと思います。
# 感想：AIアプリのUIを考えるきっかけになりました
とりあえず、コードの動きを簡単に見たい時には便利そうです。UIがstreamlitに依存してそうですが、そもそもUIについてそこまで考えたことが無かったので、これを機にUIも考えてAIアプリをどのように使っていけるか考えたいと思います。

# 参考

1. [（4/14まで公開）【Streamlit+Colab】人工知能Webアプリを手軽に公開しよう！ Section4 【Live! AIRS-Lab #87】](https://www.youtube.com/watch?v=mBrBBG4AwLY&t=3655s)
1. [Streamlitを使ってスマホから開発環境を作る(2022.4.11)](https://qiita.com/tan0ry0shiny/items/6e22287b05e0e0556126)
1. Sakizo(2020.4)、[Streamlit SharingとGitHubでStreamlitアプリを公開する方法](https://sakizo-blog.com/524/)
powershellでのbabel実行について

powershellでハマった。
原因：実行ポリシーが初期値のままだから。

対処：vscodeで実行したいディレクトリに移動したうえで以下のコマンドを実行した。
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process

その後確認してみるためのコマンドは以下の通り
Get-ExecutionPolicy
→RemoteSignedと出力される

そのうえでbabelを実行しトランスパイルを実行する
.\node_modules\.bin\babel input.js --out-file output.js


以上

参考：
https://qiita.com/ponsuke0531/items/4629626a3e84bcd9398f
GoでMarkdownからExcel試験書を作成するソフトを作った

## 入力

![スクリーンショット 2022-04-12 101539.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792099/d5526e0b-0757-bf73-cac8-3b0eba8564b1.png)

[このmd文書](https://github.com/botanic7arc/markdown2excel/blob/master/src/test.md)を入力すると

## 出力

![スクリーンショット 2022-04-12 100844.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792099/3745d43d-cff0-85f2-ef4b-5ca90c564551.png)

こうなります。(横が入りきらなかったため、縮小して表示しています)

## ソース

https://github.com/botanic7arc/markdown2excel/tree/master/src

## 操作

1. ビルドした実行ファイルをExcel化したいmdファイルと同じディレクトリに配置します。
1. ビルドした実行ファイルを実行します。
1. 同フォルダに存在するmdファイルが番号付きで表示されるため、変換したいmdファイルに対応する数字を入力します。
![スクリーンショット 2022-04-12 101707.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792099/a7282b3a-e9aa-280e-e9e2-f141e43d1c19.png)

1. 変換されたExcelファイルが同ディレクトリに配置されます。

## 利点

Markdownで試験書を作成できるようになるため、試験内容を修正する際にわざわざセルを選択してF2キーを押したりダブルクリックして修正する必要がなくなり、楽にExcel試験書が作成できます。結果も全てMarkdown側に記載するようにすれば、差分管理も可能になります。

## 使用したライブラリ

https://github.com/qax-os/excelize

ExcelizeはBaiduやbilibili,AmazonやIBMでも使われているそうです。
Excelファイルに対して様々な設定が行えるため、Excelのカレンダーの作成もできるようです。(https://xuri.me/excelize/ja/example/calendar.html)

## 余談

### セル結合について

本プログラムでは行っていませんが、セル結合も以下で行うことができます。

    excel.MergeCell(sheetName, startCell, endCell)

### Go言語について

Go言語を初めて使いましたが、関数群が充実しているため思い浮かんだ処理をすぐに実装できとても使いやすかったです。
また、実行ファイルの作成もGo言語の環境が1つあれば、様々なOSとCPUに対応できる点も便利でよいと思いました。

## 参考文献

- [はじめに · Excelize ドキュメンテーション](https://xuri.me/excelize/ja/)
- [Goのexcelizeを使ってエクセル操作を行う方法](https://tokitsubaki.com/go-excel-excelize/582/#toc8)
- [【excelize】Go言語でExcelファイル(xlsx)を扱う - Qiita](https://qiita.com/hiro_nico/items/0f180f2dfc62cf2559c7)
- [excelize/xmlStyles.go at 882abb80988b7c50286dd2e6c6589fab10662db6 · qax-os/excelize · GitHub](https://github.com/qax-os/excelize/blob/882abb80988b7c50286dd2e6c6589fab10662db6/xmlStyles.go#L362)
- [【Golang】サポートする GOOS と GOARCH を一覧で確認するコマンド（ビルドの対応プラットフォーム確認） - Qiita](https://qiita.com/KEINOS/items/15b974af2dcf7489b61f)
- [docker上にgoの環境作れたと思ったけど補完ツールとかがなくて困ったので改善した話](https://zenn.dev/tomi/articles/2020-10-22-go-docker)
Elastic Beanstalk を使ったときのメモ

### ffmpeg をインストールする

現在、この Stakoverflow の

https://stackoverflow.com/questions/39241654/how-to-install-ffmpeg-on-elastic-beanstalk

リプライにある config は動きました。

```yaml
packages:
  yum:
    ImageMagick: []
    ImageMagick-devel: []
commands:
  01-wget:
    command: "wget -O /tmp/ffmpeg.tar.xz https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-3.4.2-64bit-static.tar.xz"
  02-mkdir:
    command: "if [ ! -d /opt/ffmpeg ] ; then mkdir -p /opt/ffmpeg; fi"
  03-tar:
    command: "tar xvf /tmp/ffmpeg.tar.xz -C /opt/ffmpeg"
  04-ln:
    command: "if [[ ! -f /usr/bin/ffmpeg ]] ; then ln -sf /opt/ffmpeg/ffmpeg-3.4.2-64bit-static/ffmpeg /usr/bin/ffmpeg; fi"
  05-ln:
    command: "if [[ ! -f /usr/bin/ffprobe ]] ; then ln -sf /opt/ffmpeg/ffmpeg-3.4.2-64bit-static/ffprobe /usr/bin/ffprobe; fi"
  06-pecl:
    command: "if [ `pecl list | grep imagick` ] ; then pecl install -f imagick; fi"
```


### デプロイ時のログはどこにある？

```
eb logs
```
デプロイに成功したのであれば、

```
/var/log/cfn-init-cmd.log
```

にもある
画面表示と共に、キーボードを表示する方法

# はじめに
SwiftUIで画面遷移をした際に、キーボードを自動で表示する方法をメモしておきます。
## コード
```swift
import SwiftUI

struct ContentView: View {
    // ContentView ⇄ SecondViewのシートを管理する状態変数
    @State var isShowSheet: Bool = false

    var body: some View {
        VStack{
            Button{
                isShowSheet.toggle()
            }label: {
                Text("Go SecondView")
            }
        }
        .sheet(isPresented: $isShowSheet) {
            SecondView(isShowSheet: $isShowSheet)
        }
    }
}

struct SecondView: View {
    // フォーカスが当たるTextFieldを、判断するためのenumを作成します。
    enum Field: Hashable {
        case text
    }
    // @FocusState変数は、現在フォーカス中か、フォーカスしてないのか（Bool型）の定義ではなく、
    // 現在フォーカスしているのが、どのテキストフィールドか（カスタムField型）を定義します。
    @FocusState private var focusedField: Field?
    // ContentView ⇄ SecondViewのシートを管理する状態変数
    @Binding var isShowSheet: Bool

    @State private var inputText = ""

    var body: some View {
        VStack {
            TextField("タイトル", text: $inputText)
                .padding()
                .border(.blue)
                .padding()
                .focused($focusedField, equals: .text)
        }
        .onAppear() {
            // 画面を表示してから0.5秒後にキーボードを表示
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5){
                focusedField = .text
            }
        }
    }
}

```

キーボードを閉じる方法については参考サイトを見ていただくと分かるので、割愛します。
参考サイトでは、画面タップ時にキーボードを閉じるようにしていたので、画面表示と共にキーボードを表示するには、Viewが表示された時に、実行するアクションを記述する.onAppearの中に、
focusedField = .titleもしくはfocusedField = .message書けば出来ると考えてやってみましたが、
出来ませんでした。
シート表示だけでなく、NavigationLinkを用いた画面遷移でも同じく出来ませんでした。
詳しくは調べていないのですが、画面遷移時のアニメーションの関係上、画面遷移と同時にキーボードを表示することは出来ないのではないかと講師の方に教えていただきました。
そこで、遅らせて処理を行う(遅延実行)DispatchQueue.main.asyncAfterの中に記述をすると、無事にキーボードを表示する事が出来ました。
キーボードが少し遅れて表示されるので、UI的には美しくないですが、画面遷移する度にTextFieldをタップして、キーボードを表示するという動作がめんどくさく感じていたため、とりあえず良しとします。

# 参考サイト
- [【SwiftUI】iOS 15からの@FocusStateを使用して画面タップでキーボードを閉じる方法](https://dev.classmethod.jp/articles/focusstate-keyboard/)
- [SwiftUIの複数なテキストフィールドのキーボードを消す方法](https://zenn.dev/ianchen0419/articles/2f7a99f8a63442)
insufficient cross reference id


このエラーが出た時の対象方法。

プロファイルなども他の人と一緒なのに、このエラーの場合で、メール送信があったら疑うことがあります。

メールテンプレートのフォルダーにアクセス権があるかどうかを。
案外、この問題が多いようです。

+ [insufficient cross reference id](https://trailhead.salesforce.com/en/trailblazer-community/feed/0D54S00000G5N4A)
+ [Insufficient access rights on cross-reference id when using send email action in Flow](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000KehQr)

I was able to figure out the issue, the user did not have edit access to the record. They were sharing the record via hierarchy. 
ユーザーがレコードへの編集アクセス権を持っていなかったため、問題を特定できました。彼らは階層を介して記録を共有していました。私はそれを見落としました。

[HOW TO DEBUG INSUFFICIENT ACCESS RIGHTS ON CROSS REFERENCE ID ERROR](https://acesalesforce.com/how-to-debug-insufficient-access-rights-on-cross-reference-id-error/)

# Email error: INSUFFICIENT_ACCESS: insufficient access rights on cross-reference id: 0059A0000054bxh when sending email

+ [Configure an email letterhead and template:](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000JgXYi)

:::note
This error can also be seen when a System Administrator uses "login as" for an unverified user in ANY ORGANIZATION and tries to send email from Salesforce as that user.

For more information on "Unverified Emails" see [this article](https://help.salesforce.com/s/articleView?language=en_US&type=1&id=authentication)

Email error: INSUFFICIENT_ACCESS: insufficient access rights on cross-reference id: 0059A0000054bxh when sending email
https://help.salesforce.com/s/articleView?id=000392091&type=1
:::


+ [Trailhead](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000PfBGG)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/487cb98a-d824-abe3-ba49-d1672b4badad.png)


:::note
 If you go to Setup > Users and click on your name, there should be a 'verify' button next to your email address. 
:::


# フローのメールアクションの場合

+ [Insufficient access rights on cross-reference id when using send email action in Flow](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000KehQr)

:::note
I had to remake the flow entirely from scratch, to finally find my mistake. I accidentally referenced the wrong Related Record ID in the Send Email Configuration. No more errors during debugging. 

最終的に間違いを見つけるために、フロー全体を最初から作り直す必要がありました。メール送信構成で間違った関連レコード ID を誤って参照していました。デバッグ中にエラーは発生しなくなりました。
:::


以下を参考にするのがいいかもしれません

https://salesforcegeek.in/insufficient-access-rights-on-cross-reference-id-in-salesforce-flow/
コマンドでファイルのリアル監視 batファイルからpowershell　-command　内部起動　windows tail

<H1>入力待ちで、ファイル名と後ろから取り出す行数を入力【バッチファイル】</H1>

```bat:kanshi.bat
@echo off
chcp 65001
set z=1
REM z=1は:INPUT_START2で変数が数字か文字列かチェック

:INPUT_START1
ECHO +-------------------------------------------------------+
ECHO  監視するファイルのパスを入力してください
ECHO  ※ファイルにスペースが含まれる場合は""で囲んでください。
ECHO +-------------------------------------------------------+
SET INPUT_STR1=
SET /P INPUT_STR1=
 
IF "%INPUT_STR1%"=="" (
ECHO  パスが入力されていません。
GOTO :INPUT_START1
)

IF NOT EXIST "%INPUT_STR1%" (
ECHO  ファイルが存在しません。
GOTO :INPUT_START1
)

:INPUT_CONF1
ECHO +-------------------------------------------------------+
ECHO  監視するファイルは[%INPUT_STR1%]でよろしいですか
ECHO  y / n 問題なければyを入力 再入力する場合はnを入力
ECHO +-------------------------------------------------------+
SET CONF_SELECT1=
SET /P CONF_SELECT1=
 
IF "%CONF_SELECT1%"== SET CONF_SELECT1=y
IF /I NOT "%CONF_SELECT1%"=="y"  GOTO :INPUT_START1

:INPUT_START2
ECHO +-------------------------------------------------------+
ECHO  文末からの取り出す行数を[数字]で入力してください
ECHO +-------------------------------------------------------+
SET INPUT_STR2=
SET /P INPUT_STR2=
 
IF "%INPUT_STR2%"=="" GOTO :INPUT_START2
REM　空が入力されたら再入力
IF "%INPUT_STR2%"=="0" GOTO :INPUT_START2
REM　0が入力されたら再入力
set /a y=%INPUT_STR2%+%z%
IF "%y%"=="1" GOTO :INPUT_START2
REM　入力されたものと1を足し算して、文字列だった場合1、再入力

:INPUT_CONF1
ECHO +-------------------------------------------------------+
ECHO  文末からの取り出す行数は[%INPUT_STR2%]でよろしいですか
ECHO  y / n 問題なければyを入力 再入力する場合はnを入力
ECHO +-------------------------------------------------------+
SET CONF_SELECT2=
SET /P CONF_SELECT2=
 
IF "%CONF_SELECT2%"== SET CONF_SELECT2=y
IF /I NOT "%CONF_SELECT2%"=="y"  GOTO :INPUT_START2


echo 監視するファイル　[%INPUT_STR1%]
echo 文末からの行数　[%INPUT_STR2%]

Powershell -command "Get-Content -Path %INPUT_STR1% -Wait -Tail %INPUT_STR2%"
REM powershellを内部で起動して監視を開始
REM Powershell -command "Get-Content -Path %INPUT_STR1% -Wait -Tail %INPUT_STR2% | select-string "検索する文字" -CaseSensitive"
REM 検索文字を指定することもできる　-CaseSensitiveは大文字小文字区別
```
NFSサーバ・MySQL・k8s-wordpressの構築のメモ

# 今回行った内容

- 研究でWordPressのPodに対して実験する必要があった．実験には実際に公開しているWEBサイトが望ましいので研究室のWEBサイトを実験で使った．kubernetesのディストリビューションは，k3sを用いている．
- 構築図をいかに貼る
![CDSL-Page-5.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2217092/065087d4-48da-60c3-94ea-a32e4a3d3c8a.png)


### MySQLのサーバを構築


- 研究室のデータのうちmysqlのデータをVM1に,
- コンテンツをVM2に保存
- VM1はMySQLのデータベースとして作成
- やったこと
1.　MySQLのインストール 
2.　MySQLのデータをImport　(https://qiita.com/rato303/items/2e614f23e5feee150ffc)
3.　MySQLのuserを追加，権限の付与`create　grant`　(https://qiita.com/shuntaro_tamura/items/2fb114b8c5d1384648aa ,CREATE文: https://www.dbonline.jp/mysql/user/index1.html ,GRANT文:  https://www.dbonline.jp/mysql/user/index6.html)
4.　MySQLのデータベースにアクセスを許可する設定するための設定ファイル特定方法`mysql --help | grep my.cnf`　(https://qiita.com/is0me/items/12629e3602ebb27c26a4)
5.　MySQLのデータベースにリモートアクセスするための設定ファイルの変更`bind-address = 127.0.0.1`をコメントアウト　(https://qiita.com/u-dai/items/b360a337b5001778699e)
6.　リモートホストのMySQLにアクセスし疎通確認　(https://qiita.com/sakito/items/7ddcbfb49edc7a50c6d7)

### NFSサーバの構築

- VM2はNFSサーバを作成，k8sのPersistentVolumeを作成してNFSサーバをPodにmountする

- PVについての参考

https://qiita.com/Esfahan/items/7ad7695ea78c7630239a

- 構築の参考にしたサイト集

https://www.digitalocean.com/community/tutorials/how-to-set-up-an-nfs-mount-on-ubuntu-20-04-ja

https://qiita.com/salty-vanilla/items/5bc95ad47af6bf051bf7


- やったこと
　
1.　root権限でログイン
2.　コンポーネントのダウンロード`sudo apt install nfs-kernel-server`
3.　/etc/exportsに以下を追記`# (公開したいディレクトリ) (どのマシンに公開するか)(公開モード)　例)　/home/server/foo YYY.YYY.YYY.YYY(rw,sync,no_subtree_check)`
4.　NFSサーバの再起動`sudo systemctl restart nfs-kernel-server` 
5.　Worker-Nodeにコンポーネントのインストール`sudo apt install nfs-common`　
6.　ほかのVMからmountできるようになっているか確認`$ sudo mount -t nfs 192.168.XXX.XXX:/mnt/nfs/pv /mnt/nfs`

# k8s-Wordpressのapply
今回利用させてもらったymlのファイルです．このnfs-pv.ymlとwp.ymlの中身を変更して利用しました．

https://github.com/cdsl-research/k8s-wordpress

### applyして発生したエラー

- WorkerNodeにnfsのパッケージがなくてPodの作成にエラーが発生しました．以下のコマンドでパッケージをインストールしてください．
`sudo apt-get install -y nfs-common`

- mountでエラーが発生しました．このエラーは，`kubectl descriibe po`でmountが失敗したエラーが出ます．
- WPにアクセスしたときデータベース接続確立エラーが表示されました．これはMySQLにアクセスする時に接続できないのが原因で出てしまうエラーです．
    - 原因としてwp-config.phpを設定を見直す必要がある．MySQLについての設定でユーザー名、DB名、パスワードの確認をする必要がある．
    - DBのユーザ名とホスト名が異なっているまたは，権限が付与されていない場合がある
    - 参考資料　https://qiita.com/worldgoodbye/items/afd30058f77f529bbabc
APIにリクエストを投げたい(VSCode REST Clientの紹介)

使ってる方も多いかとは思いますが、リクエスト投げてくれるVSCodeの拡張機能Rest Clientの紹介です。

リクエスト投げる方法としてcurlコマンド叩くでもいいんですが、テキストとして残せてチームでも共有できるのが良いです。
あと個人的にPostmanよりも使いやすいし、普段使用しているエディタで使えるのが便利です。
プロジェクトディレクトリにファイル置いておけばgit経由で他メンバーも利用できます。

## 準備
VSCodeの拡張機能のタブで「REST Client」と検索して出てきたものをインストールします。

![スクリーンショット 2022-04-11 23.14.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/3f53a6b2-18c3-33f4-652e-aa0306237bdc.png)

拡張子を「~~~.http」としたファイルを作成します。
これだけで準備完了です。楽！

## リクエスト
こんな感じでバーっとAPI投げたいリクエストを書いていきます。
一行目にリクエストライン
二行目以降にリクエストヘッダー
空行をあけて最後にリクエストボディ
を書いていきます。

![スクリーンショット 2022-04-12 0.06.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/7f798f06-b787-2711-fb3d-43f12f906c2f.png)

###(シャープ3つ)で、リクエストの区切りとなります。

リクエストラインの上のSend Requestという文字をぽちっと押すと、リクエストのデータが一時テキストとして返ってきます。

![スクリーンショット 2022-04-11 23.50.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/9ce3e9f9-e315-3100-5951-706c15bfd29e.png)


必要だったら保存もできます。

内容はUnicodeにエンコードされてる時があるのでデコードして読んでください。
設定で変更できます。
![スクリーンショット 2022-07-01 15.27.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/38e57f66-44cf-0602-71b5-1ba7b523deb2.png)


## メソッド毎の書き方(例)
### GET

```
GET http://localhost:8000/api/posts HTTP/1.1
```

クエリパラメータつける場合は、以下のように改行しても投げれるみたいです。見やすい。

```
GET http://localhost:8000/api/posts
    ?user_id=1
    &title=hogehoge
    &body=fugafuga
```

### POST

```
POST http://localhost:8001/api/post HTTP/1.1
content-type: application/json
Accept: application/json

{
    "user_id": "1",
    "title": "title",
    "body": "body",
    "status": "2"
}
```

### PATCH

```
PATCH http://localhost:8000/api/post HTTP/1.1
content-type: application/json
Accept: application/json

{
    "_method": "patch",
    "title": "changed title",
    "body": "changed body",
    "status": "1"
}
```

必要なパラメータに加えて`"_method": "patch"`のパラメータをつけてます
なんかGETとPOST以外のメソッドって特殊扱いなんですよね
HTTPだかブラウザだかの仕様の話だった気がするんですけど忘れました
Laravelでもそういう感じっぽいです(めっちゃ説明適当ですみません)
ところでなんでPATCH使ってるんだろ・・・
参考: https://qiita.com/maunamoana/items/f5fff8172301ef0dc1dc

あとこれは罠なんですが、リクエストボディの最後のパラメータの後に,(カンマ)を入れると値が送信されなかったです。。
JSONっぽいですが最後のパラメータにはカンマをつけないように気をつけてください。

上記以外にもメソッドありますが割愛。

## その他の機能
変数も使えます。
下の例みたいにURLのドメイン部分を変数に変えておくと環境が変わった場合も楽だと思います。
※公式からの引用
![スクリーンショット 2022-04-09 11.50.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/4ce08536-280e-f57f-f740-f9052cddf6f7.png)

前のレスポンスの値を保存して次のリクエストの値として設定したりとかもできます。
![スクリーンショット 2022-04-09 11.56.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/a75514a4-5d99-7c5f-7083-85bdfbb17f04.png)

GraphQLにも対応してるみたいです。
![スクリーンショット 2022-04-09 11.56.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140953/c22928ed-d40a-1b7f-55f3-c924a7d08d83.png)

多分他にも色々面白い機能あると思うんでドキュメント見てみてください。

https://marketplace.visualstudio.com/items?itemName=humao.rest-client

## 最後に
4月から新しいプロジェクトに参画しましたが、フロントエンド/バックエンドと担当がハッキリ分かれてる案件でフロントの画面がない状態でバックエンド実装する必要があったので改めて真面目に使ってみました。
これのおかげで開発しやすいです。ありがとうREST Client！

おしまい
GitHubのダウンロード数をプリザンターに登録する

## はじめに
GitHubのReleasesに登録したAssetsのダウンロード数を取得して、[プリザンター](https://pleasanter.org)に登録するPowerShellを作成してみました。

## 事前準備
1. プリザンターをインストールします。  
https://pleasanter.org/manual?category=0090_0120
1. APIキーを発行します。  
https://pleasanter.org/manual/api-key
1. 下記のような記録テーブルを作成します。

|項目名|表示名|
|:----|:----|
|タイトル|name|
|数値A|download_count|
|日付A|created_at|

## PowerShell
下記のコードを記述して実行します。

```powershell
# APIキーを記述します。
$apiKey = "82d09ce6..."
# プリザンターのテーブルへの作成を示すURLを記述します。
$uri = "http://servername/api/items/12345/create"
# GitHubリポジトリからreleasesを取得します。下記の例ではmicrosoftのTypeScriptのreleasesを取得します。
$releases = (Invoke-RestMethod -ContentType 'application/json' -Uri "https://api.github.com/repos/Microsoft/TypeScript/releases")
# 取得したreleasesのassetsをforeachします。
foreach ($item in $releases.assets) {
    # プリザンターに登録するデータを作成します。
    $data = @{
        ApiKey = $apiKey;
        Title = $item.name;
        NumA = $item.download_count;
        DateA = $item.created_at;
    }
    # JSONデータに変換します。
    $body = $data | ConvertTo-Json
    # プリザンターのレコード作成APIを呼び出します。
    Invoke-RestMethod -Method post -ContentType 'application/json' -Uri $uri -Body $body
}
```

## 実行結果
リリース毎のダウンロード数をプリザンターに登録できました。タスクスケジューラなどに登録しておけば、毎日記録が取れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78230/01557ade-cb04-26df-5ab0-0dd4f2812710.png)
[SwiftUI] 可読性にパラメータを全振りした SFReadableSymbols という OSS を作った話。

## Tl;Dr
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29653/c8b5bc97-bde8-08d9-597a-1dccdb89944c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29653/d56307fc-cc92-bb59-c471-8324af747b3b.png)

https://github.com/YusukeHosonuma/SFReadableSymbols

## なぜ作ったの？

https://twitter.com/tobi462/status/1501365638337499136?s=20&t=se2PRRrdxVXMZQAQCgqNCg

## vs 標準API

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29653/2a830ec7-7efd-781d-51cd-990fd8cb7220.png)

- ✅ 可読性
- ✅ 検索性（アイコンの視覚情報から探したい場合）
- 🚫 Xcode 以外（外部エディタやGitHub）でレンダリングされない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29653/717b005c-78ba-adcf-279f-a55e68e7ba9f.png)

## vs [SFSafeSymbols](https://github.com/SFSafeSymbols/SFSafeSymbols)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29653/5e763125-6a9b-d786-0c6c-a669548cf84e.png)

- ✅ 可読性（さらに）
- ✅ 検索性（アイコンの視覚情報から探したい場合）
- ✅ SF Symbols アプリからコピペ（`Cmd + C`）できる（タイプし直す必要はない）
- 🚫 入力補完はできない
- 🚫 コンパイルタイムセーフではない

[Effective SwiftUI 候補（仮説）](https://github.com/YusukeHosonuma/Effective-SwiftUI) の[『SFSafeSymbols が本当に必要かよく検討する』](https://github.com/YusukeHosonuma/Effective-SwiftUI/discussions/12)において、コンパイルタイムセーフという理由だけで [SFSafeSymbols](https://github.com/SFSafeSymbols/SFSafeSymbols) を利用すべきでない理由について詳しく述べています。

> コンパイルタイムセーフという言葉は響きこそいいが、以下のような欠点もあることに留意すること。
>
> * SF Symbols アプリからコピーするだけではダメで、自分でタイプし直す必要がある。
> * タイプし直した結果が間違っていないことが、コンパイル時に保証されるわけではない。
> * 文字列リテラルと違ってシンタックスハイライトが効かない。
> * 文字列としてのリーダビリティは劣る。

## 利用例

App Store に公開中の、以下の2つのマルチプラットフォームアプリ（SwiftUI）で利用しています。

### SE Browser
- [App Store](https://apps.apple.com/app/id1615741502)　※macOS版のみ公開中
- [GitHub](https://github.com/YusukeHosonuma/Swift-Evolution-Browser)

### E2DC
- [App Store](https://apps.apple.com/app/id1616576556)
- [GitHub](https://github.com/YusukeHosonuma/E2DC)

## 制限

現時点では SFSymbols 3.2 (67) のみに対応しています。
（需要がありそうなら下位バージョンをサポートすることも検討しています）

## 仕組み

静的に用意した`Dictionary<Character, String>`でシンボル名をルックアップしているだけの簡単な仕組みです。

```swift
// SF Symbols version 3.2 (67)
let symbolsToName: [Character: String] = [
    "􀈂": "square.and.arrow.up",
    "􀈃": "square.and.arrow.up.fill",
    "􁅅": "square.and.arrow.up.circle",
...
]

@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
public extension Image {
    init(symbol: Character) {
        self.init(systemName: symbolsToName[symbol] ?? "") // ✅ Lookup
    }
}
```

- [Image+.swift](https://github.com/YusukeHosonuma/SFReadableSymbols/blob/1.0.0/Sources/SFReadableSymbols/Image%2B.swift)
- [SFSymbols+.swift](https://github.com/YusukeHosonuma/SFReadableSymbols/blob/1.0.0/Sources/SFReadableSymbols/SFSymbol%2B3.2.swift)

## どうやって作ったの？

SF Symbols アプリで全選択してシンボルや名前をコピーし、Swiftコードやエディタを使って整形しただけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29653/6e99002d-80d7-0d2c-a017-cf3be3c2834a.png)

Special Thanks!

https://twitter.com/the_uhooi/status/1501377828989726720?s=20&t=se2PRRrdxVXMZQAQCgqNCg

## おわりに

そんなわけで、久しぶりに自分の作った OSS について記事を書いてみました。

好き嫌いがハッキリ分かれるタイプの OSS だと思いますので、もし肌に合うようでしたらご利用くださいませ🙏
Chatterの質問機能


どこで設定するのか悩みました。

パブリッシャーのレイアウトに「質問」ボタンを追加のようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/2824976d-5728-2a91-5227-3ab847780454.png)

ちゃんと出ましたね。　ボタンの並び順はアクションに追加した順番のようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/7af2498b-a029-235a-3e65-a7d408585a02.png)



[質問機能を利用した投稿内容の編集はできますか？（mobileアプリ）](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000G5PDESA3)


モバイルには表示できますが、ボタンの数に制限がありそうです。

![パブリッシャレイアウト.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/79e35712-6c35-a026-9bb9-e13cb7434e60.png)


モバイルからは編集できないようです。アイデアに挙がっています。
環境構築が済んだPCでサクッとReactを開始する方法 

reactのコマンドとか、フォルダのパス関係とか忘れがちなのでまとめてみた。

### 前提
npm,babel等の環境構築は済んでいる。
windows環境。

---
### プロジェクトの開始
コマンドプロンプトで作業したいフォルダまで移動して、以下のコマンドを入力する

```console
npx create-react-app プロジェクト名
```
上記で作成されたプロジェクトを動かしてみる
```console
cd プロジェクト名
npm start
```

---

### プロジェクトの編集
①作成されたプロジェクトのsrc以下のフォルダからすべてのファイルを削除する。(↓の画像で選択しているファイルをすべて削除する。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918692/a978ef57-f8c0-6210-b4e9-0723bf5ba470.png)



②削除後、srcフォルダ以下にApp.jsとindex.jsを作成しなおして、reactの定型文を記載する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918692/61d579d6-041e-17e6-d720-aa9c43ba8897.png)

```react:App.js
function App(){
    return(

    );
};

export default App;
```

```react:index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

ReactDOM.render(
        <App />,
        document.getElementById("root")
);
```

③App.jsのreturnの中に表示させたい内容を入力する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/918692/2687e550-e58f-4bbe-1f05-26b02c40cabb.png)

```react:App.js
function App(){
    return(
    <h1>hello,world!</h1>//ここに入力
    );
};

export default App;
```

以上で、reactをとりあえず動かすことができます。
あとは、自分の好きなように編集すれば良いです。

----

### create-react-appしてからプロジェクトを作成する理由
reactは複数のモジュールで動いている。create-react-appすることで、必要なモジュールを自動でインストールしてくれる。
create-react-appしないと、必要なモジュールを自分で記述する必要があり「とりあえずreactを動かしてみる」のような場合はハードルが高い。
そのため、
create-react-app→デフォルトの不要なファイルを削除→自分の作りたいものを作成
という順序でreactを作成した。
【RSpec】基本的なRSpec導入手順

## はじめに
超初歩的なRSpec導入部分のみです
実践はしてませんのでご注意ください

## 環境
Rails: 6.1.4
Ruby: 2.6.3
RSpec: 3.10
rspec-rails: 5.0.1

## 手順
1.Gemfileに記述

```ruby:Gemfile.rb
:
:
  group :test do
    gem 'capybara'
    gem 'rspec-rails'
    gem "factory_bot_rails"
    gem 'faker'
  end
:
end
```
```terminal:terminal
bundle install
```
2.↓必要なファイルをインストールする

```terminal:terminal
rails g rspec:install
```
3.specフォルダにfactoriesフォルダを作成
　　　　　　　　　↓
factoriesフォルダの中にモデル名.rbを作成
例）book.rbやarticle.rbなど

4.テストデータを定義する

```ruby:spec/factories/book.rb
FactoryBot.define do
  factory :book do
    title { Faker::Lorem.characters(number:10) }
    body { Faker::Lorem.characters(number:30) }
  end
end
```
Faker::Loremはgem 'faker'を入れると使えます。
勝手にダミーサンプルを作ってくれるのでいちいちサンプル作らなくて済んで楽です。

上記の場合は
title  にランダムな５文字の文字列
contentにランダムな10文字の文字列
を作ってくれます。
<br>
5.FactoryBotが使えるように
spec/rails_helper.rbのend直前に記載
```ruby:spec/rails_helper.rb
:
:
  config.include FactoryBot::Syntax::Methods
end
```
<br>
<br>
初歩的な初歩ですがRSpecを使うまでの手順でした！
思いついた事をほかの人に伝える方法

## 参考資料
- ひきた よしあき著「博報堂スピーチライターが教える ５日間で言葉が『思いつかない』『まとまらない』『伝わらない』がなくなる本」 (大和出版) 株式会社ＰＨＰ研究所 
## なぜこの本を読もうと思ったのか
- 思いついた事をほかの人に伝えるのが苦手だからです。
## この本を通じて出来るようになりたいこと　
- 他の人に思った事を瞬時に伝える事が出来るようにしたいからです。
## 記事概要
- この記事では、箇条書き形式で他の人に思った事を瞬時に伝える事が出来るために、どんなトレーニングに取り組むべきかと、どんな考え方を取るべきかについて記します。
- 取り組むべきトレーニングは全部で3項目で、取るべき考え方は全部で2項目です。
- 取り組むべきトレーニングとして、```頭の中を知るためのトレーニング```、```考える習慣をつけるためのトレーニング```、```言葉に説得力を持たせるためのトレーニング```があります。
- 取るべき考え方として```論理的に発想する力をつける考え方```、```真に伝わる表現力を磨く考え方```があります。
### 頭の中を知るためのトレーニング
- 30秒で10個の物を言う事
- 形容詞の言葉を使わず聴覚、 味覚、 視覚、 嗅覚、 触覚 などの五感を使って表現する事
- あらゆるものを実況中継する事
- 覚える言葉を鏡の中に語り掛ける事
- 今日知った事を3個記憶する事
### 考える習慣をつけるためのトレーニング
- 人の頭で考えるクセをつける事
- なにげない行動ひとつひとつにも理由付けする事、
- 要点を明確にして頭の中を整理する事
- 仮説を立てる事
- 一人でブレインストーミングして33点のネタをくっつける事
### 言葉に説得力を持たせるためのトレーニング
- 苦労や失敗弾のネタを10個持つ事
- わかりやすい数字だけを使う事
- メモ帳をもって街に出る事
- 朝、その日の話題を仕込む事
- 「ありがとう」を今の5倍使う事
### 論理的に発想する力をつける考え方
- 「なぜ」を5回投げかける事
- 哲学者ヘーゲルの弁証法(意見と反対意見を調整して、高い次元の意見を作る事)で意見を深める事
- 伝える一人を決め、その人をすみずみまでイメージする事
- 相手と共通のイメージを持つために擬人化する事
- ゴールから考えて見えていない所を明確にする事
### 真に伝わる表現力を磨く考え方
- 書く時も話すときも40文字で表現する事
- 動かしたい動きを具体的に入れる事
- 相反する内容で、 強調する事
- 連体修飾格を使う事
- 主語を私たちにする事
## 感想
　この本に書いてあるトレーニング方法を毎日実施し、表現する前にこの本に書かれている考え方ができているかチェックします。そして、思いついたことをスラスラ言えるようにします。