OmniAICreator/Qiita-1.07M huggingface dataset:
TimedRotatingFileHandlerを使ってログローテーション時にファイルを圧縮する

# 概要
タイトル通り、ログローテーション時にファイルを圧縮する方法の備忘録です😁

# ソースコード
```custom_logger.py
import os
import gzip
import shutil
from logging import getLogger, Formatter, StreamHandler, DEBUG, INFO
from logging.handlers import TimedRotatingFileHandler


def namer(name):
    return name + ".gz"


def rotator(source, dest):
    with open(source, 'rb') as f_in:
        with gzip.open(dest, 'wb') as f_out:
            shutil.copyfileobj(f_in, f_out)
    os.remove(source)


def setup_logger(name, filename: str, when='MIDNIGHT', backupCount=30):
    # create formatter
    formatter = Formatter('%(asctime)s - %(levelname)s - %(filename)s(%(lineno)d) - %(funcName)s - %(message)s')

    # create a file handler
    file_handler = TimedRotatingFileHandler(filename=filename, encoding='UTF-8', when=when, backupCount=backupCount)
    file_handler.setLevel(INFO)
    file_handler.setFormatter(formatter)
    file_handler.rotator = rotator
    file_handler.namer = namer

    # create a logger
    logger = getLogger(name)
    logger.setLevel(DEBUG)
    logger.addHandler(file_handler)

    return logger
```

# 使い方
下記の使い方の場合、
TimedRotatingFileHandlerの引数のwhenに「S」を設定しているので
秒単位でログがローテーションされgz形式に圧縮されます

```main.py
from custom_logger import setup_logger

logger = setup_logger(name=__name__, filename='root.log', when='S')

logger.debug('debug message')
logger.info('info message')
logger.warning('warning message')
logger.error('error message')
logger.critical('critical message')
```

# 結果
![スクリーンショット 2023-05-24 213732.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3333416/d254bac2-59d8-b9ca-6af1-5856b22e1eda.png)

# 参考サイト
https://docs.python.org/ja/3/howto/logging-cookbook.html#cookbook-rotator-namer
転職してから半年が経ったので振り返り

どうもこんにちはたくびー(@takubii)です。
今回は転職してから半年が経ったので今までの振り返りをしていきたいと思います。
ざっくりと転職前と後でどう変わったのか以下に書いておきます。
|  | 転職前 | 転職後 |
|:-:|:-:|:-:|
| 業務形態 | SES | 自社開発 |
| 業務内容 | Webアプリの保守・運用 | Web・アプリ開発 |
| 勤務体系 | 出社→フルリモート | 出社 |
| 使用技術 | Windows,C#,<br />Javascript(jQuery),HTML,CSS | Mac,Linux,Docker,<br />Python,React,React Native,<br />Typescript,Javascript,HTML,CSS |
| 在籍期間 | 約4年間 | 約半年間 |

## 転職の目的
一番大きな理由は給料をあげることでした。
前職は毎年昇給はしていましたが、同年代のエンジニアの平均と比べるとかなり低かったので自分の価値をあげたいという理由です。

また、開発環境ももう少しモダンなところでやってみたいという気持ちもあったので、転職活動時は使用技術も重要な観点になっていました。

## 転職後の半年で感じたこと
フルリモートから週5出社になったことは正直自分が思っていたよりもツラかったです。
出社になるというのは面接時で分かっていたので、会社に歩いて行ける場所で一人暮らしも始めたのですが、これらのストレスが一気に来て最初は大変でした。（主にメンタル面で）
この辺りは時間をかけて慣らしていくしかないと理解はしていますが、なかなか体が追いつかない感じです。

業務の方は最初はOJT的な感じでひたすらDockerの環境構築とPythonプログラムのコードリーディングをしていました。
今まではDockerを使う現場ではなかったので、この期間で基礎的なことを学べたことはとてもよかったなと思います。

その後は新規アプリの開発案件にアサインされたので、ひたすらにReact Nativeの勉強などをしながら開発も並行に行っているという感じです。
前職と比べてモダンなフレームワークに触れられて、コードを書く量も増えたのでエンジニアとしてはちょうど良い業務感です。

また、福利厚生が入社前に考えていたよりもよく、私生活面でも助かっているのでその点はかなり満足しています。

## 今後について
今の新規アプリの開発がひと段落するまではこのまま頑張って働こうかなって考えています。
前職でもそうでしたが、職場に慣れるまで時間がかかるタイプなのでしばらく働いていれば職場環境、人間関係ともに慣れていき居心地が良くなってくれないかなと期待しています。
一番懸念しているところは昇給についてですが、こちらはしばらくしないと分からないことなのですが割と気にはしています。

