OmniAICreator/Qiita-1.07M huggingface dataset:
任意の色相に対して最低コントラストを満たす色を計算する

この記事は、Webデザインでユーザに任意のテーマカラーを選択させるときなどを想定しています。

一般に言う「色の種類」とは、[HLS色空間](https://ja.wikipedia.org/wiki/HLS%E8%89%B2%E7%A9%BA%E9%96%93)や[HSV色空間](https://ja.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93)の「[色相](https://ja.wikipedia.org/wiki/%E8%89%B2%E7%9B%B8)」によって決定されます。

<img src="https://upload.wikimedia.org/wikipedia/commons/d/db/HLSColorSpace.png" width=200>

色相は、上の画像を見ていただけると分かる通り、`0`-`360`の数値で表すことができます。

HLS色空間では、「色相、彩度、輝度」によって色が決定されるので、彩度と輝度を固定して、色相のみをユーザに選択させればいいと考えることができます。

それを実装したものが以下のGIFになります。

![qiita_color_theme_01.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227791/1207e63e-85b7-9b7d-ea1f-9aed5ece4994.gif)

しかし、実際に見てみると、赤〜青にかけては文字が見えやすい一方で、黄〜緑では文字が見えづらいことがわかります。

これは、背景色（#fff）とのコントラストが低くなることが原因です。なぜ色によってコントラストが変化するかというと、色の相対輝度にはRGBそれぞれについて重みがあり、白とコントラストが高いのは`青 > 赤 > 緑`です。黄色は赤と緑の中間色なので、上記の画像で黄〜緑の文字が見えづらいことにも納得できます。

**相対輝度(L)の定義**

```math
{\displaystyle L=0.2126\times R+0.7152\times G+0.0722\times B}
```

詳しくは[WIkipedia Help:配色のコントラスト比](https://ja.wikipedia.org/wiki/Help:%E9%85%8D%E8%89%B2%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%B9%E3%83%88%E6%AF%94)が参考になります。

では、先ほどのように彩度と輝度を固定して色相のみを変化させるのではなく、彩度のみを固定し、指定された色相に対して一定のコントラストを満たす輝度を求めてみましょう。

![qiita_color_theme_02.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227791/4192089e-2151-3940-5f31-638fc8eee8ba.gif)

赤〜青は最初とほとんど変わりませんが、黄〜緑が見えやすくなったのが分かります。
JavaScriptの関数は以下の通りです。（`chroma-js`という色操作が簡単にできるJavaScriptのライブラリを使用しています。）

```js
// 背景色、色相、彩度、満たしたいコントラスト比を渡すと、それを満たす色(16進数)を返す
const getChromaFromHue = (
  baseColor: string,
  hue: number,
  saturation: number,
  minContrast: number
): chroma.Color => {
  const BC = chroma(baseColor)
  let left = 0
  let right = 1
  let count = 0
  while (right - left > 0.01) {
    const middle = Math.floor(((left + right) / 2) * 100) / 100
    const color = chroma(hue, saturation, middle, 'hsl')
    if (chroma.contrast(BC, color) > minContrast) {
      left = middle
    } else {
      right = middle
    }
    if (count > 5) break
    count += 1
  }
  return chroma(hue, saturation, left, 'hsl').hex()
}

const changeThemeColor = (hue: number) => {
  setThemeColor({
    theme: getChromaFromHue('#fff', hue, 0.7, 1.7),
    themeAA: getChromaFromHue('#fff', hue, 0.5, 4.5),
    themeAAA: getChromaFromHue('#fff', hue, 0.4, 7),
    themeLight: getChromaFromHue('#fff', hue, 0.5, 1.05),
  })
}
```

関数内の処理としては、指定されたコントラストを満たす最高の輝度を二分探索で求めています。
(二分探索せずに求める式があれば教えてください:bow_tone1:)

ここで、`コントラスト比 > 4.5`を満たすものはアクセシビリティ適合レベル`AA`、`コントラスト比 > 7.0`を満たすものはアクセシビリティ適合レベル`AAA`です。文字色のコントラストは`AA`以上は満たすか、テーマを`AA`以上に変更するオプションなどは用意するといいでしょう。

**`AA`を満たすボタンを設置した例**
![qiita_color_theme_04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227791/89c75f80-96c4-152b-9a4d-1fe6102e3d45.gif)
Circleci で Pulumi Service を利用せず pulumi preview を実行する （AWS - TypeScript/JavaScript編）


## はじめに

CircleCI で Pulumi Service を利用せずに `pulumi preview` するまでの手順と設定をまとめました。
pulumi 公式のドキュメントにも CircleCI で preview を実行する方法は用意されていますが、 Pulumi Service の利用が前提になっています。
CircleCI で `pulumi preview` を実行する際に Pulumi Service を利用できる場合は公式ドキュメントを参照することをおすすめします。

https://www.pulumi.com/docs/guides/continuous-delivery/circleci/

また、既に pulumi のプロジェクトを作成済みであることを前提に記述します。
ブロジェクト作成方法は以下等を参照してください。

https://www.pulumi.com/docs/get-started/aws/create-project/

## 依存パッケージ等バージョン

利用したバージョンは以下のとおりです。

- pulumi v3.1 以上
- node v14

## CircleCI の設定ファイルを作成する

タイトルの通り. CircleCI の設定ファイルを用意しましょう。
先に完成後のファイルを載せ、ポイントは後で記載します。

```yml:.circleci/config.yml
version: 2.1
orbs:
    pulumi: pulumi/pulumi@2.0.0
jobs:
  pulumi_preview:
    docker:
      - image: 'circleci/node:14'
    steps:
      - checkout
      - run:
          name: Define PULUMI_CONFIG_PASSPHRASE as an environment variable
          command: echo "export PULUMI_CONFIG_PASSPHRASE=$TEST_PULUMI_CONFIG_PASSPHRASE" >> $BASH_ENV
      - pulumi/login:
          version: 3.1.0
          cloud-url: file://~
      - run:
          name: Yarn install
          command: yarn install
      - pulumi/stack_init:
          stack: 'mziyut-test-pr_${CIRCLE_BUILD_NUM}'
      - run:
          name: Set AWS region to ap-northeast-1
          command: pulumi config set aws:region ap-northeast-1
      - pulumi/preview:
          stack: 'mziyut-test-pr_${CIRCLE_BUILD_NUM}'
      - pulumi/stack_rm:
          stack: 'mziyut-test-pr_${CIRCLE_BUILD_NUM}'

workflows:
  version: 2
  test:
    jobs:
      - pulumi_preview:
          filters:
            branches:
              ignore:
                - main
                - master
```

### ポイント 1: Orb の設定をしよう

Orb に対する詳しい説明は以下を参照してください。

https://circleci.com/docs/ja/2.0/orb-intro/

簡単に説明すると、後述する `pulumi/login`を実行出来るよう、設定等を読み込むための設定です。
定義しない場合、 `pulumi/login` 等実行することができません。

```yml
orbs:
  pulumi: pulumi/pulumi@2.0.0
```

### ポイント 2: `PULUMI_CONFIG_PASSPHRASE` を定義しよう

`pulumi/stack_init` を実行する際に、パスフレーズが定義されていない場合パスフレーズを要求され先に進みません。
また、よほどのことがない限り、本番とテスト環境でパスフレーズを同一にする必要はないため Variables に別名で定義しておき、Job 内で `PULUMI_CONFIG_PASSPHRASE` に定義し直しています。

```yml
- run:
    name: Define PULUMI_CONFIG_PASSPHRASE as an environment variable
    command: echo "export PULUMI_CONFIG_PASSPHRASE=$TEST_PULUMI_CONFIG_PASSPHRASE" >> $BASH_ENV
```

### ポイント 3: `pulumi/login` 実行時の `cloud-url` の設定について

今回は Pulumi Service を利用しない（=Local にある state ファイルを参照する）ため、 `cloud-url` に `file://~` と定義します。
ちなみに、 `pulumi login --local` と実行したときに設定される `cloud-url` と同値です。

```yml
- pulumi/login:
    version: 3.1.0
    cloud-url: file://~
```

また、この時点で `pulumi` コマンドが存在しない（未インストールの場合）同時にインストールも実行されます。

### ポイント 4: リージョンを設定しよう

`pulumi preview` を実行する場合、リージョンの設定が必要です。
今回は東京リージョンを定義しておきます。

```yml
- run:
    name: Set AWS region to ap-northeast-1
    command: pulumi config set aws:region ap-northeast-1
```

## 最後に

今回はテスト用に stack を定義しましたが、production 環境に適用している stack を用い preview することもできます。
CircleCI の設定ファイル内、stack 定義部分を書き換えてみてください。
オプションrm付きで起動したdockerコンテナ環境を保存しておく

![docker.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253019/c6d3903c-d757-b4ee-f8ff-53d3fb01c598.png)

# 概要

オプション`rm`付きで起動したdockerコンテナ環境で色々と試しているうちに、そのコンテナ環境を保存しておきたいと思うことがあった。ちょっと調べてみたけど、オプションを無効化する方法は無いっぽい。代替としては、コンテナから`CTRL+P`＋`CTRL+Q`でデタッチして、環境をコミットする方法を使うしかないみたい。

※ コンテナから`CTRL+D`でデタッチしてしまうと、オプション通りにコンテナが終了してしまうので注意が必要。

- Docker Version: 20.10.6, API Version: 1.41

# 詳細

オプション`rm`付きでdockerコンテナを起動する

```shell
$ sudo docker run -it --rm --name="<CONTAINER_NAME>" <IMAGE_NAME>
```

コンテナから`CTRL+P`＋`CTRL+Q`でデタッチ

```shell
# [CTRL+P] [CTRL+Q]
```

コンテナに名前を付けて、環境をコミット

```shell
$ sudo docker commit <CONTAINER ID/NAME> <IMAGE NAME>
```

# 関連記事

* https://tech.withsin.net/2015/09/30/docker-container-detach/
（備忘録）AWSではじめるLinux入門ガイドのデモ(ファイル操作コマンドなど)

# 1. はじめに
2021年5月2日に[ヤマムギ](https://yamamugi.connpass.com/)様主催にて開催された[ヤマムギ vol.19 AWSではじめるLinux入門ガイドのデモ(ファイル操作コマンドなど)](https://yamamugi.connpass.com/event/211610/)でのAWS学習内容について、自分への備忘録としてまとめてみました。ご参考になれば幸いです。

# 2. 学んだ内容
- EC2起動テンプレート
- AWS CloudShell
- コマンドを用いたS3操作
- コマンドを用いたEFS操作

# 3. 学習サイト
- [YouTube：LinuxサーバーでS3にファイルを保存するデモ](https://youtu.be/ROKvYCZ2nB0)
- [YouTube：LinuxサーバーからAmazon EFSをマウントするデモ](https://youtu.be/lJT5o8VnnyQ)
- [GitHub：yamamanx/yamamugi](https://github.com/yamamanx/yamamugi)
- [GitHub：CloudShell用.yaml定義](https://github.com/yamamanx/yamamugi/tree/main/v19)
- [サンプル.png画像](https://yamashita-20210505.s3.amazonaws.com/yamamugi.png)

# 4. 参考サイト

# 5. ハンズオンで得た豆知識
- EC2の起動テンプレートを用いると、毎回同じEC2を作成＆起動できる
- CloudShellを用いると、コマンドラインで起動テンプレートを実行できる

```
// AWS CloudShell
aws ec2 run-instances --cli-input-yaml file://linux.yaml
```

- awsコマンド操作

```
// S3操作コマンド
// ホームディレクトリへ移動
$ cd ~

// S3バケットの作成
$ aws s3 mb s3://xxxxxxxx
$ aws s3 mb s3://yamamugi20210505

// 新規ディレクトリの作成
$ mkdir ~/work

// 画像のダウンロード
$ wget https://xxxxxxxx
$ wget https://yamashita-20210505.s3.amazonaws.com/yamamugi.png

// S3バケットへのファイルアップロード
$ aws s3 cp xxxxxxxx.png s3://xxxxxxxx

// S3バケット内の一覧表示
$ aws s3 ls s3://xxxxxxxx

// S3バケット内の一覧表示
$ aws s3 ls -recursive s3://xxxxxxxx

// S3アクセス権限の確認
$ aws s3api get-object-acl --bucket xxxxxxxx -- key xxxxxxxx.png

// S3アクセス権限の付与
$ aws s3api put-object-acl --acl public-read --bucket xxxxxxxx --key xxxxxxxx.png

// テキストファイルの新規作成
$ echo HelloWorld > ~/work/hash.txt

// ハッシュ値の確認
$ md5sum hash.txt

// opensslにてハッシュ値の生成＆base64化
$ openssl md5 -binary hash.txt | -base64

// ハッシュ値チェック付きでS3へアップロード
$ aws s3api put-object /
```

```
// EFS（Elastic File System）
// マウントポイントの作成
$ sudo mkdir /mnt/efs

// マウントヘルパー
$ sudo yum install -y amazon-efs-utils

// EFSをマウント
$ sudo mount -t efs fs-xxxxxxxx:/ /mnt/efs

// マウント先のEFSへファイルコピー
$ sudo cp hash.txt /mnt/efs

// マウント先のEFS内のファイル内容表示
$ sudo cat /mnt/efs/hash.txt
```

# 6. おわりに
AWS学習の参考になれば幸いです。
ハンズオン開催してくださいました[ヤマムギ](https://yamamugi.connpass.com/)様、感謝いたします。

2021/05/05 NISHIZONO Takahiro
GAS Bigquery insert getjobでハマった話

GASでBigqueryにinsertをする場合、insertの結果を確認する為には、jobステータスを参照する必要がある。
このとき、jobgetの際に、jobの起動ロケーションを明示しないとjob not foundエラーとなる。

jobの起動ロケーションはBigqueryのデータセットのロケーションと同じになる。
ロケーションがUSかUKの場合は、ロケーション指定を省略ができる。
（ここがおおいにはまった。うまく行っているGASとだめなGASがあって何が違うのかと）
また、ロケーション指定はAPIの引数として、パスパラメータではなく、クエリパラメータという扱いなので、JSONで引数セットしてあげなくてはいけない。
（ここもはまったポイント。）
【AWS】 AWS Certificate Manager（ACM）証明書の有効期限が切れた場合の対処法

# はじめに
やってしまいました・・・。
**証明書の有効期限切れ**という初歩的なミスです。
個人請負で運営しているサイトなのですが、Amazon Web Services, Inc.から以下のメールが来ていることに気づきました。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1203022/9bee24fa-c767-3e08-21e6-d97a2d8f2536.png)

一部抜粋

>Your certificate has expired
Greetings from Amazon Web Services,
You have an SSL/TLS certificate from AWS Certificate Manager (ACM) in your AWS account that expired on Apr 30, 2021 at 12:00:00 UTC. This certificate includes the primary domain xxxxxxxxx.org and a total of 2 domains.

DeepLで訳すと

>証明書の有効期限が切れている
Amazon Web Servicesからのご挨拶です。
あなたのAWSアカウントには、AWS Certificate Manager（ACM）からのSSL/TLS証明書があり、その有効期限は2021年4月30日12:00:00 UTCに切れています。この証明書には、プライマリドメインであるxxxxxxxx.orgと、合計2つのドメインが含まれています。

となっています。証明書の期限切れです。

# そもそもなぜ有効期限が切れたのか
不明です。
ACMは自動更新のはずなので何か設定にミスがあったのかもしれません。
今回は既に有効期限が切れており、時間もないので原因調査を行わずに暫定対処のみ行いました。

# Amazonから来ていたメール
メールボックスを見返すと、Amazonから「やばいよ、そろそろやばいよ」というメールがたくさん来ていました。
以下の件名のメールが来ていたら注意です。

## Action Required - Your certificate renewal
翻訳すると

>アクションが必要です - あなたの証明書の更新

です。

## URGENT Action Required - Your certificate renewal
翻訳すると

>緊急のアクションが必要です - あなたの証明書の更新

です。

# 対処法
**「新しい証明書を作る」**だけです。
とりあえずそれでなんとかなりました。

マネージメントコンソールからACMにアクセスし、「証明書のリクエスト」で新規の証明書作成フローに入ります。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1203022/d8392542-f6a5-0f53-7e00-aa9383692a91.png)

ここから先のフローは調べるとたくさん出てくると思うので一部割愛します。
証明書を作成したら、「Route53でのレコードの作成」を行います。
※Route53を使用している場合
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1203022/bb9a9f8a-8ead-b11b-539b-ea3d8833ad01.png)

後は30分程度待ったら反映されました。
有効期限の切れた証明書もそのままにしていますが特に問題はないです。

この情報が誰かの役にたったのなら幸いです。

以上です。
社会心理学の「ハロー効果」を使って、人事評価を上げる方法

# はじめに
誰だってビジネスにおいて**良い評価を得たい**、**評価を下げたくない**と思っているのではないでしょうか？
かくいう私も良い評価をされたいと思っています。
評価をされたいとは思いますが、人事評価の評価基準は会社や評価を行う人間によってまちまちなので、**「これさえやれば絶対に評価される」**というものはなかなかないのかなと思います。
しかし、社会心理学の**「ハロー効果」**を知っていると、どんな相手に対しても**「人事評価を上げる」**ことが出来るかもしれません。

# ハロー効果とは
[Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%AD%E3%83%BC%E5%8A%B9%E6%9E%9C)で調べてみると

>ハロー効果（ハローこうか、英語: halo effect）とは社会心理学の用語で、ある対象を評価する時に、それが持つ顕著な特徴に引きずられて他の特徴についての評価が歪められる（認知バイアス）現象のこと。

と書いてあります。

**「外見のいい人が信頼できると感じてしまう」**や**「有名大学を卒業している人を仕事が出来ると思ってしまう」**などが挙げられます。
本来、外見の良し悪しはその人の内面の評価に関係ないはずですし、有名大学を卒業しているというだけでは仕事が出来るのか判断できないはずです。
まさに**「評価の歪み」**ですね。

# ポジティブ・ハロー効果とネガティブ・ハロー効果
ハロー効果には、良い評価に繋がる**「ポジティブ・ハロー効果」**と、悪い評価に繋がる**「ネガティブ・ハロー効果」**があります。

**「ポジティブ・ハロー効果」**は先程説明したように**「良い（ポジティブな）特徴に引きずられて他の特徴についても良いと判断してしまう」**ことです。
CMで好感度が高い有名人を起用しているのもこのためだそうです。
「CM ハロー効果」で調べると色々と記事が出てくると思いますので興味のある方は調べてみてください。

**「ネガティブ・ハロー効果」**はその逆で、**「悪い（ネガティブな）特徴に引きずられて他の特徴についても悪いと判断してしまう」**ことです。
これもCMを例に説明すると、起用された有名人が不祥事を起こした場合、商品は変わっていないにも関わらず売れ行きが落ちたりクレームが入ったりすることがあるそうです。 


# ではどのようにハロー効果を使えばいいか
ハロー効果の使い方は単純です。

**「良い（ポジティブな）特徴を伸ばし、アピールする」**

**「悪い（ネガティブな）特徴をなくし、見えないようにする」**

上記の2点です。

具体的に例をあげると

## １．見た目の印象を良くする
見た目の印象を良くすることは色々なビジネス書に出てくるくらい大切なことのようです。

+ 髪型が寝癖のままになっていないか
+ 爪は伸びていないか
+ シワがない服装になっているか
+ 体臭や服の臭い（生乾き臭など）をケアしているか

などをチェックすると良いかもしれません。
こんなことでビジネスでの評価が上がる（かもしれない）のであればすぐにでも取り入れるべきです。

## ２．ネガティブな話し方をしない
ボソボソと自信なさげに話す相手より、ハッキリと自信を持って話す相手の方が信用できると思われるのではないでしょうか？
否定的なことを言わないというのもポイントです。
会話で相手がズレたことを言ってきたなと思ったら、「いや」「でも」などといった否定的な言葉を使わず、いったん「なるほど。」と言葉にしてから自分が話したい方向に持っていく方が良いです。


## ３．資格を取る
これはエンジニアには大切なことかもしれません。
私自身「資格不要派」ではあるのですが、やはり高難易度の資格保持者であれば「あの人は凄い人なんだ」と思ってしまいます。
資格はスキル票などにも載せられるのでアピールもしやすいです。

## ４．資料の体裁を整える
書いている内容は同じでも見やすい資料を作成する人の方が「仕事が出来る」と判断されます。
色を使ったたり、フォントを揃えたり、矢印や線が斜めになっていないか気をつけるだけでも印象は変わります。

## ５．得意なことをアピールする
どんなことでも良いと思います。
「Pythonでのコーディングならこの現場の誰にも負けない」だったり「テストは仕様書作成も実施も大得意」などでも良いです。
人より秀でているものがひとつでもあってそれをアピールさえ出来れば他の評価も引きずられて良くなる可能性があります。

# さいごに
ここで例に挙げた以外にも「遅刻しない」であったり「言い訳しない」などいくらでも考えられます。
自分の**「良い（ポジティブな）特徴を伸ばし、アピールする」**こと、**「悪い（ネガティブな）特徴をなくし、見えないようにする」**ことを普段の仕事に当てはめて**「じゃあ自分ならどうするか」**を考える時間を作ってみてはいかがでしょうか？

# 参考リンク
[Wikipedia　ハロー効果](https://ja.wikipedia.org/wiki/%E3%83%8F%E3%83%AD%E3%83%BC%E5%8A%B9%E6%9E%9C)
[ハロー効果って何？](https://employment.en-japan.com/tenshoku-daijiten/13955/)
Font Awesomeのアイコン(SVG)に縁取りをする

デザインなどでFontAwesomeのアイコンを重ねて表示したい時に、そのまま重ねると境界がないためきれいに表示させることができませんでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64261/c6f9a359-e52f-8c45-7bc2-f54ea4bd0068.png)


枠線の色を背景色と同じ色にして、枠線を太くすることでアイコンを縁取りするCSSを追加すると重ねても区別して表示されるようになりました。 :star2: 

```css
svg.fa-star {
    stroke: #82a5c8;
    stroke-width: 12px;
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64261/1254dc1f-4409-ab97-8971-49dfe57487df.png)
PowerShellでエクセルファイルを作成する

Powershell で Excel ファイルを新規に作成し A1 セルに値を記入して保存するスクリプト

```Powershell
$excel = New-Object -ComObject "Excel.Application"

$excel.Visible = $true
$workbook = $excel.Workbooks.Add()
$sheet = $workbook.worksheets.Item(1)

$sheet.Range("A1").value2 = 5,021

Write-Host $sheet.Range("A1").value2
$workbook.save()
$excel.Quit()

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)
```
Excel VBAとPowershellで通貨単位を変える（英語のみ）

# コードはこんな感じ

```vb
Option Explicit
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Sub test()
Dim cmdStr As String
Dim strResult As String
Dim CultureString As String
Dim CurrencyValue As Currency
CultureString = "'de-DE'" '小文字-大文字にすること、シングルクォーテーションで囲んでからダブルクォーテーションで囲むこと
CurrencyValue = 1200 'できる限り数字の形式で
cmdStr = "$(" & CurrencyValue & ").tostring('C', [cultureinfo]" & CultureString & ") "
' PowerShellの実行（Execコマンド使用バージョン）
strResult = ExecPowerShell(cmdStr)
Debug.Print strResult
'ActiveCell.Value = "'" & strResult ' こちらを使うときもあるかもしれない。ただし、シングルクォーテーションを除去しにくい
ActiveCell.Value = strResult
strResult = RunPowerShell(cmdStr)
Debug.Print strResult
End Sub
' **************************************************
' PowerShellの実行（Execコマンド使用バージョン）
' **************************************************
Private Function ExecPowerShell(cmdStr As String) As String
 
    ' WshShellオブジェクト
    Dim oExec As Object
 
    ' Execコマンドで実行する
    '  -NoLogo                          著作権の見出しを出さない
    Set oExec = CreateObject("Wscript.shell").Exec("Powershell -NoLogo Set-ExecutionPolicy RemoteSigned -Scope Process -force; " & cmdStr)
     
    ' ジョブが実行中(0)の間は、スリープしながら完了（1）まで待つ
    Do While oExec.Status = 0
        ' 100ミリ秒
        Sleep 100
    Loop
 
    ' 標準出力取得
    ExecPowerShell = oExec.StdOut.ReadAll
 
End Function
```

[PowerShell VBAでPowerShellを実行して結果を取得する（Exec編）](https://it-engineer-info.com/language/powershell/1899/)
ここを参考にしています。
しかし、commandletを実行する場合ではないからでしょうか。
`-Executionplicy`では値はかえらないので、`Set-ExecutionPolicy`で実行権限も変えてセミコロンで区切っています。

# しかし。。。
PowershellのWindowが開く上に遅いです。
さらに、現在のところExcelが日本語環境の場合、ドル以外は文字化けしてしまいます。フォントも文字化けを解消するものはないようです。なので、ほぼ実用性はありませんでした。

## ただし
[カンマ（コンマ）とピリオド（ドット）を逆にしたい−オプション-インターナショナル](https://www.relief.jp/docs/003271.html)
文字列で返ってくるため、あまり意味はないのかもしれませんが、コンマとピリオドを逆にするには、上記のCをNに変えて、Cultureinfoを`de-DE`とすると小数点以下2桁が付与されます。
ただし、セルの表示形式を文字列にしているか、`ActiveCell.Value = "'" & strResult`を使ってシングルクォーテーションで文字列にする場合が考えられます。しかし
[先頭の「'」(シングルコーテーション)を削除する](http://officetanaka.net/excel/function/tips/tips15.htm)
はききません。この変換をするとドル以外は基本的に文字列になり、数字には戻りません。ほかの通貨単位を付与すると文字列のままですし、コンマとピリオドを逆にすると文字列になっているためです。
　なお、、この田中大先生のVBAはHasFormulaがあるので、2013以降です。それ以前の場合はここをIsformuala2010を使う必要があります。
　なので、セルの表示形式は標準で、シングルクォーテーションは付与しないほうがいいようです。必要があるときだけ変えましょう。
 また実際の解決方法は、Format,Text関数を使い、JPY,USDなどのアルファベットで表記するほうが有効でしょう。外貨建預金でそういう表記を採用しているケースがあるのは、通貨単位や桁区切り文字で文字化けが起きることを回避しているものと考えられます。ただ、小数点がいくつか最初はわからないときは、この方法も使えるかもしれません。
while

 while
条件によって繰り返し処理を行う場合はwhileを使用しましょう。
以下のように使用します。

```rb
while 条件式 do
  処理するアクション
end
```

もしプログラムを終了させず同じ処理を繰り返すループ処理を行う場合は、以下のようにします。

```rb
while true do    
  処理するアクション
end
```

上記の書き方だと無限ループするので、任意のタイミングで中断させたい場合はctrl+cを押します。

**（問）**以下の仕様を満たすアプリケーションを作成します。
「仕様」：プログラムの実行すると、
①「[0]:カロリーを表示する、[1]:終了する」という選択肢が表示され、数字を入力することができる
② 0を入力すると「500kcal」と表示され、上記条件①が繰り返し実行される。
③ 1を入力するとアプリケーションが終了する

解答例

```rb
while true do
  puts "[0]:カロリーを表示する"
  puts "[1]:終了する"
  input = gets.to_i

  if input == 0
    puts "500kcal"
  elsif input == 1
    exit
  end
end
```
module

モジュール
モジュールとは、Rubyにおける「インスタンスを生成できないクラス」のようなもののことです。
下記のように定義します。

```rb
module モジュール名

end
```

また、モジュールの中にはクラスを定義することもできます。

```rb
module Sample
  class Test
  end
end
```

この「Testクラス」は下記のようにして呼び出すことができます。

```rb
Sample::Test
```
クラスとモジュールの使い分けとして、具体的なオブジェクトを生成したい場合（インスタンスを生成したい場合）はクラスを使用し、処理だけ（メソッドなど）が必要な場合はモジュールを使用します。
またモジュールの中にクラスを定義するメリットは、Testクラスが複数ある場合に、下記のような記述でクラスごとに区別ができます。

```rb
module Sample1
 class Test
 end
end
module Sample2
 class Test
 end
end
Sample1::Test
Sample2::Test
```
こうすることで、同じクラス名でも定義することができ、それぞれ区別して使用することができます。
継承の仕組み

継承の仕組み
-

あるクラスを継承して新しいクラスを作る場合には以下のように「新しいクラス < 元となるクラス」と書きます。


【例】sample.rb

```rb
class PoliceCar < Car

end
```

クラスを継承すると、親クラスから子クラスへ以下のものが引き継がれます。

親のインスタンス変数
親のインスタンスメソッド
例えば、Carクラスを継承したPoliceCarクラスを作成するコードを以下に示します。

```【例】sample.rb
class Car
  def initialize(car_type, capacity)
    @name = car_type
    @capacity = capacity
  end

  def info
    puts "車種：#{@name}　乗車定員：#{@capacity}人"
  end
end

  class PoliceCar < Car

  end

  police_car = PoliceCar.new("パトカー", 5)

  police_car.info


# ターミナル出力結果
# 車種：パトカー　乗車定員：5人
```
上記を見てみると、PoliceCarクラスの中には何も記述をしていませんが、継承元のCarクラスのインスタンス変数およびインスタンスメソッドを使うことができます。

インスタンスメソッドを追加する方法
続いて子クラスに独自のインスタンスメソッドを追加する方法について
子クラス内に新しくメソッドの定義を追加するだけです。

先ほどのPoliceCarクラスを例に説明をします。以下を見てください。

```br【例】sample.rb
class Car
   def initialize(car_type, capacity)
     @name = car_type
     @capacity = capacity
   end

   def info
     puts "車種：#{@name}　乗車定員：#{@capacity}人"
   end
end

class PoliceCar < Car
   def siren
     puts "ウゥ〜ウゥ〜"
   end
end

police_car = PoliceCar.new("パトカー", 5)

police_car.siren

# ターミナル出力結果
# ウゥ〜ウゥ〜
```

上記では、PoliceCarクラス内にsirenメソッドを新たに追加し、それを実行しています。
このように子クラスにインスタンスメソッドを追加することで、そのクラス固有のメソッドを定義することができます。

メソッドを上書きする方法
-
最後にメソッドを上書きする方法について.親クラスにあるメソッドと同じ名前のメソッドを子クラスで定義すると、メソッドを上書きすることができます。これをメソッドの「オーバーライド」と呼びます。

 オーバーライド
-

オーバーライドとは、親クラスのメソッドを子クラスに同名のメソッドを定義することによって上書きすることを指します。

```rb:【例】sample.rb

class Car
   def initialize(car_type, capacity)
     @name = car_type
     @capacity = capacity
   end

   def info
     puts "車種：#{@name}　乗車定員：#{@capacity}人"
   end
end

class PoliceCar < Car

 def info
   puts "車種：#{@name}　乗車定員：#{@capacity}人　パトロール時間：24時間"
 end

 def siren
   puts "ウゥ〜ウゥ〜"
 end

end

police_car = PoliceCar.new("パトカー", 5)

police_car.info

# ターミナル出力結果
# 車種：パトカー　乗車定員：5人　パトロール時間：24時間
```

上記では、Carクラスで定義されたinfoメソッドと同名のメソッドをPoliceCarクラス内で定義しています。
これによって、PoliceCarクラスを元に生成されたインスタンスでinfoメソッドを実行すると、上書きされた処理が実行されるようになります。

問問題.1
以下の仕様にしたがってコードを記述してください。

・Personクラスはプロパティ name, ageを持っている。
・StudentクラスはPersonクラスを継承している。
・Studentクラスにはintroduceメソッドが定義されている。実行すると
　「私の名前は◯◯です。◯歳です」と表示がされる。
・Studentクラスのインスタンスを作成し、introduceメソッドを実行する。

```rb:【例】
class Animal
  def initialize(name)
    @name = name
  end
end

class Dog < Animal
  def name
    puts "名前は#{@name}です。"
  end
end

pochi = Dog.new("ポチ")
pochi.name
```

```rb
class Person
  def initialize(name,age)
    @name = name
    @age  = age
  end
end

class Student < Person
  def introduce
   puts "私の名前は#{@name}です。#{@age}才です。" 
  end
end

taro = Student.new("Taro",13)
taro.introduce
```
v-if


```vuejs
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>

<div id="app">
<p v-if="ok">OK!</p>
</div>
```

```
  new Vue({
    el: '#app',
    data: {
      ok: true
      }
  })
```

![20210506-060511.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960935/5140fb2d-79c4-ab97-cc26-bb6765659952.png)
学習メモ(CloudFormation#01)

#内容
AWS CLIを用いてCloudFormationを使う
`testvpc`スタックを作成、削除する


#スタック作成
事前にカレントディレクトリ (c:\work\cfn) にテンプレート (01_vpc.yml) ファイルを格納済
下記コマンドを実行する。

```
c:\work\cfn>aws cloudformation create-stack --stack-name testvpc --template-body file://01_vpc.yml
```

--template-bodyのところは file://から始めないといけない。
相対パスでもOKらしい

StackId: ~~ みたいな表示のみが出てればOK

#確認(マネジメントコンソール)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1425451/3e65fd70-7f4c-7388-4b83-bc20c1a46746.png)
ちゃんとできてる:grinning:

#確認(AWS CLI)

スタックを表示
`aws cloudformation describe-stacks`

```
c:\work\cfn>aws cloudformation describe-stacks
Stacks:
- CreationTime: '2021-05-05T19:25:45.678000+00:00'
  Description: Hands-on template for VPC
  DisableRollback: false
  DriftInformation:
    StackDriftStatus: NOT_CHECKED
  NotificationARNs: []
  RollbackConfiguration: {}
  StackId: ~~~
  StackName: testvpc
  StackStatus: CREATE_COMPLETE
  Tags: []
```

スタックを表示（過去作った履歴も見れる）
`aws cloudformation list-stacks`

`--stack-status-filter CREATE_COMPLETE`オプションで絞ると削除したスタックは出てこない

```
StackSummaries:
- CreationTime: '2021-05-05T19:25:45.678000+00:00'
  DriftInformation:
    StackDriftStatus: NOT_CHECKED
  StackId: ~~~
  StackName: testvpc
  StackStatus: CREATE_COMPLETE
  TemplateDescription: Hands-on template for VPC
```

作成したときのログ表示
`aws cloudformation describe-stack-events --stack-name testvpc`

```
- EventId: ~~~
  LogicalResourceId: testvpc
  PhysicalResourceId: ~~~
  ResourceStatus: CREATE_IN_PROGRESS
  ResourceStatusReason: User Initiated
  ResourceType: AWS::CloudFormation::Stack
  StackId: ~~~
  StackName: testvpc
  Timestamp: '2021-05-05T19:25:45.678000+00:00'
```

スタックリソースを表示
`aws cloudformation list-stack-resources --stack-name testvpc`

```
- DriftInformation:
    StackResourceDriftStatus: NOT_CHECKED
  LastUpdatedTimestamp: '2021-05-05T19:26:07.454000+00:00'
  LogicalResourceId: CFnVPC
  PhysicalResourceId: ~~~
  ResourceStatus: CREATE_COMPLETE
  ResourceType: AWS::EC2::VPC
-
```

#スタック削除

下記コマンドを実行
`aws cloudformation delete-stack --stack-name testvpc`

正常にプロンプトが返ってきたらOK。
マネジメントコンソールでも確認してみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1425451/18a97892-e5e5-1082-c34d-6254409aa3f6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1425451/f62f6690-d9f9-cfdf-19d5-83ea7e6a534c.png)

ちゃんと削除できてひと安心:relieved:
Docker buildx でラズパイ用のDockerイメージをクロスビルドした

# ことの始まり
僕は役職パネルっていうDiscord BOTを運用しているのだが、開発パートナーがDockerを勉強したいとのことで、Dockerを導入することにした。
しかし、通常のPCのCPUアーキテクチャはamd64で、ラズベリーパイはarm64というものだ。アーキテクチャが違うと、イメージの共有はできない。しかしラズベリーパイでビルドはあまりしたくない。
そこで、「docker buildx」でマルチプラットフォーム向けにビルドできるので使ってみた。

# 下準備（最初だけでOK）
## qemuの準備をする
```
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
```
最初と`failed to load LLB: runtime execution on platform linux/arm64 not supported` とか `error: failed to solve: rpc error: code = Unknown desc = executor failed running [/dev/.buildkit_qemu_emulator /bin/sh -c python -m venv /venv]: exit code: 1` みたいなエラーが出てきたら実行する。

## ビルダーインスンタンスを準備する
name引数は適当でもOK

```
docker buildx create  --name builder --use
```

# ビルド
```
docker buildx build .
```
みたいな感じに、いつもビルドするときと同じようにビルドすればOK。
プラットフォームを指定したいときは

```
docker buildx build --platform=linux/amd64 --load .
```
みたいな感じにプラットフォームを指定する。（複数指定に関しては後述）

## マルチプラットフォーム向けのビルド

プラットフォームを複数指定したい場合は、以下のようにすればよいのだが
**これだけだとイメージが保存できずにエラーを吐く！**

```
docker buildx build --platform=linux/amd64,linux/arm64 --load .
```
じゃあどうするのかというと、DockerHubなどのレジストリにプッシュしてあげれば良い。
プッシュはこんな感じにする

```
#imagetagは、pushしたいイメージのタグに置き換える。
docker buildx build --platform=linux/amd64,linux/arm64 --push --tag imagetag  .
```

# 参考URL
https://github.com/docker/buildx/issues/138
学習メモ(AWS CLI設定、Windows版)

# 今日の概要
自宅のWindows10 PCにAWS CLIをインストール、設定

# AWS CLIのインストール
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-windows.html

windowsの場合は.msiファイルをDL。
インストールは次へボタンをポチポチするだけ。

# 初期設定(aws configureコマンド実行)
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-quickstart.html

リージョンは東京を登録。
アクセスキーが無かったのでその発行から。
表示形式は見やすいYAMLで。

# 確認
試しにcloudformationのコマンドを打ってみる

```
c:\work\cfn>aws cloudformation describe-stacks
Stacks: []
```

とりあえずはOKっぽい
明日からバリバリつかっていきます:grin:
Visual Studio Code(VSCode)の初期設定

VSCodeの初期設定の例を残します。
参考になれば幸いです🙏

## VSCode 関連記事

- [VSCode に必ず入れておきたい拡張機能](https://qiita.com/ucan-lab/items/e85931bf8276da43cc97)

## VSCode 初期設定例

`Command` + `,` でユーザー設定画面を開く。
昔は `settings.json` のようなjson形式で設定していた記憶ですが今はGUIでしか変更できないみたい(?)

- チェックあり => `enabled`
- チェックなし => `disabled`

の表現としてます。

### Editor

#### Sticky Scroll

`enabled`

https://code.visualstudio.com/updates/v1_70#_editor-sticky-scroll

エディタの上部にクラス/インターフェイス/名前空間/関数/メソッド/コンストラクタが固定で表示される。長いコードを読む時に便利。

関連して `Notebook › Sticky Scroll` を有効化すると.mdのマークダウンの見出しも固定表示できます。

#### Auto Indent

`none`
行を入力、貼り付け、移動、タブする際にVSCode側で自動調整する。

一見便利そうに見えるこの機能は無効化してます。
コピペする時にインデントが崩れるの許せないので無効化してます。

#### Folding

`disabled`
コード折りたたみする。

折りたたむ必要性があるのか疑問です。(長いコード書くな)
誤クリックで折りたたんでしまうこともあるので無効化しておく。

#### Font Family

`Fira Code`
フォントファミリーの設定です。

プログラミング用フォント `Fira Code` を指定してます。

- https://github.com/tonsky/FiraCode

#### Render Whitespace

`all`
空白文字を表示するか。

設定して損はない。

#### Tab Size

`2`
1回のタブのスペースの数。

拡張子が付いてるものはEditorConfigに任せる。
好みですがよく使用するスペースの数で良い。

- https://editorconfig.org

#### Word Wrap

`off`
行の折り返し方法。

好みで設定する。

### Workbench

#### Color Theme

`Github Dark Dimmed`
拡張機能で入れたものを利用してます。

#### Icon Theme

`Material Icon Theme`
拡張機能で入れたものを利用してます。

#### Editor > enablePreview

`disabled`
タブを開いたときに、タブをダブルクリックしないと上書きされてしまうので無効化します。

https://qiita.com/kgoto/items/265e3eb8a82778e33cfe

### Files

#### Auto Save

`afterDelay`
自動保存します。

保存し忘れで変更が反映されないミスを防ぐために自動保存を有効にしておきます。

#### Insert Final Newline

`enabled`
ファイルの保存時に最終行に空行を挿入します。
設定して損はない。

#### Trim Final Newlines

`enabled`
ファイルの保存時に最終行以降の行をトリミングします。
設定して損はない。

#### Trim Trailing Whitespace

`enabled`
ファイルの保存時に行末の空白をトリミングします。
設定して損はない。

## さいごに

今回ご紹介した例は最小限の設定です。
自分好みに合わせて設定をカスタマイズして使いやすいエディターを作っていきましょう！
Swiftの文字列や配列の拡張まとめ

# できること

1. **スペース埋め、ゼロ埋め、左寄せ**
```"ABC"``` ->  ```"__ABC"```
```"ABC"``` ->  ```"00ABC"```
```"ABC"``` ->  ```"ABC__"```

2. **n文字ごとに区切る**
```"ABCDEFG"``` -> ```["AB", "CD", "EF", "G"]```


3. **重複する要素ががあるかないか**
```["A", "B", "C"]``` -> ```false```
```["A", "A", "B"]``` -> ```true```

4. **配列を区切って二次元配列にする**
```["A", "B", "C", "D", "E", "F", "G"]``` 
->
``` [["A", "B"], ["C", "D"], ["E", "F"], ["G"]]``` 


5. **要素数チェックをしながらオプショナル型で取り出す**
```["A", "B", "C"][at: 2]``` -> ```"C"``` (Optional)
```["A", "B", "C"][at: 3]``` -> ```nil```

# コピペ用
それぞれ独立しているので、欲しいやつだけコピペしていってね

```Extension.swift
// 1. スペース埋め、ゼロ埋め、左寄せ
extension String {
    func padding(to count: Int, with pad: String = " ", isTrailing: Bool = true) -> String {
        let main = String(self.prefix(count))
        let pads = String(repeating: pad, count: count - main.count)
        return isTrailing ? (pads + main) : (main + pads)
    }
}

// 2. n文字ごとに区切る
extension String {
    func split(by count: Int) -> [String] {
        let arrayValue = Array(self)
        if arrayValue.isEmpty { return [] }
        var parent: [[Element]] = []
        for i in 1 ... arrayValue.count {
            if (i % count == 0) {
                parent.append(arrayValue[(i - count)...(i - 1)].map{$0})
            }
        }
        if arrayValue.count % count != 0 { parent.append(arrayValue.suffix(arrayValue.count % count)) }
        return parent.map({String($0)})
    }
}

// 3. 重複する要素ががあるかないか
extension Array where Element: Hashable {
    var hasDuplicated: Bool {
        return Set(self).count != self.count
    }
}

// 4. 配列を区切って二次元配列にする
extension Array {
    func split(by count:Int) -> [[Element]] {
        if self.isEmpty { return [] }
        var parent: [[Element]] = []
        for i in 1 ... self.count {
            if (i % count == 0) {
                parent.append(self[(i - count)...(i - 1)].map{$0})
            }
        }
        if self.count % count != 0 { parent.append(self.suffix(self.count % count)) }
        return parent
    }
}

// 5. 要素数チェックをしながらオプショナル型で取り出す
extension Array {
    /// Optional<Elemnt> として取り出す
    subscript (at index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}
```

# 使い方
``` Sample.swift
// 1. スペース埋め、ゼロ埋め、左寄せ
let a = "ABC".padding(to: 5)
let b = "ABC".padding(to: 5, with: "0")
let c = "ABC".padding(to: 5, isTrailing: false)
let d = "ABC".padding(to: 5, with: "0", isTrailing: false)
// a: "  ABC"
// b: "00ABC"
// c: "ABC  "
// d: "ABC00"

// 2. n文字ごとに区切る        
let e = "ABCDEFG".split(by: 2)
// e: ["AB", "CD", "EF", "G"]

// 3. 重複する要素ががあるかないか
let f = ["A", "B", "C"].hasDuplicated
let g = ["A", "A", "C"].hasDuplicated
// f: false
// g: true

// 4. 配列を区切って二次元配列にする
let h = ["A", "B", "C", "D", "E", "F", "G"].split(by: 2)
// [["A", "B"], ["C", "D"], ["E", "F"], ["G"]]

// 5. 要素数チェックをしながらオプショナル型で取り出す
let i = ["A", "B", "C"][at: 2]
let j = ["A", "B", "C"][at: 3]
// i: "C" (Optional)
// j: nil

```

# さいごに
どれも調べたら出てきそうですが、一箇所にまとめておきたかったので。
[RSpec] 画像投稿機能 モデルスペック をかく (ActiveStorageの場合)

###目的
Railsアプリの開発中に、Model側でも画像データのバリデーションが有効か調べる。

###前提
- Rails に ActiveStorage 導入済
- Rails に RSpec・FactoryBot 導入済
- model spec 作成済

```shell:Format
- (削除箇所)
+ (追加箇所)
```

####開発環境
- Rails 6.1.3
- Ruby 2.7.3
- AWS Cloud9 (Ubuntu 18.04.5)
- RSpec 3.10 (rspec-rails 4.0.2)
  - FactoryBot

```:ディレクトリ構成
myapp
├── app ── models
(省略)
├── spec
      ├── factories
      ├── fixtures
      └── models
(省略)
└── (省略)

```

###設定

- rails_helper.rb の設定

```ruby:spec/rails_helper.rb
(省略)
RSpec.configure do |config|
  (省略)
  + config.include ActionDispatch::TestProcess::FixtureFile
end
```

- [ActionDispatch::TestProcess::FixtureFile - api.rubyonrails.org] (https://api.rubyonrails.org/classes/ActionDispatch/TestProcess/FixtureFile.html)

- [ruby on rails - Why is fixture_file_upload not available in RSpec (without ActiveRecord)? - Stack Overflow](https://stackoverflow.com/questions/54469435/why-is-fixture-file-upload-not-available-in-rspec-without-activerecord)

他の先駆者の方の設定方法もありますので、参考URLもあわせてご覧ください。(私の場合は下記の参考URLの方法ではなぜか解決できませんでした...)

###ディレクトリ作成

```shell:Terminal
$ mkdir -p spec/fixtures/files/image
```

上記で作成するディレクトリ'files'は、後ほどテストで実行するfixture_file_uploadメソッドのルートディレクトリとなります。

###テストデータ作成

先ほど作成したディレクトリにテスト用のデータを作成。

- ファイルのサイズを指定してダミーデータ作成(下記の例は jpeg 6MB)

```shell:Terminal
$ dd if=/dev/zero of=/home/ubuntu/environment/myapp/spec/fixture/image-test-6mb.jpeg bs=1M count=6
```

[ddコマンド ダミーデータ生成 - Qiita](https://qiita.com/r_o/items/ef72ad1a7c21ba8c1d15)
[dd コマンド – ファイルをコピー | Linuxコマンド.NET](https://linuxcommand.net/dd/)

###モデル側の検証
```ruby:app/models/post.rb
class Post < ApplicationRecord
  has_one_attached :image
  validates :content, presence: true
  + validates :image, size: { less_than: 5.megabytes, \
                            message: "should be less than 5MB" }
end
```

### テストを書く
```ruby:spec/factories/posts.rb
FactoryBot.define do
  factory :post do
    id { 1 }
    content { "Good" }
  end
end

```

```ruby:spec/models/post_spec.rb
require 'rails_helper'

RSpec.describe Post, type: :model do
  # 6MB以上の画像アップロードは無効であること  
  it "is invalid with an image data greater then 6MB" do
    post = FactoryBot.build(:post)
    post.image = fixture_file_upload("image/image_test_6mb.jpeg")
    expect(post.valid?).to eq false
  end
end
```

###単体テスト実行
```shell:Terminal
$ bin/rspec spec/models/posts.rb
```

###参考URL
- [Active Storage 導入環境下での単体テスト - Qiita](https://qiita.com/maca12vel/items/ee4d16827f24f69080ae)
- [Rails 6.1にあげたらundefined method `file_fixture_path' for FactoryBot::SyntaxRunner:Classになりましたとさ - Qiita](https://qiita.com/tatematsu-k/items/1cdd946cd38e69d16340)
Pythonとffmpegで動画の無音部分をカットする

# 目的
Python + ffmpeg で動画ファイル(mov)から無音部分をカットして、残った部分をそれぞれ動画ファイルにする。

前回の記事「[shotcutで動画作ってyoutube投稿してみた](https://qiita.com/igapon1/items/5486f187d7751af2ddd5) 」


# 背景
shotcutで動画を編集する前に、素材動画から不要な無音部分をカットしたかった。

参考サイト「[PythonでYouTube用動画のカットとテロップ挿入を自動化してみた！](https://kajimublog.com/python-video-cut/)」のコードは、生成する動画ファイルが一つにまとまる様だが、分割されたファイルの方が、shotcutで使いやすいと思ったので、一部修正する。


# 環境

- Windows10 バージョン2004(OSビルド19041.928)
 - Python3.9.1
 - chocolatey 0.10.15
 - 動画編集ソフト：Shotcut Version UNSTABLE-21.02.09
 - 動画再生ソフト：MPC-HC(64-bit) バージョン1.7.10(d911f14)
- 動画撮影：iPhoneX 14.4.2
 - 動画撮影アプリ：Microsoft Pix


# セットアップ

- Windowsキーを押す
- 検索ボックスに「cmd」と入力
- Ctrl + Shift + Enterを押す(管理者として実行)
- 以下のコマンドを実行する

```commandline:cmd.exe（管理者として実行）
choco install ffmpeg
```

- 途中選択肢が出る場合は、すべてy + Enterする。
- 以下のコマンドを実行する

```commandline:cmd.exe（管理者として実行）
pip install soundfile
pip install numpy
pip install moviepy
```


# やっていること
大部分は参考サイト「[PythonでYouTube用動画のカットとテロップ挿入を自動化してみた！](https://kajimublog.com/python-video-cut/)」と「[動画の無音部分を自動でカットする](https://nantekottai.com/2020/06/14/video-cut-silence/)」のコードを流用した。ただし、コード一式が見つからず、結合してもそのままでは動かなかったので修正した。

- 動画ファイルから音声ファイルを生成する。
- 音声ファイルから無音部分を除いた、残す部分を特定する
- 残す部分の動画を個別に※生成する

※shotcutで動画を結合するのは簡単なので、無音部分をカットした後の動画ファイルをバラバラに生成するコードに修正した。


# 使い方
[Git](https://github.com/igapon50/training/releases/tag/1.2.0)からSource code(zip)をダウンロードして、展開する。
展開したパスを「c:\Git\traning\」として、加工前の動画ファイルが「c:\test.mov」なら以下のコマンドを実行する。

```
cd c:\Git\traning\python\Movie
python movieCutter.py c:\test.mov
```

加工前の動画ファイル名に「_part{:2d}」を付けた、以下のファイルが作成される。

```
c:\test_part01.mov
c:\test_part02.mov
・・・
```


# おわりに
ffmpegがとても素晴らしい。今後も是非活用したい。
参考サイトは、Git URLを見つけることができなかったので、すぐに試すことができなかった。とりあえずコード一式になり、試すことができたので良かった。


# 関連リンク
## 参考サイト

- [PythonでYouTube用動画のカットとテロップ挿入を自動化してみた！](https://kajimublog.com/python-video-cut/)
- [動画の無音部分を自動でカットする](https://nantekottai.com/2020/06/14/video-cut-silence/)
- [それFFmpegで出来るよ！](https://qiita.com/cha84rakanal/items/e84fe4eb6fbe2ae13fd8)


## ドキュメント

- [FFmpeg](https://trac.ffmpeg.org/wiki/CompilationGuide)
Stripe Subscription 開始パターンごとの作成方法まとめ

Stripe を使うとサブスクリプションの決済が楽にできます。

サービスにおいてサブスクリプションを開始する際、トライアルをつける場合や、決済日を月初に固定したい場合などがあります。
サブスクリプションの開始パターンの取り方としては下記の方法があると思います。

- トライアル期間の有無
- 固定決済日の有無
    - 日割り決済の有無

上記の選択肢で6パターンのSubscription作成パターンが存在することになります。
が、トライアル終了後に日割りなしでその月の金額を請求するということは、その月の決済タイミングまででトライアルが実質終了していることになるので省かれます。
そのため、 5パターンについてRuby での例でコードを記述します。

https://stripe.com/docs/api/subscriptions/create?lang=ruby

API ドキュメントは上記をご参照ください

### 固定決済日なし、トライアルあり
よくあるサブスクリプションサービスのパターンだと思います。
トライアルが一月(30日)あって、その後、登録日を基準に毎月決済されるパターンです。
これは、`trial_end` オプションを指定するだけです。

```rb
Stripe::Subscription.create({
  customer: STRIPE_CUSTOMER_ID,
  items: [
    { price: STRIPE_PRICE_ID },
  ],
  trial_end: TRIAL_END_DATETIME,
})
```

### 固定決済日なし、トライアルなし

フリーミアムモデルで、途中から有料プランに切り替える場合はトライアル無く、サブスクリプション開始時から決済が発生する形になると思います。
これは `trial_end` を指定しません。
明示的にオプション指定する場合は `trial_end: 'now'` とします

```rb
Stripe::Subscription.create({
  customer: STRIPE_CUSTOMER_ID,
  items: [
    { price: STRIPE_PRICE_ID },
  ],
})
```

### 固定決済日あり、日割り決済あり、トライアルあり

決済を固定したいケースもあると思います。
決済タイミングが固定されていないと、ユーザーはいつ決済が行われるかわかりにくくなってしまいますが、月初に揃えるなどするとわかりやすくなります。
決済日を指定する場合は `billing_cycle_anchor` オプションを使用して決済日を指定します。

また、トライアルを含む場合、トライアル終了後に次の決済日(`billing_cycle_anchor`)までの残り日数を元に日割りの決済が行われます。

`billing_cycle_anchor` は `trial_end` より先の時間を指定する必要があります。

```rb
Stripe::Subscription.create({
  customer: STRIPE_CUSTOMER_ID,
  items: [
    { price: STRIPE_PRICE_ID },
  ],
  trial_end: TRIAL_END_DATETIME,
  billing_cycle_anchor: FIRST_BILLING_DATETIME,
})
```

### 固定決済日あり、日割り決済あり、トライアルなし

これは `trial_end` を指定しません。

```rb
Stripe::Subscription.create({
  customer: STRIPE_CUSTOMER_ID,
  items: [
    { price: STRIPE_PRICE_ID },
  ],
  billing_cycle_anchor: FIRST_BILLING_DATETIME,
})
```


### 固定決済日あり、日割り決済なし、トライアルなし

日割り決済をしない場合はオプションで `prorate: false` を指定します。
このとき、最初の決済は `billing_cycle_anchor` で指定した日時が初回の決済タイミングになります。

```rb
Stripe::Subscription.create({
  customer: STRIPE_CUSTOMER_ID,
  items: [
    { price: STRIPE_PRICE_ID },
  ],
  billing_cycle_anchor: FIRST_BILLING_DATETIME,
  prorate: false,
})
```

もし、サブスクリプション開始時に決済し、次回の決済タイミングを固定したい場合は無理矢理実現する必要があります。
`billing_cycle_anchor` を最初は指定せず、Subscription を作成し、後から `billing_cycle_anchor` を変更します。

ただ、 `billing_cycle_anchor` を変更する場合、`'now'` しか指定できません。
そのため、代替方法として、終了日が次回決済日になるトライアルを後から追加します。

```rb
stripe_subscription = Stripe::Subscription.create({
  customer: STRIPE_CUSTOMER_ID,
  items: [
    { price: STRIPE_PRICE_ID },
  ],
  prorate: false,
})

Stripe::Subscription.update(stripe_subscription.id, trial_end: FIRST_BILLING_DATETIME, proration_behavior: 'none')
```
AWS、EC2でインスタンスを作り直した際のEC2への接続エラーについて

こんにちは、今日は
# ポートフォリオをAWSにデプロイする際に起きたEC2への接続エラー
について書き残します。

以前、デプロイしていた別のアプリのインスタンスを削除し、再度インスタンスを生成、下記を入力してEC2に接続しようとした際に以下のようなエラーが出ました。

```
ssh -i ###.pem ec2-user@hostname

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
SHA256:######
Please contact your system administrator.
Add correct host key in /Users/###/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /Users/###/.ssh/known_hosts:1
ECDSA host key for hostname has changed and you have requested strict checking.
Host key verification failed.
```

原因は同一ホストに対し、認証鍵が違っていたためでした。
（今回はelastic IPは従来のまま、インスタンスのみ生成しなおした）

初めて接続した際に"known_hosts"ファイル内に認証鍵が書き加えられていたようです。
これを下記を入力し取り除きます。

```
ssh-keygen -R hostname (hostnameはこの場合、AWSのパブリックIPです。）
```
これにより"known_hosts"ファイルがアップデートされ、過去のファイルとして"known_hosts.old"という名前で保管されます。


この後、再度

```
ssh -i ###.pem ec2-user@hostname
```
を入力すると、無事EC2に接続することができました。


####　以上です！

余談ですが


当初hostnameが何を指しているのかわからなかったため、known_hostsファイルの内容を確認しようとしていました。
ファイルの内容は下記で確認することができます。

```
ssh-keygen -l -f known_hosts

以下、内容
256 SHA256:############################# hostname (ECDSA)
```

SHA256というのは、NSA（米国家安全保障局）が考案した「任意の長さのデータから固定長の出力データを返す暗号化技術」のようです。
下記サイトから勉強させていただきました。
https://gaiax-blockchain.com/sha-256#SHA-256-3

また、今回のエラー解決については
https://sayjoyblog.com/aws-ec2-identification-warning/
を参考にさせていただきました。
ありがとうございました！
【Actions】Slackに通知するJavaScriptアクションコードを作ってみた


actionsを使ってslackに通知するものは公開されていますが、そもそもactionsはどのようにして動いているかをざっくりと理解するために、公式のドキュメントを参考にしながらslackに通知するactionを実際に作ってみます。

https://docs.github.com/ja/actions/creating-actions/creating-a-javascript-action

## アクションのメタデータファイル作成

```yaml:action.yml
name: 'Slack Notification'
description: 'Sending a message from actions to slack.'
inputs:
  webhook-url:  # id of input
    description: 'Webhook url'
    required: true
outputs:
  time: # id of output
    description: 'The time we receive'
runs:
  using: 'node12'
  main: 'dist/index.js'

```

## アクションのコードを記述

JavaScriptでwebhookを使用してslackに通知するアクションコードを実装します。

```js:index.js
// require('dotenv').config();

const axios = require('axios');
const core = require('@actions/core');
const url = core.getInput('webhook-url');

const config = {
  method: 'post',
  url: url,
  headers: {
    'Content-Type': 'application/json'
  },
  data: {
    text: 'Hello from GitHub Action'
  }
};

axios(config)
.then(function(response) {
  const time = (new Date()).toTimeString();
  core.setOutput("time", time);
  // console.log(response.data)
})
.catch(function(error) {
  // console.log(error.response.data)
  core.setFailed(error.message);
})
```

### ローカルでテストするとき

actionsの``getInput``メソッドは環境変数から値を取得しています。
https://github.com/actions/toolkit/blob/6251fe73d7afcdd9367e9c994676fbf7b7420ba9/packages/core/src/core.ts#L67-L72

なのでアクションコードをローカルで実行するときはnode.jsで環境変数を設定します。node.jsで環境変数を扱うのに便利な``dotenv``をインストールします。

```
npm install dotenv --save-dev
```

インストールしたら``.env``ファイルを用意し、``webhook-url``を環境変数に設定します。
SlackのWebhook URLは[https://api.slack.com/apps/](https://api.slack.com/apps/)からAppを選択し、[**Incomint Webhooks**]から取得します。

```conf:.env
INPUT_WEBHOOK-URL=your-webhook-url
```

``index.js``でdotenvを適用します。

```js:index.js
require('dotenv').config();
```

環境変数を設定できたら、``node index.js``でアクションコードを実行します。


## モジュールコンパイル

node.jsモジュールを1つのファイルにコンパイルしてくれる``ncc``ライブラリを使用します。

```
npm i -g @vercel/ncc
ncc build index.js --license licenses.txt
```

## リポジトリに登録

作成したアクションコードをGitHubのリポジトリに登録することで、actionsのワークフローから利用できるようになります。

作成したアクションを使用するワークフローは、こちらの記事で紹介しています。

https://qiita.com/kiyo27/items/c46d67c9fc198bddfa95
【Actions】 actを使ってGitHub Actionsをローカルで実行する

``act``を使用すればGitHub Actionsをローカルで実行できます。動作確認するのにワークフローファイルをGitHubにプッシュして確認、という煩わしいプロセスがなくなります。

https://github.com/nektos/act

## 環境

os: windows10

```
> wsl -l -v
  NAME                   STATE           VERSION
* Ubuntu                 Running         2
  docker-desktop         Running         2
  docker-desktop-data    Running         2
```

## インストール

wsl2に``act``をインストールします。

```
$ curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
```

インストールできたか確認します。

```
$ which act
/usr/local/bin/act
```

## ワークフローを作成

https://docs.github.com/ja/actions/reference/workflow-syntax-for-github-actions

ワークフローファイルは``.github/workflows``ディレクトリに配置します。このディレクトリに配置されているファイルをactionsは読み取ってイベントが発生すると実行されます。ジョブで使用するアクションには、こちらの記事で作成したアクションを使用します。

https://qiita.com/kiyo27/items/66149a4074daabdfe35c

```yaml:main.yml
on: [push]

jobs:
  slack_notification_job:
    runs-on: ubuntu-latest
    name: A job to send a message to slack
    steps:
    - name: Slack notification
      id: slack
      uses: kiyo27/hello-world-javascript-action@v1
      with:
        webhook-url: ${{ secrets.WEBHOOK_URL }}
    - name: Get the output time
      run: echo "The time was ${{ steps.slack.outputs.time }}"
```

## 実行

``act``コマンドでワークフローを実行できます。引数の``event``を指定しないとデフォルトでは``on: push``が実行されます。ワークフローファイルで``secrets``をコンテキスト使用しています。``act``で``secrets``を使用するには``-s``オプションで指定します。

```
act -s WEBHOOK_URL=your-webhook-url
```
OCS Inventory-NG 2.9インストール時のエラーまとめ

#本記事について
OCSInventory-NG 2.9をインストールした際に発生したエラーとその対処方法をまとめておきます。


#OCSInventoryインストール時に発生したエラー
Perlモジュールのチェックでエラーが出た。
エラーが出るとインストールを続けるか聞かれたので、一旦インストールを終了して、足りなかったモジュール`Apache::DBI`の追加インストールを実施した。
※公式ドキュメントの通りにパッケージを入れたはずだが足りなかった。
■エラーメッセージ抜粋

```
+----------------------------------------------------------+
| Checking for required Perl Modules...                                 |
+----------------------------------------------------------+

Checking for DBI PERL module...
Found that PERL module DBI is available.
Checking for Apache::DBI PERL module...
*** ERROR: PERL module Apache::DBI is not installed !
Checking for DBD::mysql PERL module...
Found that PERL module DBD::mysql is available.
Checking for Compress::Zlib PERL module...
Found that PERL module Compress::Zlib is available.
Checking for XML::Simple PERL module...
Found that PERL module XML::Simple is available.
Checking for Net::IP PERL module...
Found that PERL module Net::IP is available.
Checking for Archive::Zip Perl module...
Found that PERL module Archive::Zip is available.
*** ERROR: There is one or more required PERL modules missing on your computer !
Please, install missing PERL modules first.
 
OCS setup.sh can install perl module from packages for you
The script will use the native package from your operating system like apt or rpm
Do you wish to continue (y/[n])?
```
■追加インストールコマンド

```bash
yum install perl-Apache-DBI
```


#PHP7.2でOCSInventoryを動作させた際のエラー
PHP7.2で動作させようとしたら、500エラーとなった。
`/var/log\httpd/error.log`を確認したところ以下のエラーが出ていることを確認。
どうやらPHP7.2では`mysqli_real_escape_string()`という関数が破棄されていて動作できないから出たエラーということ。

```
PHP Fatal error:  Uncaught Error: Call to undefined function mysqli_real_escape_string() in /usr/share/ocsinventory-reports/ocsreports/require/function_commun.php:67
Stack trace:
#0 /usr/share/ocsinventory-reports/ocsreports/require/function_commun.php(85): generate_secure_sql('select NAME,IVA...', Array)
#1 /usr/share/ocsinventory-reports/ocsreports/require/function_commun.php(37): mysql2_query_secure('select NAME,IVA...', NULL, Array)
#2 /usr/share/ocsinventory-reports/ocsreports/require/function_commun.php(346): look_config_default_values('CUSTOM_THEME')
#3 /usr/share/ocsinventory-reports/ocsreports/install.php(30): html_header(true)
#4 /usr/share/ocsinventory-reports/ocsreports/require/header.php(84): require('/usr/share/ocsi...')
#5 /usr/share/ocsinventory-reports/ocsreports/index.php(44): require('/usr/share/ocsi...')
#6 {main}
  thrown in /usr/share/ocsinventory-reports/ocsreports/require/function_commun.php on line 67
```
PHP7.2での動作は諦めアンインストールし、PHP5.6をインストールして動作することを確認。
DynamoDB操作

## テーブルの作成

```python:
import boto3
from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource('dynamodb')
table = dynamodb.create_table(
            
                TableName="テーブル名",
                KeySchema=[
                    {
                        'AttributeName': 'id',
                        'KeyType': 'HASH'
                    },
                ],
                AttributeDefinitions=[
                    {
                        'AttributeName': 'id',
                        'AttributeType': 'N'
                    },
                ],
                ProvisionedThroughput={
                    'ReadCapacityUnits': 1,
                    'WriteCapacityUnits': 1
                },
            )
```






## 項目追加

```python:
import boto3
from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table("テーブル名")
                    
                    table.put_item(
                        Item = {
                            'id': 1,
                            'name':"鈴木",
                            'people':"empty",
                        }
                    )
```


##　項目の更新

```python:
import boto3
from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table("テーブル名")
                    
                    table.update_item(
                        
                        # primary_keyのみでOK
                        Key = {
                            'id': 1
                        },
                        # set 属性名 = :任意
                        UpdateExpression="set name = :name",
                        # ':任意': '変更したい文字列'
                        ExpressionAttributeValues={
                            ':name': '田中'
                        },
                        
                    )
```



##　項目の取り出し

```python
import boto3
from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table("テーブル名")

def lambda_handler(event, context):

       queryData = table.query(
            KeyConditionExpression = Key("user_id").eq("1"),
            Limit = 1
       )
    
       return queryData["Items"][0]["name"]

```

### 参考文献

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/GettingStarted.Python.html

https://www.javaer101.com/en/article/997447.html

https://qiita.com/estaro/items/b329deafdfef790aa355#lsi-1

https://www.tantan-biyori.info/blog/2018/11/aws-dynamo.html