やっぱり年齢を重ねていくごとにお金が必要になる面が増えていくと感じているので、給料はなるべくいただきたいところです。
基本的には転職活動を行う予定はありませんが、良い話があれば聞きにいきたいという気持ちはありますので、お気軽にお声がけください。Twitterアカウントは[こちら](https://twitter.com/takubii0)になります。

## 終わりに
ここまで読んでいただきありがとうございます。
こうやって自分がどういう目的を持って転職したのか、転職した後のギャップや良かったところなどを振り返ってみると次に活かせそうな気がします。
それでは今回はここまでにしたいと思います。
また機会がありましたらお会いしましょう。
TerraformでECRのリポジトリを作成しdocker build & pushする

`terraform apply`コマンドの中でECRのリポジトリを作成し、docker build & pushまで実行してしまうサンプルです。

Terraformのファイル本体は以下の通りです。

ECRのリポジトリ作成のほかに `null_resource` というリソースでスクリプトを実行しています。スクリプトの中身は後述しますが、単にDocker build & pushしているだけのスクリプトです。Dockerイメージ作成時に依存するファイルが変更されたときにのみ実行されるよう `triggers` を指定するのがポイントです。

```main.tf
variable aws_profile {}
variable aws_region {}
variable ecr_repository_name {}

terraform {
  backend "s3" {
  }
}

provider "aws" {
   profile = var.aws_profile
   region = var.aws_region
}

################################################################################
# ECR
################################################################################

resource "aws_ecr_repository" "default" {
  name = var.ecr_repository_name
  force_delete = true # terraform destroyしたときにリポジトリが削除されるように
}

################################################################################
# docker build & push
################################################################################

resource "null_resource" "default" {
  triggers = {
    file_content_sha1 = sha1(join("", [for f in ["build-docker.sh", "Dockerfile"]: filesha1(f)]))
  }

  provisioner "local-exec" {
    # docker build & push
    command = "sh ./build-docker.sh"
  }
}

################################################################################
```

Terraformの変数定義は、AWS環境の指定やECRリポジトリ名を以下のように書きます。

```terraform.tfvars
aws_profile="default"
aws_region="ap-northeast-1"
ecr_repository_name="sample"
```

Docker build & pushしているスクリプトの中身は以下の通りです。手抜きのため、環境に依存するパラメータを上記 `terraform.tfvars` から直接読み込んでいます。 `tfvars` ファイルをシェルスクリプトファイルとみなして読み込ませる反則技です。Terraformのファイルから環境変数を通してパラメータを渡すこともできますが、 `build-docker.sh` を単独で実行できるようにするためです。

```build-docker.sh
#!/bin/bash

. ./terraform.tfvars

aws_account_id=$(aws --profile $aws_profile sts get-caller-identity --query 'Account' --output text)

# Docker login
aws --profile $aws_profile --region $aws_region ecr get-login-password | docker login --username AWS --password-stdin $aws_account_id.dkr.ecr.$aws_region.amazonaws.com

# Build image
docker build -t $aws_account_id.dkr.ecr.$aws_region.amazonaws.com/$ecr_repository_name .

# Push image
docker push $aws_account_id.dkr.ecr.$aws_region.amazonaws.com/$ecr_repository_name
```

※Dockerfileも同じディレクトリに置き、 `docker build` できることが前提です。
DockerコンテナでMysqlにログイン出来ません

## はじめに
Dockerを使ってMySQLデータベースを構築しようと思いましたが、初めてのデータベース構築ですので、色々と苦戦しました。
docker-compose.ymlファイルの書き方やボリュームの設定などトライアンドエラーの連続でした。特にユーザー名、パスワードを入力したら、エラーになり、ログイン出来ないことが特に苦戦しました。

## MySQLにログイン
DockerによるMySQLの構築が完了し、MySQLにログインしてSQLの勉強をしようと思って、**#> mysql -u user -p**を入力してEnterを押したら、***エラーでした！***
```bash:bash
ERROR 1045 (28000): Access denied for user 'test'@'localhost' (using password: YES)
```
色々と調べたら、ユーザー権限がないとか、パスワードが正しくないとか、色々と試しましたが、全部解決出来ませんでした

## 検索地獄
数時間をかけて、色々なサイトを検索しました。英語を読めないのに、英語のサイトまで探って、翻訳サイトを使って、翻訳したりして、翻訳文の意味が分からなかったり、解決法は効果がなかったりして、中々解決出来ませんでした。検索したサイトの一つがDockerのボリュームを削除して作り直すと治るという話が有ったので、試してみましたが
```bash:bash
#> docker volumes rm ボリューム名
```
結局ログイン出来ませんでした。
最終的に永続化ボリュームのディレクトリーを削除して、作り直したらログイン出来ました。
## 最後
永続化ディレクトリーを削除したら、作った意味がなくなる為、また色々調べて、ルートでログイン出来るようにして、MySQL内ユーザーとパスワードを追加して、再ログインしてみたら、出来ました。
最初からルートでログイン出来るようにすれば、良かったな！失敗失敗。
【iOS】画面全体を録画する

# はじめに
特定のWindowやWindowSceneを画面録画したくて、CVPixelBufferに変換するなどして頑張ってた。 
複数Windowの描画や、画面の回転(orientation)を考慮しようとすると割と大変だった。 
これは成果物 
 
https://github.com/p-x9/ScreenCapture


一方で、スクリーン全体を録画したいのなら、ReplayKitを使用すると簡単に行えることが分かったので、そのやり方をメモに残します。 

## 本編
### 準備

importして`RPScreenRecorder`のsharedインスタンスを取得

```swift 
import ReplayKit

let recorder = RPScreenRecorder.shared()
```

### 録画開始 

```swift 
guard recorder.isAvailable,
      !recorder.isRecording else {
    return
}

recorder.startRecording()
```

上記のメソッドを呼ぶと、画面の取り込みをしても良いかのダイアログが表示されるようになっている。 
一度同意しても、startRecordingを呼ぶたびに毎回表示されるようだ。

![A.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377767/a30043d1-89b5-d6c6-2547-3f2b054f17bf.png)


### 録画終了
outputURLを指定して`stopRecording`を呼び出すと、動画ファイルが保存される。

```swift 
let outputURL = FileManager.default.temporaryDirectory.appending(components: "output.mp4")

recorder.stopRecording(withOutput: outputURL) { error in
    print(error.debugDescription)
}

```

# おわりに 
owari

画面全体を録画したいなら、はじめにで示した、UIView->CVPixelBufferの方法より、明らかにパフォーマンスが良い。
同時にアプリ内音声やマイクも使用可能である。 

IOKitのIOSurfaceを使用すれば、RepleayKitを使わずとも同じくらいのパフォーマンスで録画できると思うが、プライベートメソッドを触ることになるので、AppStoreには出せないかもしれない。
React x react-ga4 x CookieConsent

# インストール
```
npm i react-ga4
npm i react-cookie-consent
```

# コード
```jsx:App.jsx
import { Cookies, getCookieConsentValue } from 'react-cookie-consent';
import ReactGA from 'react-ga4';
// ...
const cookieName = 'accept-cookie';
const App = () => {
  const acceptCookie = useCallback(() => {
    ReactGA.initialize(process.env.REACT_APP_ANALYTICS_KEY);
  }, []);
  const declineCookie = useCallback(() => {
    const cookie = Cookies.get();
    Object.keys(cookie).forEach((key) => {
      if (key !== cookieName) Cookies.remove(key);
    });
  }, []);
  useEffect(() => {
    if (getCookieConsentValue(cookieName) === 'true') {
      ReactGA.initialize(process.env.REACT_APP_ANALYTICS_KEY);
    } else {
      const cookie = Cookies.get();
      Object.keys(cookie).forEach((key) => {
        if (key !== cookieName) Cookies.remove(key);
      });
    }
  }, []);
  // ...
  return(
    // ...
    <CookieConsent
      location='bottom'
      buttonText={/* 同意ボタンの文字列 */}
      declineButtonText={/* 拒否ボタンの文字列 */}
      cookieName={/* クッキーの名前 */}
      style={/* css */}
      buttonStyle={/* css */}
      enableDeclineButton={/* 拒否ボタンを表示するか */}
      expires={/* 有効期間 */}
      onAccept={acceptCookie}
      onDecline={declineCookie}
    >
      {/* Cookieの使用について同意を求める文章 */}
    </CookieConsent>
  )
};
```

# 文章例
当サイトではアクセス解析のためにCookieを使用しています。Cookieを許可するか否かを選択してください。
Cookieの利用についてはプライバシーポリシー及び利用規約をご確認ください。
Back Translationの基礎【Back Translationについて】#1

# はじめに
私はB4から自然言語処理研究室に配属になり、M2になった今年までNMT(Neural Machine Translation,ニューラル機械翻訳)の研究を行っています。メインで取り扱っているのはdata augmentation(データ拡張)によるモデルの精度改善なのですが、NMTのデータ拡張を語る上では**Back Translation**という手法を避けては通れません。

この２年間でそれなりにノウハウも溜まってきたので、この機会にアウトプットしてみたいと思います。ご覧になった方の参考になれば幸いです。

# Back Translationとは何か？
[この手法は2016年にSennrichらの論文「Improving Neural Machine Translation with Monolingual Data」内で発表されました。](https://aclanthology.org/P16-1009/)

NMTは基本的に大規模な対訳コーパスを用意し、Transformerなどのアーキテクチャを用いて機械翻訳モデルを構築します。具体的には数百万〜数千万個の文章が最低限必要ですし、翻訳精度を上げようとすればするほど更に桁違いの学習データが必要になってきます。

ここで、いくつかの問題が生じます。例えば
- **言語によって得られる資源の量に格差がある。** 英語などメジャーな言語のデータに比べて日本語のデータは母数がはるかに少ないし、当然収集できる対訳コーパスのサイズも小さくなる。
- 対訳コーパスは基本的にあるドメイン（分野）に特化している（例えばWikipediaの同一記事の英語版と日本語版から得た文とか）。よって完成した翻訳モデルは、**翻訳したい文章がどのドメインに属するかによって翻訳の精度に偏りが生じる。** （上記の例だとWikipedia記事中に現れない言葉や話し言葉・砕けた表現を使った文章の翻訳精度はどうしても低くなる。）

これらの対策となりうる手法がBack Translationです。それでは、順を追って説明していきます。

### データの用意
ソース言語を英語、ターゲット言語を日本語とし、精度の高い英日翻訳モデルが欲しいという状況を仮定します。ここで以下のデータを用意します。
- 英日対訳コーパス$D_E,D_J$（それなりのサイズ）
- 日本語の単言語コーパス$M_J$（大きめのサイズ）

後者は要するに日本語の文章を片っ端から集めたデータと思ってください。当然ですが対訳コーパスに比べて簡単に集められるので、まあまあ大量に用意できますよね。ここでは対訳コーパスはWikipedia記事を収集したもの、単言語コーパスはTwitterからアニメに関するツイートを収集したものと考えましょう。

### 翻訳モデルの学習
対訳コーパスを用いて英語→日本語方向と日本語→英語方向の翻訳モデルを学習します。サイズが小さいのでこの時点ではどちらのモデルも大した精度ではありません。

### 逆方向に翻訳
ここで、最終的な目的である英日翻訳とは逆方向となる日英モデルを用いて$M_J$を翻訳し、新たな英語文$M_E$を生成します。

先述の通りドメインの違いがあるため、$M_E$**はノイズや文法のミスに塗れた汚い英語文になっていることに留意してください。**

### 新しいデータを用いて再学習
そうすると、$D_E+M_E$**と**$D_J+M_J$**のペアを新たな対訳コーパスとして扱うことができます。** この対訳コーパスで1から再学習しても良いですし、先程作成した英日モデルのパラメータを引き継いでも構いません。 **いずれにしても、上記の行程により対訳コーパスのサイズが大きく水増しされたことにより、当初のモデルより精度の高い翻訳モデルを作ることが可能になります。** 

また、学習データに$M_J$が入り込んだことにより、**このモデルは「Wikipedia記事」と「アニメに関するツイート」の2つのドメインに対応しているということが考えられます（これを「ドメイン適応」と言います）。**


### なんで"Back Translation"なの？
先述の通り$M_E$は必ずしも文法的・意味的に正しくないノイズの多い文になりますが、これが新たな対訳コーパスのソース言語（英語）側に使われています。直感的に考えて学習データの品質が悪ければモデルに悪影響を及ぼすのでは？と考えがちですが、今回の英日翻訳モデルにおいて英語側のデータは正しい日本語文を生成するための条件・前提の役割にすぎません。目的はあくまで正確なターゲット言語（日本語）文を生成することなので、英語文にノイズがあることは必ずしもモデルの性能悪化には繋がりませんし、**むしろノイズが混じることでモデルの頑健性が上がることが期待できます**。[（実際、あえてソース側のデータの一部の単語orトークンをランダムに削除した対訳ペアを学習データに追加することで、モデルの精度を上げるという方法があります。）](https://aclanthology.org/P15-1162/)

逆に英語の単言語コーパスを翻訳して日本語文を生成した上で同じことをやろうとすると（つまり日本語側にノイズの多い対訳コーパスを追加データにしてしまうと）、結果的に大してモデルの精度は上がりません。英日モデルの精度を上げるには、逆方向である日英モデルで単言語コーパスを翻訳することで追加の対訳コーパスを作らなければならないのです。この**逆方向の翻訳で順方向のモデルを改善する**というのが、"Back Translation"という名前の由来となっています。

# まとめ
Back Translationの基本的な流れを説明しました。

蓋を開けてみればシンプルな方法ではありますが、実際に数多くのタスクでBack Translationによって翻訳モデルの精度が大幅に改善すること、ドメイン適応の面でも大きな効果があることがこれまでに報告されています。また、本手法に一工夫を加えたり本手法を反復的に繰り返すことでさらに大きくモデルを改善するという方法も多数存在します。

それらに関しては#2の記事で説明していく予定です。

最後まで読んでいただき、ありがとうございました！
【データサイエンティスト検定】メモ

## バージョン管理ソフトウェアについて
**バージョン管理**：開発するソフトウェアのソースファイルを蓄積し、ソースコードの変更を追跡管理する機能。

オープンソースのバージョン管理システムとして有名なものを以下に二つ。

**Git**：GitをベースとしたGitHub（オープンソースソフトウェアの開発ソースやドキュメントの公開に活用）が有名。分散レポジトリ方式を採用。

**Subversion**：集中リポジトリ方式を採用。リポジトリを集中管理するSVNサーバーがあり、各開発者はそのサーバーからソースを部分的（もしくは全体）を取得し、開発作業を行う。


## Dockerについて
一つのコンピュータ上に仮想的に複数のコンピュータを稼働させているように見せる技術を**仮想化**と言う。

**Docker**は**コンテナ方式**の仮想化技術の中で最も多く利用されているオープンソースの仮想化ソフト。


## 通信プロトコルについて
**通信プロトコル**：通信を行うための規格。

|通信プロトコル    |概要             |
|----------|--------------------|
|HTTP (Hyper Text Transfer Protocol)|主にWebブラウザがWebサーバーと通信する際に使用される通信プロトコル。  |
|HTTPS (HTTP Secure)|上記HTTPの通信がSSLやTSLで暗号化され、盗聴や改ざん、なりすましを防止できる通信プロトコル。|
|FTP (File Transfer Protocol)|主にネットワーク上のクライアントとサーバーとの間でファイル転送を行うための通信プロトコル。|
|SSH (Secure Shell)|暗号や認証の技術を利用して、安全にコンピュータ間で通信するための通信プロトコル。|
|Telnet|端末から遠隔地にあるサーバー等を操作する際に使用される通信プロトコル。


## データレイク・データウェアハウス（DWH）・データマート
**データレイク**：データをそのままの形で集めて保存できる格納庫。

**データウェアハウス (DWH)**：基幹系システムなどから収集したデータを活用するための保管場所。データを使いやすいように整理済み。

**データマート**：データの利用目的などに応じて必要なものだけを抽出し、利用しやすい形に加工したうえで格納する場所。
【PowerAutomate】SharePoint　「ファイルの更新」アクションを使ってみた。

# 概要

PowerAutomateのSharePointアクションにあるファイルの更新を使った記事があまり見つからなかったので、書いてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2401966/df010873-6224-4aff-e7e0-c05668846927.png)

# 結論

ファイルコンテンツの項目に更新したい内容(文字列)をいれるとあっさり更新(上書き)できた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2401966/79ba2bdd-3575-da80-ce71-d520e3b5398d.png)


ついでにjson形式のデータも更新できるか試してみる。
結果、こちらもできた。

「チャネルの一覧表示」アクションで取得した"body"を「作成」アクションで出力された値をそのまま入れてみた。
（「チャネルの一覧表示」アクションの"body"はファイルコンテンツ欄でそのまま使えなさそうだったので、一度「作成」アクションを入れてます。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2401966/f6dc2f1c-f971-ef51-679d-58ff1a6e11fe.png)

# 使い道・・・？
PowerAppsで使用するデータ格納先としてSharePointリストやExcelのテーブルなどが挙げられる。今回、JSON形式も更新できることがわかったので、一つのファイルで階層表現が必要な場合にはJSONファイルで管理するのもありなのかもしれない。
C#でメソッドをモック化してテストする

署名検証みたいな実物がないとテストがし辛い処理で、実物が来るまで一時的にモックを使ってテストをしたい部分があった。
諸々の事情で別クラスに分けるわけにもいかず、どうにかならないものかと思って書いたもの。
この発想を忘れないように書き記しておく。

### 環境
.NET6
xUnit
[NSubstitute](https://www.nuget.org/packages/NSubstitute)（モックライブラリ）

#### テストしたいコード

```c#
public class Receipt
{   
    public string Invoke(string token)
    {
        // 検証
        if (!Verify(token))
        {
            // 検証失敗
            throw;
        }

        return "";
    }

    private bool Verify(string token)
    {
        // 検証処理省略
        return true;
    }
}
```

大雑把に書くとこんな感じの処理。
Verifyメソッドは実物がないと動かないのでそれまではモック化しておきたい。

#### スタブを作る
まずモック化したいメソッドをインターフェースで抽象化しておく。

```c#
public interface IVerify
{
    bool Verify(string token);
}
```

#### スタブからメソッドを参照する
コンストラクタで渡された実装済みのインスタンスからメソッドを参照するようにコードを追記。
このときモック化したいメソッドがpublicになってしまうので本当にその場しのぎ感がある。

```c#
public class Receipt
{   
    private readonly IVerify _verify;

    public Receipt(IVerify verify)
    {
        _verify = verify;
    }

    public string Invoke(string token)
    {
        // 検証
        if (!_verify.Verify(token))
        {
            // 検証失敗
            throw;
        }

        return "";
    }

    public bool Verify(string token)
    {
        // 処理省略
        return true;
    }
}
```

#### UnitTest側
```c#

    [Fact]
    public void Test200()
    {
        // Verifyをモック化
        var verify = Substitute.For<IVerify>();
        verify.Verify(Arg.Any<string>()).Returns(true);

        // モックを渡す 
        var receipt = new Receipt(verify);

        // テスト対象
        var result = receipt.Invoke("token");
        Assert.Equal(result, "");
    }
```

これでその場しのぎだがテストができるようになった。
以上
How to add multiple ssh keys for the same host

## Create ssh directory

```sh
mkdir ~/.ssh
mkdir ~/.ssh/github
mkdir ~/.ssh/devops
```


## Create ssh key

### ssh key for Github

```sh
ssh-keygen -t rsa -b 4096 -f ~/.ssh/github/${accountName}/id_rsa -C "test@mail.com"
```

### ssh key for Azure DevOps

```sh
ssh-keygen -t rsa -b 4096 -f ~/.ssh/devops/${organizationName}/id_rsa -C "test@mail.com"
```

## Create .ssh/config

```sh
vi ~/.ssh/config
```

```sh
Host github-${accountName}
HostName github.com
  User git
  AddKeysToAgent yes
  IdentitiesOnly yes
  UseKeychain yes
  IdentityFile ~/.ssh/github/${accountName}/id_rsa

Host devops-${organizationName}
HostName ssh.dev.azure.com
  User git
  AddKeysToAgent yes
  IdentitiesOnly yes
  UseKeychain yes
  IdentityFile ~/.ssh/devops/${organizationName}/id_rsa
```

## Copy and paste public key to Github

```sh
pbcopy < ~/.ssh/github/${accountName}/id_rsa.pub
```

## Copy and paste public key to AzureDevOps

```sh
pbcopy < ~/.ssh/devops/${organizationName}/id_rsa.pub
```

## Clone repository

### Github

```sh
git clone git@github-${accountName}:${accountName}/${projectName}.git
```

### DevOps

```sh
git clone git@devops-${organizationName}:v3/${organizationName}/${projectName}
```
私の中のおすすめAI画像生成3選を紹介します！

はじめに
Qiitaのアイコン画像が欲しいとおもい、AIでつくれないかなと試行錯誤して、探したAI画像作成サイトを紹介し酔うと思います！

# 1位 Dream Studio
[Dream Studio](https://beta.dreamstudio.ai/generate)は、おすすめです！
想定と近い結果を返してくれたり、かわいいイラストを描いてくれるので、一番気に入っています。

30クレジット程、初回に付与されるため大体10回くらい無料で画像を生成できます。
その後は、有料になります

# 2位 DALL-E
[DALL-E](https://labs.openai.com/)は、OpenAIが提供するサービスです。
30クレジット程、初回に付与され、10回くらい無料で画像を生成できたと思います。
その後は、有料になります

# 3位 Stable Diffusion
[Stable Diffusion](https://stablediffusionweb.com/)は無料でいくらでも生成できます。ただし、なかなか想定通りの画像が生成されないので、3位かなと考えています。


# 最後に
無料で使えるので、みなさんもぜひ一度使用してみてはいかがでしょうか。
PowerShellでフォルダの容量をしらべて、ＨＴＭＬ出力してみた件

Powershellでフォルダの容量をしらべてＨＴＭＬに出力する必要があるためのコード。

```
$path = "E:\hoge"

Get-ChildItem $path | ? PSIsContainer | % {
    $subFolderItems = (Get-ChildItem $_.FullName -Recurse | where Length | measure Length -sum)
    [PSCustomObject]@{
        Fullname=$_.Name
        MB=[decimal]("{0:N2}" -f ($subFolderItems.sum / 1MB))
    }
}
$path = "E:\hoge"

$html = @"
<html>
<head>
    <title>Folder Sizes</title>
    <style>
        table, th, td {
            border: 1px solid black;
            border-collapse: collapse;
        }
        th, td {
            padding: 5px;
            text-align: left;
        }
    </style>
</head>
<body>
    <h1>Folder Sizes</h1>
    <table>
        <tr>
            <th>Folder Name</th>
            <th>Size (MB)</th>
        </tr>
"@

Get-ChildItem $path | ? PSIsContainer | % {
    $subFolderItems = (Get-ChildItem $_.FullName -Recurse | where Length | measure Length -sum)
    $html += "<tr><td>$($_.Name)</td><td>$([decimal]("{0:N2}" -f ($subFolderItems.sum / 1MB)))</td></tr>"
}

$html += @"
    </table>
</body>
</html>
"@

$html | Out-File "E:\\\\hoge\\\\output.html"
```

参考にしたサイト：https://it-study.info/powershell/folder-subfolder-check/
AWS User Notifications を試してみる

# 概要

https://aws.amazon.com/jp/about-aws/whats-new/2023/05/aws-user-notifications-available/

- Health イベント、CloudWatch アラーム、EC2 インスタンスの状態変更などといった AWS サービスからの通知を、一元的に設定および確認できる。

- コンソールの通知センターでは、アカウント、リージョン、およびサービスにまたがる通知を確認できる。

- E メール、Chatbot、コンソールモバイルアプリに通知できる。

※コンソールモバイルアプリへの通知については、次の記事で公開されているようです。

https://aws.amazon.com/jp/about-aws/whats-new/2023/05/aws-console-mobile-application-launches-push-notifications/

- これまでも個別に EventBridge ルールを作成することはできました。要件に応じて今後は AWS User Notificaitons を利用する事で、一元的に管理ができる。また整形された形で通知内容がわかり易くなるため、Email に送信する場合は、SNS から　Lambda を挟んでといった処理も不要になるかと思います。

# やってみた

今回は単純ですが、スナップショットが作成された場合に、Eメールに通知するように設定します。

「通知設定を作成」をクリック。
![スクリーンショット 2023-05-24 18.51.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/35d240ba-5945-0e35-8135-ed77cd13a8d7.png)

通知データが保存され、処理されるリージョンを選択。
![スクリーンショット 2023-05-24 18.52.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/b74f23cd-a7e8-9ed5-16d7-e24f1a419671.png)

イベントルールで、EBSスナップショットが作成された場合に、通知を受け取るルールを作成します。
![スクリーンショット 2023-05-24 18.58.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/2ce8dc0f-4acc-b25c-507f-3cfdf45539a1.png)

```json
{
  "detail": {
    "event": [
      "createSnapshot"
    ]
  }
}
```

集約設定では、通知の受信頻度を選択できるようです。
![スクリーンショット 2023-05-24 19.04.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/a07f7856-5a84-724a-7387-d6db26ee0ef7.png)

今回は、Emailに配信するように設定します。
![スクリーンショット 2023-05-24 19.05.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/7ab573b1-19a6-6b76-f141-8c184fa2ad9e.png)

Email　に Email verification　が届くので、「Verify　email」をクリックします。
![スクリーンショット 2023-05-24 19.11.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/a86a938a-4a84-2258-63b4-0231313c675f.png)

EBSのスナップショットを作成し、Emailに通知がされたことを確認します。
![スクリーンショット 2023-05-24 19.25.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/8c60b75b-e0bb-d0ac-9b5a-676234d67111.png)

コンソールの通知センターでも確認できます。
![スクリーンショット 2023-05-24 19.26.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/89b8d3e8-857b-9f69-16b9-a08efc5db2d3.png)

AWS User Notifications で通知設定を行うと EventBridge ルールを作成してくれるようです。
![スクリーンショット 2023-05-24 19.38.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/a4c1e594-e160-14dd-5959-a095397770e0.png)
テスト96

```vb
Private Declare PtrSafe Function URLDownloadToFile Lib "urlmon" Alias "URLDownloadToFileA" _
(ByVal pCaller As Long, _
ByVal szURL As String, _
ByVal szFileName As String, _
ByVal dwReserved As Long, _
ByVal lpfnCB As Long _
) As Long
    
Sub ie()

'IEまたはEdgeの特定のページから
'ファイルをDLする......〆(･ω･*)ﾒﾓﾒﾓ

Dim objIE As New InternetExplorer
Dim htmlDoc As New HTMLDocument
Dim objXML As New MSHTML.HTMLDocument
Dim objITEM As Object
Dim lngRes As Long
Dim strpath As String
Dim strURL As String

Set htmlDoc = objXML.createDocumentFromUrl("https://www.○○○", vbNullString)
Set objIE = New InternetExplorer
objIE.Visible = True

'保存先
strpath = "C:\Users\User\Desktop\テスト\aaa.xlsx"

'インターネットの特定のページを開く
objIE.Navigate "https://www.○○○"

For Each objITEM In htmlDoc.getElementsByTagName("A")

    If objITEM.Title = "AAA" Then
    
        Stop
        
        lngRes = URLDownloadToFile(0, objITEM.href, strpath, 0, 0)
         
        If lngRes = 0 Then
        
            MsgBox "ダウンロード完了"
            
        Else
        
            MsgBox "ファイルをダウンロードできませんでした"
            
        End If
    
    End If
    
Next

End Sub
oneloginでOTPによるMFAを設定したのに、従業員にデバイスの登録が強制されない。

# これなに
完全に忘備録。自戒。この件から得られる教訓は「設定画面は隅々まで読もう」ということ。

# onelogin再活用月間
自分が今の会社に入社して約1年、onelogin導入から2年近く経とうとしていますが、やっとこさ全ユーザーにログイン時にMFAでの認証を強制するポリシーの適用をすることができた。

設定も済んだところでユーザーを強制ログアウト。再ログインする時にMFAデバイスを登録しようね〜〜画面が出てくるはず！と思ったら普通にログインできてしまった。これはいけない。なぜだ。

# 設定は隅々まで読もう
同僚と管理画面のポリシーの設定画面を一個ずつ確認したらこんな項目が
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3328170/6b989eee-6a43-b50f-ebaa-bfbf762ceb8c.png)

:::note info
管理画面のSecurity > Policy > {任意のPolicy｝ > MFA の一番下
:::

要はMFA用の端末を誰に登録させるかって話。
これが「Administrator Only(管理者のみ)」になってたからってわけ。

こいつを
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3328170/2ad8d2f2-36c8-cdce-50b7-11d0f88dac75.png)

「All users(みんな)」に変えてやったら解決した。

# まとめ
設定は隅々まで、読もう。
こういった小さな発見もアウトプットしていくと、いつか誰かを助けられるかもしれない。
【devise】新規登録でカラムを増やすには

本文は、カラムの追加方法は省きます。

初期状態の devise は、サインアップ、サインイン時に「email」と「パスワード」しか受け取ることを許可されていない。なので現状では 他のカラム を作成して入力しても、データとして保存することはできない状態です。

ここで、こちらで指定したデータは保存できるよう、許可を与える必要があります。
deviseのストロングパラメータを編集する場合、ApplicationController に記述していきます。deviseのコントローラは直接修正できないため、全てのコントローラに対する処理を行える権限を持つ、ApplicationControllerに記述する必要があります。

ApplicationControllerはapp/controllersフォルダのapplication_controller.rbファイルにあります。ひとまず、以下のように記述を追加していく。
~~~application_controller.rb
class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected

  def configure_permitted_parameters
    devise_parameter_sanitizer.permit(:sign_up, keys: [:カラム名])
  end
end
~~~
以上が記述内容です。

~~~application_controller.rb
before_action :configure_permitted_parameters, if: :devise_controller?
~~~
上記のように記述することで、devise利用の機能（ユーザ登録、ログイン認証など）が使われる前にconfigure_permitted_parametersメソッドが実行されます。

そして、
~~~application_controller.rb
def configure_permitted_parameters
  devise_parameter_sanitizer.permit(:sign_up, keys: [:カラム名])
end
~~~
上記の　configure_permitted_parametersメソッドでは、devise_parameter_sanitizer.permitメソッドを使うことでユーザー登録(sign_up)の際に、カラム名のデータ操作を許可しています。先ほど述べたように、これはストロングパラメータと同様の機能です。

※privateは記述をしたコントローラ内でしか参照できません。
一方、protectedは呼び出された他のコントローラからも参照することができます。

以上
ラジオボタンで選択解除をする（v-radio-group, v-radio）

vuetifyのv-radio-group/v-radioを使ったラジオボタンの実装において、現在選択されているものをクリック/タップした場合に選択解除する

選択を解除するというとラジオボタンの意図から外れると思うのでフォーム設計を見直すのがベターですが、やる必要が出た時用メモ

# バージョン

nuxt3, vuetify3

```
"nuxt": "^3.2.3"
"vuetify": "^3.1.7"
```

# 実装

```vue:sample.vue
<template>
  <v-radio-group v-model="hoge" @click="clearRadioSelected($event, 'hoge')">
    <v-radio label="はい" value="1"></v-radio>
    <v-radio label="いいえ" value="2"></v-radio>
  </v-radio-group>
  <v-radio-group v-model="fuga" @click="clearRadioSelected($event, 'fuga')">
    <v-radio label="YES" value="1"></v-radio>
    <v-radio label="NO" value="2"></v-radio>
  </v-radio-group>
</template>

<script setup lang="ts">
const hoge = ref();
const fuga = ref();
const clearRadioSelected = (event: Event, target: string) => {
  if (event.target instanceof HTMLInputElement && event.target.type === "radio" && event.target.tagName === "INPUT") {
    const value = event.target.value;
    switch (target) {
      case "hoge":
        hoge.value = value && value === hoge.value ? undefined : value;
        break;
      case "fuga":
        fuga.value = value && value === fuga.value ? undefined : value;
        break;
    }
  }
};
</script>
```
Ubuntuで起動時にfsckを強制的にかけてみた

# 概要
UbuntuのHDDの調子がよくないので、fsckをかけてみました。

# ファイル作成
以下のコマンドで/forcefsckを作成しました。
```
$ sudo touch /forcefsck
```

# 再起動
```
$ sudo shutdown -r now
```

これで起動時にチェックをしてくれます。

# まとめ
何かの役に立てばと。
【Swift】ユニットテストの目的

# はじめに
iOSテスト全書を読んで、備忘録としてユニットテストの目的を記録しておきたかったので記事として残す。

:::note warn
本記事の内容はiOSテスト全集を参考にしています。
良識の範囲内で書籍から引用しています。
:::

https://peaks.cc/books/iOS_testing

# ユニットテストの目的
ユニットテストのメリットは主に2つあります。

**1\.より早い段階で不具合に気づく**
**2\.コードや設計を継続的に改善できる状態にする**

## より早い段階で不具合に気づく
ユニットテストを書いていない環境で、リリース前に新機能に関する不具合を見つけるとする。
この場合、不具合がどこにあるか見つけることが難しい。
クライアント側に問題があるのか。それとも、バックエンド側なのか。

**それぞれの処理の単位で正しく動くことを検証できていれば、不具合は早いタイミングで見つけることができる。**

また、**正しく動くことを保証しているテストコードがあれば、テストできていない箇所に原因があると判断することができる。**

## コードや設計を継続的に改善できる状態にする
ユニットテストがなければ、エンジニアが動作確認にリソースを割かれ、設計のリファクタリングを行うのが難しくなる。

# 終わりに
保守性・可読性のために設計が重要なのは理解していたが、テストの目的・メリットについては深く理解していなかったので、今回学習できてよかった。

今後、個人開発でも動作確認の手間を減らすために、ユニットテストを書いてみることにする。

この記事を読んで、iOS開発のテストに興味が湧いた方は以下の書籍を一読してみてください。

https://peaks.cc/books/iOS_testing
FlutterでAPIに繋ごうとしたら「Failed to parse header value」というエラーが出た


# 事象
Flutter(with Amplify) + API Gateway(Cognitoによるオーソライザー付き) + lambdaの構成で
Flutter から APIを叩いた時に以下のようなエラーメッセージが出た。

- 401 UnAuthorized
- `Clientexception (failed to parse header value)`
- `I/flutter (29486): Failed to parse header value`

# 解決

ググっても中々解決策とかが出てこなくてかなり調べたけど、
結論としては
**API GatewayのCognitoオーソライザーの対象者(ClientId)の設定漏れ**
だった。
Flutterで設定したCognito設定のClientIdが対象者として入っていなかった。

UnAuthorizedと出ている時点でピンと来ればよかったのだけどエラーメッセージがアバウト過ぎて原因の特定に時間がかかってしまった。:ghost:

個人的には意外な落とし穴でした。
Databricks にて Evidently をとりあえず動かしてみる～Basic Example編～

## 概要

Databricks にて Evidently という ML モデルのパフォーマンス監視ライブラリをとりあえず動かす方法を共有します。Evidently の Docs に記載されている [Basic Example - Evidently Documentation (evidentlyai.com)](https://docs.evidentlyai.com/get-started/hello-world)をベースに実行する方法を提示します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/b60b7c67-e70b-1a13-58e8-6dc20818f1bb.png)

引用元：[Evidently AI - Open-Source Machine Learning Monitoring](https://www.evidentlyai.com/)

Issues に記載されていたのですが、Databricks 上で実行結果を表示する際には`mode`を`inline`に設定する必要があるようです。

- [Databricks ReferenceError: requirejs is not defined · Issue #493 · evidentlyai/evidently (github.com)](https://github.com/evidentlyai/evidently/issues/493)

## Basic Example の実行手順

```python
%pip install evidently -q
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/dd66629e-a12a-6573-0449-368e13b8914a.png)

```python
import pandas as pd
 
from sklearn import datasets
 
from evidently.test_suite import TestSuite
from evidently.test_preset import DataStabilityTestPreset
 
from evidently.report import Report
from evidently.metric_preset import DataDriftPreset
 
iris_data = datasets.load_iris(as_frame='auto')
iris_frame = iris_data.frame
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/a6f133e9-556b-0525-f561-b242e604974c.png)

```python
data_stability= TestSuite(tests=[
    DataStabilityTestPreset(),
])
data_stability.run(current_data=iris_frame.iloc[:60], reference_data=iris_frame.iloc[60:], column_mapping=None)
data_stability.show(mode='inline')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/b0b0c5c7-f524-c3b3-6ca4-e024aa6e1114.png)

```python
data_drift_report = Report(metrics=[
    DataDriftPreset(),
])
 
data_drift_report.run(current_data=iris_frame.iloc[:60], reference_data=iris_frame.iloc[60:], column_mapping=None)
data_drift_report.show(mode='inline')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/a1a48e9a-fd69-6c9b-1c7c-fa83beed70a8.png)
GoogleMapで警告が出るから修正する

GoogleMapsAPI使ってたら、警告が出てるので修正しようと思う
```console
google.maps.event.addDomListener() is deprecated, use the standard
        addEventListener() method instead:
        https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener
  The feature will continue to work and there is no plan to decommission
  it.
```

## 作業環境
* Nuxt.js (v2.15.8)
* `vue2-google-maps` (v0.10.7) を使用
* `@googlemaps/markerclustererplus` (v1.2.10)　を使用

## とりあえず警告をなんとかするため最新ライブラリにする

### 1) `google.maps.event.addDomListener()` で書いている所を修正
`addDomListener`は非推奨となったので、使っている部分は修正

:whale: イベントの種類は以下を参照
https://developers.google.com/maps/documentation/javascript/reference/event?hl=ja

### 2) vue2-google-mapsが4年以上更新されてないので使うのを辞める
パッケージをインストールして、

```
npm install @googlemaps/js-api-loader
```
インポートする
```js
import { Loader } from "@googlemaps/js-api-loader"
```

:whale: 手順は公式に乗ってるので、あとは割愛
https://developers.google.com/maps/documentation/javascript/overview?hl=ja#js_api_loader_package


### 3) `@googlemaps/markerclustererplus`は`@googlemaps/markerclusterer`にMergeされたっぽい!! :rolling_eyes::rolling_eyes::rolling_eyes:
詳しいHistoryはMakerClustererの公式ドキュメントを見てね

最新ライブラリをインストール
```
npm install @googlemaps/markerclusterer
```

インポートしてMakerClustererを追加
```js
import { MarkerClusterer } from "@googlemaps/markerclusterer";

const markerCluster = new MarkerClusterer({ map, markers });
```

:whale: 公式ドキュメント
https://developers.google.com/maps/documentation/javascript/marker-clustering?hl=ja

:whale: ライブラリのドキュメント
https://googlemaps.github.io/js-markerclusterer/

ライブラリのドキュメントが英語で理解するのに時間を溶かしたので、
別投稿でメモする予定、乞うご期待！

## まとめ
`@googlemaps/markerclustererplus` ライブラリ内で
`google.maps.event.addDomListener()` の記述があるっぽい
（という何かを見かけた、知らんけど）

元々は
`@googlemaps/markerclusterer` はClusterer化出来るライブラリ
`@googlemaps/markerclustererplus` はClustererした時のMakerIconをカスタマイズするライブラリっぽい

`@googlemaps/markerclustererplus`の実装は以下でお世話になりました、感謝 :whale:
https://blog.ryou103.com/post/marker-cluster-customize/ 

## 次の記事
https://qiita.com/n739dev/items/2a2eba4d442cc919f8db
弾性棒に係る不静定系の力学 基本例題1.21 P15「材料力学演習(20221021)」をsympyでやってみた。 Trussで。

・solveを1回にする方法を教えて下さい。
# オリジナル
基本例題1.21 P15
http://zairikiweb.starfree.jp/zai_enshuh/zai_enshuh_ver.3.3.pdf#page=20

「材料力学演習(20221021)一括(ver.3.3)」を勉強したい。＞sympy
https://qiita.com/mrrclb48z/items/c3274daf5f7a99cfe027#%E4%BD%9C%E6%A5%AD%E4%B8%AD%E7%A7%81%E3%81%AE%E8%A7%A3%E7%AD%94sympy

# sympyで
```python
from sympy import *
l  ,t   =symbols('l   t'  ,real=True)
P       =symbols('P'      ,real=True)
NAC,NBC =symbols('NAC NBC',real=True)
RAH,RAV =symbols('RAH,RAV',real=True)
RBH,RBV =symbols('RBH,RBV',real=True)
my121_FH=Eq(NAC*cos(t)+NBC,0)
my121_FV=Eq(NAC*sin(t)-P  ,0)
ans=solve([my121_FH,my121_FV],[NAC,NBC])
NAC=ans[NAC]
NBC=ans[NBC]
#
myZu122L_FH=Eq( RAH+RBH   ,0)
myZu122L_FV=Eq( RAV+RBV-P ,0)
myZU122L_M =Eq(-RAH*l-P*l ,0)        # (Bまわり)  
ans=solve([myZu122L_FH,myZu122L_FV,
           myZU122L_M   
          ],[RAH,RBH])
RAH=ans[RAH]
RBH=ans[RBH]
#
myZu122R_FV=Eq( RAV-NAC*cos(t) ,0)
ans=solve(myZu122R_FV,RAV)
RAV=ans[0]
RBV=P-RAV
print("# RAH  RAV      RBH RBV"   )
print("#",RAH," ",RAV,RBH," ",RBV )
# RAH  RAV      RBH RBV
# -P   P/tan(t) P   P - P/tan(t)
```

# sympyのTrussで
```python
# ver1.1
from sympy.physics.continuum_mechanics.truss import Truss
from sympy import *
var('P')
var('L,H,l,A,E',positive=True)  
def myTruss(L):
    def myAdd_member(*args):
        def make_triples(s):
            return (s,s[0],s[1])
        for s in args:
            t.add_member(make_triples(s))      
    t = Truss()
    t.add_node     (('A',0,L)) 
    t.add_node     (('B',0,0),('C',L,0)) 
    myAdd_member   ( 'AC','CB')  
    t.apply_support(('A' ,'pinned'))
    t.apply_support(('B' ,'pinned'))
    t.apply_load   (('C',P,270))   
    return t
# -------------------------------------------------------------------------------------
myTruss(1).draw().show()       # 作図のため(l=1)
t=myTruss(l);t.solve()         # 計算のみ
print("#",{k:v for k,v in t.reaction_loads.items()})           
# {'R_A_x': -P, 'R_A_y': P, 'R_B_x': P, 'R_B_y': 0}
```
・縦横比は1:1でありません。

![111.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109698/10265a7b-0912-4fd7-bea4-8aa4c9cd9747.png)

# Mathematicaで
勉強中

# いつもの? sympyの実行環境と 参考のおすすめです。
[(テンプレート)](https://qiita.com/mrrclb48z/items/00dd08b0317069be9342)
# いつもと違うおすすめです。

#### wikipedia 

#### Qiita内で
・類題

https://qiita.com/mrrclb48z/items/4e793da5b801e616ceb8
AWS CodePipeline によるビルドでシンボリックリンク (symlink) が単なるファイルに置換される問題への対応

# 問題

CodePipeline による一般的なビルドでは Source -> Build -> Deploy という流れで行われる。
今回は Source に CodeCommit を、 Build に CodeBuild を利用して Docker Image を作成したのだが、Docker Image が正常に動作しなかったためその理由を調査した。
結果、CodeCommit 上にシンボリックリンクとして保存されているコンテンツが単なるファイルに置換されていたことが原因で正常に動作しなかった。

検索すれば同様の問題に遭遇した人を見つけることはできた。

https://dev.classmethod.jp/articles/tips-codepipeline-symlink/

https://qiita.com/shonansurvivors/items/8b14180d5b7740be0009

これらの記事は解決策までは示してくれなかったが **CodeCommit を利用している場合は** 簡単な解決策が存在するため、その解決策を以下に示す。


# 解決策

:::note warn
CodeCommit を利用している場合にのみ利用可能な点に注意
:::


問題が発生する原因は CodePipeline のデフォルト設定の場合 Pipeline のフェーズを引き継ぐなかで S3 に一度ファイルを格納するため、そのタイミングでシンボリックリンクが単なるファイルになってしまうためである。

そのため、直接 Git から clone したコンテンツを元にビルドができれば問題は解消する。 これを利用するためには **CodePipeline の Source アクションで、出力アーティファクト形式を「完全クローン」に設定** すればよい。

具体的な手順は例えば以下の記事を参照。

https://dev.classmethod.jp/articles/update-codepipeline-supports-codecommit-git-clone-as-source-action/
【SwiftUI】sheetの背景を透過する方法

# はじめに
iOS16.4でついに標準モディファイア`sheet`の背景透過が実現できるようになりました！


# 基本実装
```diff_swift
import SwiftUI

struct ContentView: View {
    @State private var isPresented = false
    var body: some View {
        Button {
            isPresented = true
        } label: {
            Text("表示")
        }
        .edgesIgnoringSafeArea(.all)
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(LinearGradient(
            gradient: Gradient(colors: [.purple, .pink, .orange, .yellow]),
            startPoint: .topLeading,
            endPoint: .bottomTrailing
        ))
        .sheet(isPresented: $isPresented) {
            Button {
                isPresented = false
            } label: {
                Text("閉じる")
            }
            .padding(30)
            .background(Color.green)
            .cornerRadius(10)
+           .presentationBackground(Color.clear)
        }
    }
}
```

![simulator_screenshot_7C3B39FE-8B16-40D2-8322-DF6417CDFAF9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/32b13416-f4f5-ef33-6bf1-0df2b6f0ffec.png)

# ガラスのような透過
```diff_swift
import SwiftUI

struct ContentView: View {
    @State private var isPresented = false
    var body: some View {
        Button {
            isPresented = true
        } label: {
            Text("表示")
        }
        .edgesIgnoringSafeArea(.all)
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .background(LinearGradient(
            gradient: Gradient(colors: [.purple, .pink, .orange, .yellow]),
            startPoint: .topLeading,
            endPoint: .bottomTrailing
        ))
        .sheet(isPresented: $isPresented) {
            Button {
                isPresented = false
            } label: {
                Text("閉じる")
            }
            .padding(30)
            .background(Color.green)
            .cornerRadius(10)
+           .presentationBackground(Material.ultraThinMaterial)
        }
    }
}
```
![Simulator Screenshot - iPhone 14 Pro - 2023-05-24 at 18.04.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/4ba69522-a318-0801-1e94-f87ae31ab6e4.png)

# おわり
最近流行りのスケスケUIは`Material`を使うといい感じにできます！！
Mac OSでSSH Keyにパスフレーズ(passphrase)を付与する

# はじめに
SSH Keyにパスフレーズ(passphrase)を付与する備忘録です。
やったほうが良さそうだけどほんとにやって大丈夫？影響は？という点が気になって躊躇していた時期があったのと、権限周りで少し気づきがあったため記事にしてみました。

# SSH keyに対するパスフレーズとは？
ソース：https://docs.github.com/en/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases

> SSHキーを使用すると、誰かが自身のコンピューターにアクセスした場合、攻撃者はそのキーを使用するすべてのシステムにアクセスすることができてしまいます。
そこで、さらにセキュリティを強化するために、パスフレーズをSSHキーに追加することができます。
さらに、接続のたびにパスフレーズを入力するのを避けるために、パスフレーズをSSHエージェントに安全に保存することもできます。

# SSH keyにパスフレーズを設定する方法
## コマンド
必要なコマンドは以下です。AWS.cerをご自身のcerに置き換えてください。
```zsh
$ chmod 600 ~/.ssh/AWS.cer
$ sh-keygen -p -f ~/.ssh/AWS.cer
Enter new passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved with the new passphrase.
$ chmod 400 ~/.ssh/AWS.cer
```

## 詳細
次のコマンドを入力すると、キーペアを再生成せずに、既存の秘密鍵にパスフレーズを設定することが出来ます。

```zsh
$ ssh-keygen -p -f ~/.ssh/AWS.cer
Enter new passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved with the new passphrase.
```

鍵に既にパスフレーズが設定されている場合は、新しいパスフレーズに変更する前に、パスフレーズを入力するよう促されます。
```zsh
$ ssh-keygen -p -f ~/.ssh/AWS.cer
Enter old passphrase:
Enter new passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved with the new passphrase.
```

Keyのパーミッションを400としている場合、パーミッションエラーで弾かれます。
そのため、600など一時的にパーミッションを変更した上でパスフレーズを設定し、再度400に戻す必要があります。
実際のながれは下記のような形となります。
```zsh
$ chmod 600 ~/.ssh/AWS.cer
$ sh-keygen -p -f ~/.ssh/AWS.cer
Enter new passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved with the new passphrase.
$ chmod 400 ~/.ssh/AWS.cer
```

なお、777のようにあまりに広いパーミッションを付与すると別のエラーで設定できなくなります。
```zsh
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0777 for '~/.ssh/AWS.cer' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Failed to load key ~/.ssh/AWS.cer: bad permissions
```

# 対応結果
SSH Keyを利用する際にパスフレーズを求められるようになります。
```zsh
ssh -i "~/.ssh/AWS.cer" user@ip.address.com
Enter passphrase for key '~/.ssh/AWS.cer':
```

# パスフレーズをキーチェーンに保存して毎回入力する必要をなくす
こちらについてはまた次回。

ガイドはこちら：https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent#adding-your-ssh-key-to-the-ssh-agent

<説明>
Mac OS X LeopardからOS X El Capitanでは、これらのデフォルトのプライベートキーファイルは自動的に処理されます：
.ssh/id_rsa
.ssh/identity
初めて鍵を使用するときは、パスフレーズを入力するよう求められます。
パスフレーズをキーチェーンで保存することを選択した場合、再度入力する必要はありません。

それ以外の場合は、鍵を ssh-agent に追加する際に、パスフレーズをキーチェーンに保存することができます。
ソース：https://docs.github.com/en/authentication/connecting-to-github-with-ssh/working-with-ssh-key-passphrases#saving-your-passphrase-in-the-keychain

環境をセキュアに守りつつ、検証を進めていきたいと思います。
YOLOv8をCLIで動かしてみた

## 概要

オープンソース物体認識のデファクトスタンダート・You Only Look Once（以降，YOLO）の正規第8版．YOLACTやYOLO-Xなどの派生系もあるので正規と表記した．これまでGithubリポジトリとして提供されてきたYOLOシリーズだが，このYOLOv8はPYPIパッケージとして提供されている．物体認識から派生して領域認識や個体認識も実行できる．

公式のGithubは[こちら](https://github.com/ultralytics/ultralytics)．

Githubトップページにはコマンドによる推論とPythonの学習プログラムが記載されている．

- コマンド側のDocsは[こちら](https://docs.ultralytics.com/usage/cli/)：推論以外にも学習用コマンドや評価用コマンドも用意されている
- Python側のDocsは[こちら](https://docs.ultralytics.com/usage/python/)：学習以外に評価サンプルや推論サンプルも用意されている

## インストール

このコマンドでインストールが可能．公式のインストールガイドは[こちら](https://github.com/ultralytics/ultralytics#documentation)．

```bash
pip install ultralytics
```

要件はこの通り．

- Python≥3.7
- torch>=1.7

## テスト

今回はコマンドを学習，推論，検証を実行してみる．

環境はGoogle ColabでURLは[こちら](https://colab.research.google.com/drive/1ff7r9M8wb9q3qFe2NhKnVmpPujC20VEk#scrollTo=VbEehIeX9MPU&forceEdit=true&sandboxMode=true)である．

ただし，このままでは`NotImplementedError: A UTF-8 locale is required. Got ANSI_X3.4-1968`というエラーが出るので，この[コメント](https://github.com/googlecolab/colabtools/issues/3409#issuecomment-1446281277)を参考にしてはじめに下記コードを打ち込む．

```python
import locale
locale.getpreferredencoding = lambda: "UTF-8"
```

基本的な文法としてはこの通り

```python
yolo TASK MODE ARGS
```

- yolo：コマンド名
- TASK：オブション引数でdetect，segment，classifyから選択する．与えなければモデルタイプから自動で判定してくれる
- MODE：train，val，predict，export，trackから選択する．今回はtrain，val，predictを実行する
- ARGS：その他の引数．公式DocsのC[onfiguration](https://docs.ultralytics.com/usage/cfg/)を参考にして必要なものを与える．今回は最低限の構成で実行する．

なお，実行するとコンソール出力の最後の方に`Results saved to HOGE`という出力がある．`HOGE`はディレクトリを指しており`HOGE`以下ににコマンドの実行結果（モデルファイルなど）が格納されている．

今回は一貫して最低限動くことを確認するところまでをみていく．

1. 学習
学習コマンドはこの通り．
    
    ```python
    !yolo train data=coco128.yaml model=yolov8n.pt epochs=2
    ```
    
    - data：データファイルへのパス．ローカルになければ対応したものをダウンロードしてきてくれる．[ultralytics/ultralytics/datasets](https://github.com/ultralytics/ultralytics/tree/main/ultralytics/datasets)にダウンロード可能なデータファイルがある
    - model：モデルファイルへのパス．pt形式の重みファイルかyaml形式のモデルファイルを与える．
    - epochs：エポック数．デフォルトが100で，与えずに実行すると時間がかかるため，テスト用に2にしている
    
    出力は`runs/detect/train/`に保存される．重みファイルや学習データの推論の様子や検証データの推論の様子といった学習結果だけでなく，混同行列やROCカーブなどの精度評価が揃っている．
    
2. 推論
推論コマンドはこの通り．
    
    ```python
    !yolo predict model="./runs/detect/train/weights/best.pt" source='https://ultralytics.com/images/bus.jpg'
    ```
    
    - model：重みファイルへのパス．ここでは上のコマンドで学習済みのモデルを与えてみた
    - source：ソースディレクトリ，ソースファイルへのパス．画像と動画の両方に対応している．ここではultralyticsで用意しているバスの画像をダウンロードして使っている
    
    出力は`runs/detect/predict/`に画像や動画にアノテーションしたものが保存される．
    
3. 検証
    
    ```python
    !yolo val model="./runs/detect/train/weights/best.pt" data=coco128.yaml
    ```
    
    - model：重みファイルへのパス．ここでは上のコマンドで学習済みのモデルを与えてみた
    - data：データファイルへのパス．ローカルになければ対応したものをダウンロードしてきてくれる．今回は学習データに合わせてcoco128にしてみた．
    
    出力は`runs/detect/predict/`に保存される．揃っているデータは検証データの推論の様子や混同行列，ROCカーブなどで．実は学習時に検証も走っているのでオーバーラップしている．なお，学習コマンド`train`にオプション`val=False`をつけると学習時には検証されない．
Google Maps JavaScript APIで現在地用の青丸のアイコンを表示する

Google Maps JavaScript APIには地図アプリによくある現在地を示す青丸のアイコンを表示する機能が提供されていないみたいですね。

自力で作るしかなさそうです。

ドキュメントを読むのがめんどくさかったので、結局はチャットGTPに聞きました。
下記の２つの質問で皆さんも答えはゲットできちゃうと思います。
プロンプト：
```javascript
google map apiで現在地によくある青丸のアイコンを表示したい
```
```javascript
青丸の周りに円形の薄い青の影を表示したい
```

一応コードも共有します。

**作るもの**
・青丸表示
![スクリーンショット 2023-05-24 17.25.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1129677/aa8e51a9-490c-b2c0-3ac9-381371deed4d.png)
薄い青丸の縁をマップに追加し、その中央に濃い青丸のマーカーを表示する感じです。
引数のmapにはGoogle Maps JavaScript APIを使用して作成される地図オブジェクト、
positionには用事する位置の緯度と軽度のオブジェクト（{lat: 緯度, lng: 軽度}）を渡してください。
```javascript

function setCurrentLocationMaker(map, position){
        //縁の薄い青丸
        new google.maps.Circle({
            strokeColor: '#115EC3',
            strokeOpacity: 0.2,
            strokeWeight: 1,
            fillColor: '#115EC3',
            fillOpacity: 0.2,
            map: map,
            center: position,
            radius: 100
        });        
        //  中央の濃い青丸
        new google.maps.Marker({
            position: position,
            map: map,
            icon: {
              path: google.maps.SymbolPath.CIRCLE,
              fillColor: '#115EC3',
              fillOpacity: 1,
              strokeColor: 'white',
              strokeWeight: 2,
              scale: 7
            }, 
        });
    }

```


現在地に移動するボタンも必要に応じて追加すると便利だと思います。
（APIでは提供されていないため、自力で作りました。）
![スクリーンショット 2023-05-24 17.25.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1129677/2fd90c6f-fcfc-9960-dccb-0ace024d3a02.png)

私が作った際には誰も記事化していなかったので、どなたかのお力になれれば幸いです。
The following Availability Zones ap-northeast-1b cannot be associated with a load balancerが出たら。。。

# はじめに

`aws cdk`で`ALB`や`ApplicationLoadBalancedFargateService`などを東京リージョンで作成する際に、何も指定しなければ自動で`Availability Zones`が指定されますが、デプロイするとエラーが出ます。

# エラー文

```
The following Availability Zones ap-northeast-1b cannot be associated with a load balancer. 
Please try a different Availability Zone. (Service: AmazonElasticLoadBalancing; Status Code: 400; Error Code: ValidationError; Request ID: hoge; Proxy: null)
```

# 対処法

## 自動生成されている`cdk.context.json`を修正する

### 修正前

```cdk.context.json
{
  "availability-zones:account=888888888888:region=ap-northeast-1": [
    "ap-northeast-1a",
    "ap-northeast-1b",
    "ap-northeast-1c",
    "ap-northeast-1d"
  ],
}
```

### 修正後

アベイラビリティーゾーンを`ap-northeast-1a`と`ap-northeast-1c`だけにするとうまくデプロイできました。

```cdk.context.json
{
  "availability-zones:account=888888888888:region=ap-northeast-1": [
    "ap-northeast-1a",
    "ap-northeast-1c"
  ],
}
```

現場からは以上です。
Pythonでtimezoneを変更する方法

# 背景
OSS活動をしていたときに、[コメント](https://github.com/dbt-labs/dbt-core/issues/7664#issuecomment-1554923838)で「タイムゾーンを'America/Denver'に設定した場合はどうなる？」といただいた。
調べてみると、該当のファイルでは、time.timezoneでUTCからのオフセットを取得していたため、time.timezoneの参照先とそのタイムゾーンの変更方法について、Pythonの[ドキュメント](https://docs.python.org/ja/3/library/time.html)を元に調べた。備忘録として記事に残す

# 結論
- time.timezoneはtzset()が実行されたタイムゾーンが設定される
- tzset()は環境変数‘TZ’で設定されたタイムゾーンが設定される
- os.environ[‘TZ’]は、`/usr/share/zoneinfo`にある相対パスを読み取りタイムゾーンを設定することが可能

# 実装例
該当するファイルの先頭に以下を追加すれば良い
```
import os
import time

os.environ['TZ'] = 'America/Denver'
time.tzset()
```

なお、設定した環境変数は、そのPythonプログラムの中でのみ有効。システムの環境変数が書き変わるわけではないのでご安心を。
Oracle Graph でユーザー定義関数を使う

Oracle Graph は Oracle Database (RDBMS) 上でグラフを扱う機能に加えて、PGX というインメモリのグラフ分析エンジンを提供しています。PGX はグラフを効率的な圧縮形式でメモリに展開することで、グラフを辿る処理をさらに高速化することができます。

ただし、PGX がサポートするクエリ言語「PGQL」は、歴史の長い汎用クエリ言語である SQL と比較すると関数が少ないです。そこで、今回はユーザー定義関数を用いることで必要な機能を補完する方法を紹介します。実際の利用例は、別記事[「グラフを使った部品数の数え上げ」](https://qiita.com/ryotayamanaka/items/02d249c25a6647aae790)にて紹介していますので、そちらも併せてご参照ください。

この記事では Graph Server and Client のバージョン **23.2** を使用しています。

## ユーザー定義関数の作成

まずは、PGX がデプロイされている OS のディレクトリ上で、関数を定義した Java プログラムを作成します。

```sh
mkdir -p ~/udfs/java
cd ~/udfs/java
vi Product.java
```

```java:Product.java
package my.udfs;

public class Product {
    public static int product(String str) {
        String[] nums = str.split(",");
        int product = 1;
        for (int i = 0; i < nums.length; i++) {
                product = product * Integer.parseInt(nums[i]);
        }
        return product;
    }
}
```

コンパイルして JAR ファイルを作成します。

```
javac -d ./ Product.java
jar cvf udfs-product.jar my
```

JAR ファイルを PGX のライブラリが所定のディレクトリにコピーして、所有者権限を適切に変更します。

```sh
sudo cp udfs-product.jar /opt/oracle/graph/pgx/server/lib/
sudo chown root:oraclegraph /opt/oracle/graph/pgx/server/lib/udfs-product.jar
```

不要となったファイルは削除しておきます。

```
rm -r my
```

## ユーザー定義関数の有効化

新たにディレクトリとユーザー定義関数の設定ファイル `udfs.json` を作成します。

```sh
sudo mkdir /opt/oracle/graph/pgx/udfs
sudo vi /opt/oracle/graph/pgx/udfs/udfs.json
```

```json:udfs.json
{
  "user_defined_functions": [
    {
      "namespace": "my",
      "language": "java",
      "implementation_reference": "my.udfs.Product",
      "function_name": "product",
      "return_type": "int",
      "arguments": [
         {
           "name": "str",
           "type": "string"
         }
       ]
    }
  ]
}
```

PGX の設定ファイル `pgx.conf` に上で作成したディレクトリのパスを設定してユーザー定義関数を有効化します。

```
vi /etc/oracle/graph/pgx.conf
```

次の一行を追加してください。

```json:pgx.conf
  ...
  "udf_config_directory": "/opt/oracle/graph/pgx/udfs/"
}
```

PGX を再起動します。

```sh
sudo systemctl restart pgx
tail -f /var/log/oracle/graph/pgx-server.log
```


## 動作確認

Python クライアントでログインします。

```
python
```

```python
from opg4py import graph_server
from pypgx import setloglevel
setloglevel("ROOT", "WARN")

instance = graph_server.get_instance(
    "http://localhost:7007", "graphuser", "****")
session = instance.create_session("test")
```

上の代わりに `opg4py` コマンドを使うこともできます。

```
opg4py -b http://localhost:7007 -u graphuser
```

セットアップ時に含まれる `hr` グラフを用いて、ユーザー定義関数 `my.product()` の動作を確認します。

```python
graph = session.get_graph("hr")

graph.query_pgql("""
SELECT my.product('1,2,3') FROM MATCH (v) LIMIT 1
""").print()
```
```
+---------------------+
| my.product('1,2,3') |
+---------------------+
| 6                   |
+---------------------+
```

ユーザー定義関数は Java の他に JavaScript で定義することも可能です。詳細については、ドキュメントの[「14.9 User-Defined Functions (UDFs) in PGX」](https://docs.oracle.com/en/database/oracle/property-graph/23.1/spgdg/developing-applications-graph-analytics.html#GUID-C3E37DD0-E18E-40BE-B4A3-C5B49B89A039)をご参照ください。

Oracle Graph の[記事一覧](https://qiita.com/tags/oraclegraph)もぜひご覧ください！