OmniAICreator/Qiita-1.07M huggingface dataset:
UbuntuにDockerとGitをインストール

# 概要
新しく立ち上げたubuntuインスタンスでdockerを使うことを想定して、以下のことを行う
1. docker,docker-composeをインストール
2. gitをインストールし、自分のprivateなgithubリポジトリにアクセスできるようにする


# DockerをUbuntuにインストールする

### dockerをインストール
標準リポジトリにあるDockerのバージョンは、ここで使用する最新のコミュニティーエディションではないため、以下のコマンドで必要なソフトをインストール
```sh
# 公式のDocker GPGキーを追加する。
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# 次に、公式のDockerリポジトリーを追加する。
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 必要な依存関係をインストール
sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release -y

# aptを更新してDockerをインストールする
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io -y
```

## docker-compose をインストール
インストールにはインストールしたいバージョンが必要、バージョンは以下で確認できる。
https://github.com/docker/compose/releases

```sh
sudo curl -L "https://github.com/docker/compose/releases/download/v2.2.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose -v
```


### ユーザーをDockerグループに追加する
Dockerをsudoで呼び出さなくても使用できるようにするため、以下のコマンドを実行して、ユーザーをdockerグループに追加
```sh
sudo usermod -aG docker $USER
# dockerデーモンを再起動
sudo service docker restart
# ユーザーがdockerグループに正しく追加されたことを確認するために、一度ログアウトする
exit
```


## インストールしたDockerをテスト

```sh
docker version # インストールできているか確認
docker ps # こちらで失敗する場合、グループ追加のところで失敗している可能性がある。
```



# gitまわりの設定

### gitをインストール
```sh
sudo apt install git
git --version  # 正常にインストールできているか確認
```

### Git の設定
```sh
git config --global user.name [名前]
git config --global user.email [メールアドレス]
```

## GitHub連携
Githubを連携させるには公開鍵が必要なので作成する
```sh
ssh-keygen -t rsa -C "メールアドレス"
```
~/.ssh/id_rsa が作成される


### GitHubに鍵を登録
Githubのページに移りアカウント設定の [SSH and GPG keys](https://github.com/settings/keys) に移動

「New SSH key」から鍵を登録する。
- `Title`: Ubuntu等のわかりやすい名前を登録する。
- `Key Type`: Authentication Key
- `Key` : 公開鍵をクリップボードにコピーして、Githubに鍵を登録する ↓

```sh
cat ~/.ssh/id_rsa.pub # 公開鍵
```

### 接続確認
ubuntuに戻り
```sh
ssh -T git@github.com
```
yesと入力

### githubにアクセスできない！
以上のことをしてもアクセスできない場合、pingで通信が出来ているかを確認してください。
pingが通らない場合、ipv6のインスタンスを使っている可能性があります。
githubは ipv4 しか対応していないようで、ipv6からはアクセスできないようです。
```sh
ping github.com
# ping: connect: Network is unreachable と表示され、アクセスできない場合は ipv6かも
```
【環境構築】Laravel Jetstream + Inertia(Vue3)で環境構築


Laravel Breeze+Inertia(Vue.js3)　以前の記事と変われらない箇所は飛ばします。

https://qiita.com/LB16B/items/53bd6ceed1a3bdce318f

公式

https://readouble.com/laravel/9.x/ja/installation.html

# 開発環境

### プロジェクト作成

```
composer create-project "laravel/laravel=9.*" task_test --prefer-dist
```

### Jetstream
```
composer require laravel/jetstream:^2.6
```
### livewire

```
php artisan jetstream:install livewire
```

### inertia(Vue)

```
sail artisan jetstream:install inertia
```


### コンパイル

```
npm install
npm run dev
```

### マイグレーション

```
php artisan migrate
``
GostScriptを使用してPDFをJPEGに変換するツールを作成してみた（Windows CMD)

### 背景

仕事で、PDFをjpegに変換してWinMergeで比較しているのだけど、ツール作ってと言われたので。
PDFをjpegに変換する部分だけ作ってみました。

### 手順

## 1.GostScriptをインストール

以下から、GostScriptをダウンロードしてインストール
[GostScript](https://www.ghostscript.com/releases/gsdnld.html)


## 2.バッチファイルを作成

今回は、バッチ実行フォルダ内の[pdf]フォルダ内にあるpdfファイルを
バッチ実行フォルダ内の[jpeg]フォルダに連番付きで変換するようにしてみました。

```
set current_dir=%~dp0
cd %current_dir%

 for %%a in (pdf\*.pdf) do (
 "C:\Program Files\gs\gs10.01.1\bin\gswin64c.exe" -dBATCH -dNOPAUSE -sDEVICE=jpeg -r300 -sOutputFile=jpeg\%%~nxa_%%03d.jpg %%a
 )
```
Fundamentals of Data Engineering Preface 輪読会資料 第0回分 20230403開催

# 本記事の位置付け
こちらの勉強会　[英語で技術書を読もう：Fundamentals of Data Engineering 第0回](https://gaisaba.connpass.com/event/279517/)　に参加し、その際に発表したものです。

# Preface / 序文

- データサイエンスからデータエンジニアリングへ
- 基礎ができてないからいつも苦闘する
- データサイエンティストはデータ収集・精製・アクセス・変換・インフラ等は習ってない。
- それがデータエンジニアが解決すべき課題


## What This Book Isn't / この本で書かれていないこと
- この本では特定の技術に肩入れしたデータエンジニアリングは記載しない。
- そういう本は他にもあるが短命だ。

## What This Book Is About / この本で書かれていること
- 今あるデータエンジニアリングの隙間を埋める本
- 特定の技術についての本はあるけど、それらを組み合わせて包括的に取り扱い、現実の課題に適用する本がない
- この本はそれらの点をつなぎ、データの下流にいるデータ利用者にデータを提供する
- データエンジニアリングライフサイクルについての本だ。
    - データの生成
    - データの蓄積
    - データの摂取
    - データの変換
    - データの提供
- いろんな製品の栄枯盛衰があるが、データエンジニアリングの本質は変わらない
- その本質で現実の課題を解くのだ
- 二つの軸
    - データエンジニアリングを様々な技術を包含できる原則に高める
    - その原則を時の試練に耐えるように発表（present？）する
- ここでの学び（過去20年で様々に生まれた技術）を、今後10年以上にわたって続くようにしたい


## Who Should Read This Book / 想定読者層
- 1stターゲット
    - 技術の実践者
    - 中堅以上のソフトウェアエンジニア
    - データサイエンティスト
    - データエンジニアリングに興味がある分析者
- 2ndTarget
    - データチームリード
    - DWHのマネージャ（オンプレからクラウドへの移行をしたい）


## Prerequisites / 前提事項
- データシステムに慣れていること
- SQLやPythonの知識
- クラウドサービスの経験


## What You'll Learn and It Will Improve Your Abilities / 読者の実力を向上させる、学べる事

- データエンジニアがどのような影響を持つか

- マーケットのジャーゴンを理解して正しい技術や構造を理解できる

- 堅牢なアーキテクチャを設計して構築できる

- データエンジニアリングのライフサイクルの各段階のベストプラクティスを知ることができる


## Navigating This Book / この本をざっと探ると

- データエンジニアリングの原則を自分の仕事に組み込める

- いろんなクラウドサービスを組み合わせてデータ利用者に提供できる

- データエンジニアリングの問題を、ベストプラクティスにもとづいた考えの枠組みで調査できる

- セキュリティとプライバシーをデータエンジニアリングライフサイクルに組み込める

## Conventions Used in This Book / 本書で使用する規約

- Part Ⅰ　基礎
    - Chapter 1 データエンジニアリングの定義
    - Chapter 2　データエンジニアリングの計画
    - Chapter 3  良いアーキテクチャ　
    - Chapter 4　正しい技術の選択


- Part Ⅱ　データエンジニアリングライフサイクルの深堀
    - Chapter 2　データエンジニアリングの深堀り
        - データ生成
        - データ蓄積
        - データ摂取
        - データ変換
        - データ提供

- Part Ⅲ　セキュリティ、プライバシー、そしてデータエンジニアリングの未来
    - Chapter 10　セキュリティとプライバシー
    - Chapter 11　未来のデータエンジニアリング
    - Appendix A　変換と圧縮
    - Appendix B　データシステムとクラウドネットワーキングのパフォーマンス
CloudWatch Syntheticsのメトリクスとアラームについて

# 概要
CloudWatch Syntheticsで取得できるメトリクスを元にアラームを設定することができるが、メトリクスがどのように取得されているかやデータの解像度が分からなかったので調査。

# Canary によって発行される CloudWatch メトリクス
こちらはドキュメントの通り。
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_metrics.html

APIの外形監視であれば、おそらく下記の3つを利用することになる
- SuccessPercent
- Duration
- Failed

# アラームの設定
SyntheticsのGUIで提供されているアラーム設定画面。
こちらから、アラームの状態・閾値・期間が設定できる。
![スクリーンショット 2023-04-03 18.30.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659784/b7e57d9b-7590-b0b4-c196-2af5a501d183.png)

このとき、閾値と期間をどのように設定すればよいかを考える。

# Syntheticsのメトリクスの解像度

Syntheticsの実行には、下記のロールが必要になる。
[Canary に必要なロールとアクセス許可](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_CanaryPermissions.html)

この中でCloudWatchに関するポリシーは下記の通り
```
{
   "Effect": "Allow",
   "Resource": "*",
   "Action": "cloudwatch:PutMetricData",
   "Condition": {
      "StringEquals": {
         "cloudwatch:namespace": "CloudWatchSynthetics"
      }
   }
}
```
またVPCエンドポイントの設定でも同様に「cloudwatch:PutMetricData」を許可している。
[インターフェイス VPC エンドポイントでの CloudWatch および CloudWatch Synthetics の使用
](https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/cloudwatch-and-interface-VPC.html)

このことから、Syntheticsのメトリクスは実行される**カナリヤ（Lambda)からPutMetricData APIを実行し、カスタムメトリクスとして取得している**ことがわかる。
※ このことはPutMetricDataのメトリクスを確認するとある程度答え合わせができる。

またメトリクスはカナリヤから送られているため、メトリクスの解像度はカナリヤの実行間隔に依存することになる。
カナリヤは定期実行の場合1−60分で設定ができるため、事実上**1分が最も細かいデータポイント**となる。

# アラーム設定を考える
カナリヤの実行間隔を1分とした場合、SuccessPercentの閾値は期間と合わせる。
期間を5分とする場合カナリヤは最大5回しか実行できないため、20％刻みでの設定が適切である。
```
例）
アラームの状態：より低い
SuccessPercent：80%
期間：5分
```
上記の場合、SuccessPercentを90%などにしてもそもそもデータとして取り得ない値であるため無意味となる。
[WPF]1行で図形を描画する

## はじめに

実装するなかで以下のような左向きの矢印を含む見た目のボタンをどうやって描画しているのか見ていたところ、`Button`の`Background`にて図形を描画し表示している手法を見かけました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1062357/b5d8a48c-40dc-4be6-f974-fedcab3e4f96.png)


```xml
<GeometryDrawing Brush="#FFA0A0A0" Geometry="F1 M 0,4.7 L 0,9.4 L 0,0 Z " />
```

当初何をしているのかさっぱりだったのですが、実はXmlでUI要素を`LineSegment Point`が・・・のように多くのタグを含めて実装するのと同じことを1行の`Geometry`で指定するという手法だったのです。

## パス マークアップ構文

- [公式](https://learn.microsoft.com/ja-jp/dotnet/desktop/wpf/graphics-multimedia/path-markup-syntax?view=netframeworkdesktop-4.8)
    - パス マークアップ構文というそうでジオメトリをよりコンパクトに指定するために使用できる強力で複雑なミニ言語があるみたいです。
- [わかりやすかった記事](https://rcosic.wordpress.com/2009/08/11/wpf-geometry-mini-language/)
    以下抜粋
    >F 値 - Geometry.FillRule プロパティを設定します。EvenOddの場合は0を、NonZeroの場合は1を使用します。このコマンドは、文字列の先頭に表示する必要があります（使用する場合）。
M x,y - ジオメトリの新しい PathFigure を作成し、その始点を設定します。このコマンドは、Fを除く他のコマンドの前に使用する必要がありますが、描画シーケンス中に使用して、座標系の原点を移動させることもできます。(Mはmoveの略)。
L x,y - 指定した点までのLineSegmentを作成します。
H x - 指定されたX値を使用し、Y値を一定に保つ水平方向のLineSegmentを作成します。
V y - 指定されたY値を使用し、X値を一定に保ったまま、垂直方向の線分を作成します。
A radiusx, radiusY, degrees isLargeArch, isClockwise x,y - 指定した点までのArcSegmentを作成します。円弧を記述する楕円の半径、円弧を回転させる度数、IsLargeArcとSweepDirectionプロパティを設定するブール値フラグを指定することができます。
C x1,y1 x2,y2 x,y - (x1, y1) と (x2, y2) の制御点を使用して、指定されたポイントに BezierSegment を作成する。
Q x1,y1 x,y - (x1, y1)を制御点とするQuadraticBezierSegmentを指定されたポイントに作成します。
S x2,y2 x,y - 前のBezierSegmentの2番目の制御点を新しいBezierSegmentの最初の制御点として使用し、滑らかなBezierSegmentを作成する。
Z - 現在のPathFigureを終了し、IsClosedをtrueに設定します。IsClosedをtrueに設定したくない場合は、このコマンドを使用する必要はありません。代わりに、新しいPathFigureを開始したり、文字列を終了したりする場合は、単にMを使用してください。

こういった構文を
```xml
<Path Stroke=”Black”>
    <Path.Data>
        <PathGeometry>
            <PathFigure IsClosed=”true” StartPoint=”10,100″>
                <LineSegment Point=”100,100″ />
                <LineSegment Point=”100,50″ />
            </PathFigure>
        </PathGeometry>
    </Path.Data>
</Data>
```
↓
こう圧縮できる
```xml
<Path Stroke=”Blue” Data=”M 10 100 L 100 100 L 100 50 Z” />
```

## さいごに

可読性に若干難ありなので複雑な図形であったり変更が入る可能性のある図形に用いるのは危なそうですが、ちょっとしたボタンの見栄えに使うなど今後変更がなさそうな部分にさくっと用いる分には画像を探す手間も省け、シンプルに記述できて良さそうです。
goで特定のTestifyのテストをCLIで実行する例

## 例
```shell
go test  \
    -timeout 120s \
    -tags mybuildtag \
    -run ^Test_MyTestSuite$ \
    -v -test.v \
    github.com/my-org/my-project/mypkg　\
    -testify.m ^\(Test_MyFuncTest\)$
```

## ポイント

- `-testify.m ...`フラグはパッケージより後に置く
- 正規表現の内容によっては必要に応じて中身をエスケープする
  - ここでは`()`カッコはエスケープする


## Story

### VSCodeのOutputに騙された
いつもVSCodeの`run test`をクリックしてテストを実行していましたが、CLIで実行したい場面があり、
VSCodeのOutputビューに出力されるコマンドをコピペしたところうまくテストが実行されず。
結局試行錯誤してヘルプ見て解決。

### テストバイナリ向けのフラグはパッケージより後に渡す
ヘルプによれば、ビルドフラグ、テストフラグはパッケージ指定より前でも後でも良いが、テストバイナリフラグはパッケージより後に渡さないといけない模様。
```
$ go help test
usage: go test [build/test flags] [packages] [build/test flags & test binary flags]
```

ということで、`-testify.m ^\(Test_MyFuncTest\)$`はTestifyに渡すフラグということで、テストバイナリ用のフラグであるため、パッケージ指定より後に渡すべし。
CTOになって実践してきた選書方法を後世に伝える


# はじめに
2023年3月31日付でCTOを退任しました@noriakiutsu こと宇都宮です。

今回は自分のキャリアにおいてCTOというエグゼクティブ職となってから
これまでのステージと違い会社経営に直接携わる為に
_どのようにして必要な知識、知見を習得したか？_
という事を10年20年後エグゼクティブ職を目指す後世に伝えていければと思います。

# まず読書
本を読み知らなかった事を知る、これに尽きると思います。
ではエグゼクティブ職が直面する不確実性の高い業務に対しどのような知識を
限られた時間の中で仕入れていけば良いのでしょうか？
良書と一般的に呼ばれる本が読者に適しているかどうかはレビューサイトの存在があれど、ある程度読んでいかないと分からない所も厄介です。
早く脳の記録領域に直接情報を書き込む技術が求められます。

# 選書方法
そこで重要になってくるのが本の効果的な選び方です。

+ キュレーターに頼る
これは自分がDJ をやっていたので完全な経験則なのですが
媒体を通じたアウトプット（すなわちDJならDJMixや曲のセレクトであり本なら公開された書評や選書）を
継続的にしてきた人達は総じてその媒体の重度消費者であり情報提供者であります。
そのキュレーターのセレクトや人となりに何かしらシンパシーを感じとる事ができれば
キュレーターから提供される情報を信頼して選書を行えば本の選択を完全に謝る事は避けられるでしょう。
私はあんちぽさんこと栗林健太郎氏のCTOとして歩まれたキャリアと豊富な知識の源泉を氏の[実際に読んで選んだマネジャーのための100冊](https://kentarokuribayashi.com/journal/2016/01/19/2016-01-19-230041)という記事で知り、殆どの本を図書館で借りて現在も通読中です。
また最初にIT技術者として仕事を始めたばかりの頃
チームリーダーが共有してくださった『[伽藍とバザール](https://cruel.org/freeware/cathedral.html)』を読みfreewareとOSSの存在に衝撃を受けて以来
[山形浩生氏の書評や翻訳記事、翻訳書籍](https://cruel.hatenablog.com/)を20年以上読み続けています。
あとは気に入った本の著者がSNSを運用されている場合フォローしておくと
著者が参考文献や読んでよかった本を紹介してくれたりするのでそれも参考になりますね。 

+ 自分でdigる
これは月に数万円書籍購入予算を設け、定期的に電子書籍やオライリーの物理本を購入してますが
いざエグゼクティブ職（ただし企業ステージに依存）での実践に応用できそうな本に巡り合うまでのコスパとタイパが見合わないと思います。
そして積読本が増え続けていく...

# 最後に
ソフトウェアはもはや社会を実装しています。
その為現代社会がどのように今の形になったのか、社会が抱えるペインは何なのか
なぜこのような世界で生かされているのか？と言うような歴史や哲学的な問いを追求し
その上で技術的素養を活かし何ができるのか？何を提供できるのか？を問い続けて行く事が
技術経営者に対し不変的に求められる事であると考えます。
10年20年後そのような立場を目指す後世の人々に何かしら響くものがあれば幸いです。　

# おまけ

後世にこの曲達も送ります。

_OK 余裕_

<iframe width="560" height="315" src="https://www.youtube.com/embed/u5KV0B0teIA" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

<iframe width="560" height="315" src="https://www.youtube.com/embed/iPOUXI0U7Gc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
railsの環境構築(railsインストールまで) M1Macにて

# 個人的リマインド用
めっちゃ時間かかったから覚えておくために書いとく
ドットインストールどおりにrailsを入れようとしていたが、M1のマックとintelマックでは、どうもいろいろと違うらしい

今回参考にさせていただいた動画やサイト
https://qiita.com/hajsu00/items/ee0a7cef304f6f2d5fab
https://www.youtube.com/watch?v=z3pjRoHuhEc
本当にありがとうございました

## 大まかな手順
1.ターミナルにて、「Rosettaを使用してひらく」を設定する
2.Homebrewをインストール
3.rbenvをインストール
4.Rubyをインストール
5.railsをインストール

## 1
**Rossetaとは**
Rosettaを使うとIntel Mac用ソフトウェアを使うことができる(例外あり)

**設定方法**：
ターミナルで右クリック→オプション→Finderに表示→情報を見る→Rosettaのやつにチェックを入れる
uname -mと打つと、今Rosetta上なのか確認できる
arm64→ARMアーキテクチャ x86_64→Rosetta利用かIntelアーキテクチャで実行中

## 2
**Homebrewとは**
macOSのパッケージマネージャー

HomebrewのHPにあるコマンドをコピーしてターミナルに貼り付け実行(何か聞かれても全部yes)
※Xcodeなんちゃらのインストールも必要らしいが、Homebrewを入れたら勝手にインストールされたから大丈夫

```
brew -v バージョン確認。入っているかどうかも確認。
which brew どこに入っているか確認
brew list Homebrewにてインストール済みのパッケージ一覧を表示
```
どうやらM1にはバージョン2.XのHomebrewは対応してないらしいので、3.X~を要確認

## 3
ruby-buildとともにrbenvをインストール
```
brew install ruby-build rbenv 
このコマンドを叩いてインストール
```

**rbenvとは**
いろんなRubyのバージョンをインストールできる環境を作れるソフトウェア
ruby-buildに依存

### パスを通して初期化
~/.zshrcを編集する(bashの.bashrcだと思うのでお気をつけて)

```
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.zshrc

echo 'eval "$(rbenv init -)"' >> ~/.zshrc

上の二つをターミナルで打ち込むべし
```
```
その後以下のコマンドを打ち込み、.zshrcを読み込んでrbenvを初期化

source ~/.zshrc
これを実行しないとrbenvコマンドが使えないらしい
```
**.zshrcとは**
ターミナルを開く時に最初に読み込まれるファイル
・初期コマンドやPATHを通す処理、エイリアスなどを記述

```
[よく使うコマンド]
rbenv -v バージョン確認。入っているかも確認。
rbenv versions 今なんのバージョンのrubyがインストールされているか一覧で確認
rbenv install -l インストールできるrubyバージョン表示
rbenv install 3.2.2 特定のバージョンのrubyをインストール
rbenv global 3.2.2 特定のバージョンのrubyをmac全体で設定(3.2.2は例)
rbenv local 3.2.2 特定のバージョンのrubyを今いるディレクトリだけで設定(3.2.2は例)
rbenv rehash 更新
```
## 4
```
まずはインストールできるバージョンを確認
rbenv install -l

自分の場合は3.2.2だったのでそれをインストール(5分くらいかかるかも)
rbenv install 3.2.2
→問題発生！対処は下に

rbenvを更新
rbenv rehash

バージョン切り替え
rbenv global 3.2.2

rubyバージョン一覧確認と、現在使っているバージョンを確認
rbenv versions
ruby -v

rubyのコマンドの場所が.rbenv/下か確認
which ruby

gem(rubyのパッケージ管理ソフトウェア)が使えるか確認
gem -v
```
rubyのバージョン3.2.2をインストールしようとするとErrorがででインストールができなかった。他の方の記事を参考に対処しました。(本当にありがとうございます)
https://qiita.com/hajsu00/items/ee0a7cef304f6f2d5fab

## 5
```
railsをインストール
gem install rails

インストールされたか確認
gem list

更新
rbenv rehash

バージョン確認
rails -v
```
どうやらbundlerもインストールされるらしい
ChatGPTで始めるJavaScript入門⑩～イベント処理～

# 概要
　今回は3.JavaScriptの文法と構文について、の
　イベント処理について確認していきます。

　イベント処理はJavaScriptのメインの機能です。
　これを避けて通ることはできず、ユーザーの操作とも密接に関係してきます。

　**※無料版なので、誤っている情報もあると思いますが、アウトプットできれば良しとします。**


# イベント処理
　イベント処理でどのようなことができるのか、どう記載すればいいのか、何に注意すればいいかを聞いてみます。

　【質問】
　JavaScriptの文法と構文について、イベント処理の概要、記述方法や注意点を教えてください。

　【回答】
　JavaScriptにおいて、イベント処理は、ユーザーのアクション（マウスクリック、キー入力など）やページの状態の変化（ページの読み込み完了など）に対する反応を定義することができます。
イベント処理を行うためには、以下の手順に従います。

1. イベントの発生元を取得する
2. イベントリスナーを登録する
3. イベントハンドラーを定義する

例えば、ボタンがクリックされたときにアラートを表示する場合、以下のように記述します。

```javascript:event.js
// 1. イベントの発生元を取得する
const myButton = document.getElementById('myButton');

// 2. イベントリスナーを登録する
myButton.addEventListener('click', myEventHandler);

// 3. イベントハンドラーを定義する
function myEventHandler() {
  alert('ボタンがクリックされました！');
}
```

ここでは、getElementByIdメソッドを使って、HTML要素からボタンの要素を取得し、addEventListenerメソッドを使って、ボタンがクリックされたときに呼び出す関数を指定しています。
そして、myEventHandlerという関数を定義し、アラートを表示する処理を記述しています。

注意点としては、イベントハンドラー内でthisキーワードを使用すると、イベントの発生元の要素を参照できます。
また、イベントリスナーは複数登録することができ、登録された順番に実行されます。
しかし、removeEventListenerメソッドを使ってイベントリスナーを削除しない限り、イベントリスナーは永続的に存在し続けます。

*****

何かしらのイベントが発生したことをトリガーに、関数を実行することができます。
**イベントをトリガーにして実行するというのは、具体的にどのような場面で活用できるのでしょうか。**

*****

　【質問】
　イベント処理では具体的にどのようなことができますか？
　
　【回答】
　JavaScriptにおいて、イベント処理を使用することで、以下のようなことができます。

1. ボタンのクリックやキーの入力など、ユーザーのアクションに対して反応する
2, ページの読み込みが完了したときに、特定の処理を実行する
3. フォームが送信されたときに、入力された情報を検証する
4. マウスカーソルが要素の上に移動したときに、要素のスタイルを変更する
5. スクロールが発生したときに、特定の処理を実行する

イベント処理を使用することで、ユーザーのアクションやページの状態の変化に対して、動的な反応を実現することができます。
また、イベントリスナーを複数登録することで、複数のイベントに対して反応することも可能です。

*****
決定ボタンが押されたとき、フォームが送信されたとき等、Webサイトではよく使用するかと思います。
**複数のイベントリスナーを登録することができますが、どのような場合に使用するのでしょうか。**

*****

　【質問】
　イベント処理では複数登録はどのような場合に使用しますか？
　
　【回答】
　イベントリスナーを複数登録する場合、主に以下のような場合に使用します。

1. 同じ要素に対して、複数のイベントが発生する場合
例えば、マウスカーソルが要素の上に乗ったときと、要素の上からマウスカーソルが離れたときに、それぞれ別の処理を行いたい場合などが該当します。

2. 複数の要素に対して同じイベントが発生する場合
例えば、ページ内のすべてのボタンに対して、クリックされたときに同じ処理を行いたい場合などが該当します。

3. 同じ要素に対して、同じイベントが発生する場合でも、異なる関数を実行したい場合
例えば、ページが読み込まれたときに、複数の関数を実行する場合などが該当します。

複数のイベントに対して反応する場合には、addEventListenerメソッドを複数回呼び出して、それぞれに対して異なる関数を指定することができます。
また、event.stopPropagation()メソッドを使って、イベントの伝播を止めることで、イベントが重複して発生することを防ぐこともできます。

*****
このボタンが押されたら、○○と✕✕をしたい。
このページ内のボタンが押されたら、〇〇をしたい。
ページが完全に読み込まれたら、○◯をしたい。

登録や検証、クリア処理など、色んな場面で使えますね。
2つのTBLの差分を比較する (mergeを使って)


2つのTBLの差分を比較して相違点を洗い出す。


```python

a1 = pd.read_csv("test.csv")
a2 = pd.read_csv("test2.csv")


a_merge = pd.merge(a1,a2,how="outer",on="カラム1",indicator=True)

a_merge["_merge"].values_counts()
```

肝はindicatorを使うこと。_mergeカラムができること。
indicatorはどれぐらい紐づいているかを判別するオプションみたいですね。

２つをマージして、_mergeをみると

both , left_only,right_onlyの表示が出てくる。

これにより、左にしかないもの右にしかないものを見ることが可能になる。

最終的に、以下を実行すれば違うところが目に見えてわかるので便利かもしれない。。。
```python
a_merge[a_merge["_merge"] == "left_only"]
a_merge[a_merge["_merge"] == "right_only"]
```

参考：
https://qiita.com/mk_GF/items/2c47dfed15520d34f7d4
inputタグに自動で付与されるアイコンを独自に変更する方法

## サンプルコード

```sample.css
input[type='time'] {
  position: relative;
}

input[type='time']:after {
  font-family: FontAwesome;
  content: '\f017';
  color: #fff;
  padding: 0 5px;
}
input[type='time']::-webkit-calendar-picker-indicator {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: auto;
  height: auto;
  color: transparent;
  background: transparent;
  z-index: 100;
}
```

## 結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2530037/2cf98675-dbdb-d118-f8eb-1481040bf437.png)

## 用途
背景が黒の上にinputタグを置く場合ですね。
ダークモード対応なんかに使えそう。

需要としてはあまりにニッチだし、アイコンを意識的に変えるってことはしないとは思う。
ただ引き出しを持っておくと何かの拍子で使えるかもしれない。
jsonファイルを読み込んで、キーでソートしてDOMとして展開する。

# やりたいこと
- jsonファイルを読み込みたい。
- jsonデータをつかって、記事の一覧をHTMLで出力したい。
- 出力場所は「\<dl id="entryList">\</dl>」の中。
- 「記事ID」でソートしたい。

# jsonのなかみ
```json
[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
```

# JavaScriptを書いてみた。
```JavaScript
// jsonファイル
const json_url = 'https://jsonplaceholder.typicode.com/posts/';

// jsonを読み込む
fetch(json_url)
  .then(response => response.json()) //json形式で返す
  .then(json_data => jsonArray(json_data))
  .catch(error => console.log(error));

//jsonデータを引数に受け取ってDOM操作を行う関数を作成
const jsonArray = (jsonObj) => {
 const data = jsonObj; //変数化

 // キーでソートする
 data.sort((a, b) => {
//  return (a.id < b.id) ? -1 : 1;  //オブジェクトの昇順ソート
  return (a.id > b.id) ? -1 : 1;  //オブジェクトの降順ソート
 });


 // HTMLを出力する
 Object.values(data).forEach(data => {

// テンプレートリテラル
let pub_html = `
<div>
　<dt>【${data.id}】${data.title}</dt>
　<dd>${data.body}</dd>
</div>
`;

  // #entryListの中に出力
  document.querySelector('#entryList').insertAdjacentHTML('afterend',pub_html);
 });
};
```

## 参考サイト

https://jsonplaceholder.typicode.com/

https://dezanari.com/js-sort/

https://www.javadrive.jp/javascript/string/index5.html
Lisp構造エディタを作った話

# ノスタルジー
Lispの構造エディタなんて今どきのLisperに通じないかもしれません。今やスクリーンエディタが主流です。Lisp向きのEmacsのような強力なスクリーンエディタがあります。もはや構造エディタを使うなんて場面はないでしょう。でも、急に懐かしくなっちゃったんです。初めてLispの処理系を入手したのはモトローラ6809のFLEXで動作するものでした。Lisp09という日本製のものでした。この処理系には構造エディタが付属していました。もう、そのマニュアルは行方不明なのですが、思い出しつつ作ってみることにしました。

# 参考文献
インターフェース誌のアーカイブにLispプログラミングを特集したものがありました。これは今でも残してありました。理化学研究所の稲田さんという人が書いた「Lisp構造エディタ」を参考にしました。

![IMG_1192.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33927/1f5457f3-4a31-cbc3-967d-4105aa613711.jpeg)


# 仕様
稲田さんの記事を参考にしつつ次のような仕様にしました。

```
# Spec
## q quit
## h help
## r move right
## l move left
## u move up
## d move down
## t move top
## positive number move right n
## negative number move left n
## i insert s-exp at left side of index
## a insert s-exp at right side of index
## k kill s-exp at index
## s replace s-exp at index
## c copy s-exp to buffer at index
## m paste s-exp from buffer at index
## x exchange s-exp at index and s-exp at index+1
## f find s-exp and set index 
## n find next and set index
## p print s-exp and index
## (s-exp) eval s-exp and print it
```


# 動作例
自作のEasy-ISLisp 処理系にライブラリとして構造エディタを実装しました。スクリーンエディタしか扱ったことがない人にとっては構造エディタの動作は想像しにくいと思います。以下は動作例です。

```
Easy-ISLisp Ver2.87
> (import "sedit")
T
> (defun foo (x) (+ x 1))
FOO
> (editf 'foo)
* p
(DEFUN FOO ### ###)  [0]
* 3
* p
(DEFUN FOO ### ###)  [3]
* d
* p
(+ X 1)  [0]
* 2
* p
(+ X 1)  [2]
* s 2
* p
(+ X 2)  [2]
* u
* p
(DEFUN FOO ### ###)  [3]
* (foo 1)
3
* f foo
* p
(DEFUN FOO ### ###)  [1]
* q
T
> 

```

# リストの破壊的操作の勉強にいいかも
ただ、ひたすらに懐かしくって構造エディタを作ってみました。今さらこれが何の役に立つのか？　リストの破壊的操作に慣れ親しむのに面白い題材かもしれません。
[python]AzureMLでファイルをマウントしたとき日本語がURLエンコードになる件

### Azureさん、日本語でおｋ
Azureファイル共有とかBlobからファイルを読み込むときなどに、dataset.mount()でtempフォルダでマウントすると、os.listdir()でファイル名の一覧取得時に日本語がURLエンコードされてしまいます。
厄介なことに実際のファイル名は日本語名なものですから、URLエンコードされたファイル名（パス）じゃ読み込めないんですね～

### コード
今回はAzure MachineLearning Studioのコンピューティングインスタンス上で実行するように作ったときのコード。
データセットにパワポが詰まったフォルダを指定してて、pptxファイルを読みだそうとしてますね。

```php:python
# 環境：Python 3.10 - SDK v2
from azureml.core import Workspace, Dataset
from pptx import Presentation
import pandas as pd
import os
import urllib.parse
import tempfile

# ワークスペースの設定、適当に。
subscription_id = <<subscription_id>>
resource_group = <<resource_group>>
workspace_name = <<workspace_name>>

workspace = Workspace(subscription_id, resource_group, workspace_name)

# データセットの設定、適当に。
dataset = Dataset.get_by_name(workspace, name = <<Dataset_name>>)

# データセットをマウント
mounted_path = tempfile.mkdtemp()
mount_context = dataset.mount(mounted_path)
mount_context.start()

file_names = os.listdir(mounted_path)

for i in file_names:
    # urllib.parse.unquote()で日本語に再変換
    decoded_str = urllib.parse.unquote(i)

    file_path = mounted_path+'/'+ decoded_str

    # pptxファイルのロード
    if '.pptx' in file_path:
        prs = Presentation(file_path)
```

### 結果の確認
言わずもがな
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3315153/41bd5ff2-1e55-f86a-c2d5-6c0b41d7d798.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3315153/1e9fe3c3-63e5-db59-9a45-df69a4af6cbf.png)

コードは適当に編集したのでバグってるかも。
module 'pandas' has no attribute 'TimeGrouper'というエラーの原因を調べてみた。

## 今回の疑問
とある書籍で勉強している際、```pd.TimeGrouper()```というものが出てきたが実行してみるとエラーに。
なので調べてみました。
```Python
import pandas as pd

tg = pd.TimeGrouper("10min")
#AttributeError: module 'pandas' has no attribute 'TimeGrouper'
```
## 調べたこと
まずはエラーコードを調べてみます。
AttributeError: module 'pandas' has no attribute 'TimeGrouper'
こちらの意味は「pandasにそんなもんねぇ(意訳)」だそうです。


次に公式さんを覗きます。が、TimeGrouperで調べてみてもGrouperばかりが検索に引っ掛かります。
https://pandas.pydata.org/docs/reference/api/pandas.Grouper.html




そこで以下の記事?質問掲示板?みたいなのを見つけました。
https://stackoverflow.com/questions/45156289/pandas-where-is-the-documentation-for-timegrouper

ここの掲示板によると
>pd.TimeGrouper() was formally deprecated in pandas v0.21.0 in favor of pd.Grouper().

「```pd.TimeGrouper()```はバージョン0.21.0で消えたから代わりに```pd.Grouper()```を使うといいよ。」みたいな感じだと思います。

さらに読み進めると```pd.Grouper(freq=)```で代用ができるそうです。
よって先ほどのコードは

```Python
tg = pd.Grouper(freq="10min")
tg
#TimeGrouper(freq=<10 * Minutes>, axis=0, sort=True, closed='left', label='left', how='mean', convention='e', origin='start_day')
```
こう直すことで無事に解決しました。
```pd.TimeGrouper()```はなくなっても、ちゃっかりTimeGrouperオブジェクト(あってるかな?)なことにﾌﾌｯとなりました。

## まとめ
バージョンによって機能が消されたり、追加されたりと移り変わりが激しいこの業界。
まだまだ勉強することがたんまりだなぁと痛感することになりました。

いつかは公式ドキュメントと握手くらいできるような仲になれればと思いながら終わりたいと思います。

参考にさせていただいたページ
https://pandas.pydata.org/docs/reference/api/pandas.Grouper.html
https://stackoverflow.com/questions/45156289/pandas-where-is-the-documentation-for-timegrouper
AWS AppRunnerのapprunner.yamlにSSM Parameter Storeの値を設定する

# AWS AppRunnerとは
- `AWS App Runner`はフルマネージド型のコンテナアプリケーションサービスでECRまたはGitHubのソースコードから簡単にデプロイ、実行できるサービスです
- 近年アップデートが進み、VPC内のリソースにもアクセスできるようになったりとより実用性が高まっています
	- 以前はVPC内のリソースにはアクセスできなかったのでRDSをPublicに配置しなければならなかったりとセキュリティ的に運用するのは難しかったのです。。。

# 本題
- 今回の記事では、構築設定に`apprunner.yaml`を利用する場合に環境変数にSSM Parameter Storeの値を設定する方法を記載します
	- AWSマネージメントコンソールから行う場合はポチポチでいけます
![構築設定](https://storage.googleapis.com/zenn-user-upload/91d5fc145697-20230403.png)
- AppRunnerにSSM ParameterStoreへのアクセス許可用ロールを付与している前提です

## arn形式で指定する場合
- arn形式でも設定できますが、ソースにAWSアカウントを含めることになるので基本的には後述のパラメーター名で指定するやり方を使うことが多いかと思います

```yaml
version: 1.0
runtime: nodejs16
build:
  commands:
    build:
      - npm install
run:
  runtime-version: 16.19.0
  command: npm start
  network:
    port: 3000
  secrets:
    # envではなくsecretsにて設定する
    - name: my-parameter
      value-from: "arn:aws:ssm:{ap-northeast-1}:{aws-account-id}:parameter/{parameter-name}"
```

## パラメーター名で指定する場合
```yaml
version: 1.0
runtime: nodejs16
build:
  commands:
    build:
      - npm install
run:
  runtime-version: 16.19.0
  command: npm start
  network:
    port: 3000
  secrets:
    # envではなくsecretsにて設定する
    - name: my-parameter-only-name
      value-from: "parameter-name"
```

# まとめ
- 個人的には`apprunner.yaml`で管理するのがいいのかなと思います、環境変数を追加する際やruntime等をいじるときに各AWS環境でポチポチするのもツライので
- 今回紹介した内容ですが2023年4月時点では[日本語版AppRunnerの開発者ガイド](https://docs.aws.amazon.com/ja_jp/apprunner/latest/dg/config-file-ref.html)には記載されていないです
	- 英語版だと書いてあります
	- AppRunnerに限らずですが、AWSのドキュメントは英語版を正としてマルチランゲージにて公開されていて、ローカライズのため更新時刻に差異が発生することがあるので気をつけましょう！
Db2 SQLでIBM MQと送受信：Db2のMQ連携機能ご紹介

# IBM MQとは？
IBM MQ は、メッセージ・キューを介してメッセージ・データを送受信することにより、多様なコンピューティング環境(*1)において、アプリケーション同士を様々なタイミングで通信できるようにするメッセージング・ソリューションです。
(*1)オンプレミス、マルチ/ハイブリッドクラウド、異種プラットホーム混在、同種など

MQは豊富な実績を持ち、高可用性構成やTLS暗号化通信もサポートするなど高い信頼性と堅牢性を持つソリューションでもあります。


IBM MQもDb2 LUW同様、2023年に30周年を迎えました。


Db2 LUWにはMQとの連携を実現する機能がDb2の関数(スカラー関数、表関数)として提供されています。
これらの機能により、Db2 SQLを使ってDb2からMQへメッセージを送信したり、MQからメッセージを受信したりできます。
それでは、MQとの連携機能をスカラー関数、表関数別にハイライト的に見ていきましょう。

# MQスカラー関数(_MQREAD/MQRECEIVE/MQSEND_)
## (a)MQREAD ##
MQからVARCHAR型データとしてメッセージを受信します。
受信後キューからメッセージは削除されません。
返すメッセージがない場合は、NULL 値が返されます。

## (b)MQRECEIVE ##
MQ からVARCHAR型データとしてメッセージを受信します。
この操作により、キューから受信メッセージが削除されます。
返すメッセージがない場合は、NULL 値が返されます。

## (c)MQSEND ##
VARCHAR型データをMQへ送信します。
戻り値(VARCHAR(1)型)は、成功した場合は 1、失敗した場合は 0 です。


**MQSEND使用例**
下記はSELECT文にメッセージ送信処理 (MQSEND)を組み込んだ例です。
```
SELECT DB2MQ1C.MQSEND (LASTNAME || ' ' || FIRSTNAME || ' ' || DEPARTMENT)
  FROM EMPLOYEE WHERE DEPARTMENT = '5LGA'; 
COMMIT;
```


**(補足)**
MQSENDのスキーマ名’DB2MQ1C’は、1フェーズ・コミット・トランザクションのMQ関数を意味しています。
非トランザクションMQ関数の場合は スキーマ名は’DB2MQ’です。

# MQスカラー関数(_MQPUBLISH/MQSUBSCRIBE/MQUNSUBSCRIBE_) #

MQのパブリッシュ(情報のプロバイダー)とサブスクライブ(情報のコンシューマー)に対応した関数もあります。

## (a)MQPUBLISH ##
VARCHAR型データを MQ にパブリッシュします。 
戻り値(VARCHAR(1)型)は、成功した場合は 1、失敗した場合は 0 です。

## (b)MQSUBSCRIBE ##
サブスクライブ(購読)したいトピックをMQに登録します。
戻り値(VARCHAR(1)型)は、成功した場合は 1、失敗した場合は 0 です。
トピックに一致するメッセージは、MQREAD、MQRECEIVE、MQREADALL、または MQRECEIVEALLの呼び出しによって読み取りまたは受信できます。 

**MQSUBSCRIBE使用例**
この例では、トピック”Weather”を含むメッセージをサブスクライブします。
```
VALUES MQSUBSCRIBE(‘Weather’);
```

## (c)MQUNSUBSCRIBE ##
既存のサブスクリプションを解除します。 
戻り値(VARCHAR(1)型)は、成功した場合は 1、失敗した場合は 0 です。


# MQ表関数(_MQREADALL/MQRECEIVEALL_)
MQ表関数(テーブル関数)には、以下が含まれています。
## (a)MQREADALL ##
MQからVARCHAR型データとしてメッセージ、そのメッセージのメタデータ(相関ID、トピック、キュー名、メッセージID、メッセージ・フォーマット)などを受信し、Db２の表として返します。 この操作では、受信メッセージはキューから削除されません。


**MQREADALL使用例**
この例では、キューからすべてのメッセージを受信します。 
メッセージとそのメタデータが表として返されます。
```
SELECT * FROM TABLE (MQREADALL()) AS T ;
```

## (b)MQRECEIVEALL ##
MQからVARCHAR型データとしてメッセージ、そのメッセージのメタデータなどを受信し、Db２の表として返します。 この操作では、受信メッセージはキューから削除されます。


今回はIBM MQとの統合を実現する代表的なDb2の機能を簡単にご紹介しました。
提供されている機能や使い方の詳細、考慮点(例：2フェーズコミットには対応していない)などはDb2マニュアルのトピック”**_WebSphere MQ and DB2 user-defined functions_**”、”**_Commit Environment for Db2 WebSphere MQ functions_**”をご参照ください。
この記事での説明は割愛しましたが、MQとの統合機能を利用するためには、**事前準備作業が必要**となっています。
こちらに関しては、Db2マニュアルのトピック” **_Installing and using the DB2 WebSphere MQ functions_**”などをご参照ください。
関数・無名関数・アロー関数

# この動画を見て点だった理解が線になった！

https://youtu.be/CcUk46e6TDk

# 学んだこと
<b>・関数</b>
– 処理の塊に名前をつけることできる。
– その処理の塊の名前を呼び出すと何度も使えるようになる。
– その処理の塊の名前を呼び出さないと使えない。

https://qiita.com/emioiso/items/49b75b699b355af56303

<b>・無名関数</b>
– 関数を無名関数に書き換えることができる。
– 無名関数は変数に代入できる。
– 関数が複数あると何の関数が何の処理をしているか探さないといけないので特定の関数だけを変更したい時
デメリット
– 処理が直接書いてあるので行が必然的に長くなるのでコード全体が読みにくくなる。

<b>・アロー関数</b>
– 無名関数を省略記法（シンタックスシュガー）で書けること
```html
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>function && allow</title>
</head>
<body>
	<button id="id-button">my button</button>
</body>
<script>
	// // 関数
	// btn = document.getElementById("id-button")
	// btn.addEventListener("click",clickedButton)

	// function clickedButton() {
	// 	alert("clickedButtonという名前の関数が動いたよ")
	// }

	// // 無名関数
	// // 関数を定義しなくてもaddEventListenerの引数に直接無名関数を書ける
	// btn = document.getElementById("id-button")
	// btn.addEventListener("click",function(){
	// 	alert("無名関数が動いたよ")
	// })

	// // 無名関数 + 変数
	// clickedButtonHensu = function(){
	// 	alert("clickedButtonHensuという変数に無名関数を代入して動いたよ")
	// }

	// btn = document.getElementById("id-button")
	// btn.addEventListener("click", clickedButtonHensu)

	// ↓はリロードするとアラートが出る。
	// clickedButtonHensu ()

	// // アロー関数
	// clickedButtonHensu = () => {
	// 	alert("アロー関数が動いてるよ")
	// }

	// function clickedButton(hensu) {
	// 	alert("clicked button")
	// 	hensu()
	// }
	// clickedButton(clickedButtonHensu)

	// // アロー関数2(シンタックスシュガー)
	clickedButtonHensu = () => alert("アロー関数の{}がないバージョンが動いてるよ")

	btn = document.getElementById("id-button")
	btn.addEventListener("click", clickedButtonHensu)

</script>
</html>
```
[Python]コマンドライン引数を受け取る方法

# はじめに
Pythonでコマンドライン引数を受け取るには、標準モジュールsysのargvを使う方法と、標準モジュールargparseを使う方法があります。それぞれの使い方について簡単にまとめてみました。
- 動作環境
    - Python 3.11

# sys.argv
sys.argvを使う場合、コマンドライン引数が文字列のリストとして sys.argvに渡されます。`sys.argv[0]`が実行されたpyファイルのファイル名です。

sys.argvを出力してみます。
```python: main.py
import sys
print(sys.argv)
```

```bash
$ python3 main.py arg1 arg2
```

```:stdout
['main.py', 'arg1', 'arg2']
```

引数が足りない場合は`IndexError`が発生します。
```python: main.py
import sys
print(sys.argv[1])
```

```bash
$ python3 main.py
```

```:stdout
IndexError: list index out of range
```
そのため、エラーメッセージやhelpメッセージを表示したい場合は、自分で実装する必要があります。
```python: 例
import sys

if len(sys.argv) == 3:
    file_name = sys.argv[1]
    password = sys.argv[2]
    print(f"File name: {file_name}, Password: {password}")
else:
    print("Usage: python main.py <file_name> <password>")
```

# argparse
argparseモジュールを使うと、helpメッセージなどを表示してくれるので、より柔軟で分かりやすく記述することができます。
```python: main.py
import argparse

# argparse.ArgumentParserクラスをインスタンス化して、説明等を引数として渡す
parser = argparse.ArgumentParser(
    prog="sample",  # プログラム名
    usage="python main.py <file_name> <password>", # プログラムの利用方法
    description="sample script.", # ヘルプの前に表示
    epilog="end", # ヘルプの後に表示
    add_help=True, # -h/–-helpオプションの追加
)

# 引数の設定
parser.add_argument("file_name", type=str, help="File name")
parser.add_argument("password", type=str, help="Password")

# 引数の解析
args = parser.parse_args()

print(f"File name: {args.file_name}, Password: {args.password}")
```

```bash
$ python3 main.py samplefile 123
```

```:stdout
File name: samplefile, Password: 123
```

引数が正しくないと、エラーメッセージ等が表示されます。
```bash
$ python3 main.py samplefile
```

```:stdout
usage: python main.py <file_name> <password>
sample: error: the following arguments are required: password
```

helpオプションが設定されている場合、`-h`(--help)オプションを指定すると、helpメッセージが表示されます。
```bash
$ python3 main.py -h
```

```:stdout
usage: python main.py <file_name> <password>

sample script.

positional arguments:
  file_name   File name
  password    Password

options:
  -h, --help  show this help message and exit

end
```

また、位置引数かオプション引数かも指定できます。オプション引数には接頭辞`-`か`--`を付ける必要があり、実行時に引数を指定する場所はどこでも構いません。
```python
# 引数の設定
parser.add_argument("-f", "--file", type=str, help="File name")
parser.add_argument("-p", "--passwd", type=str, help="Password")

# 引数の解析
args = parser.parse_args()

print(f"File name: {args.file}, Password: {args.passwd}")
```

```bash
$ python3 main.py -f samplefile -p 123
```

```:stdout
File name: samplefile, Password: 123
```

その他の使い方について[公式ドキュメント](https://docs.python.org/ja/3/library/argparse.html)等をご参照ください。


# おわりに
今回はPython標準モジュールsysのargvとargparseを使ったコマンドライン引数の受け取り方について簡単にまとめてみました。詳しい使い方が気にある方がいらっしゃいましたら、公式ドキュメント等をご参照ください。
データーシートの読み方

etherswitchにATUサポートを入れてみようかと思い立ちました。

自分が作ったドライバーコードでデーターシートが手に入っているInfinionのADM6996FCをターゲットにしてみます。

データーシートを見ると下記のようにありATUの処理が出来そうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/7d7ae89f-3167-3fa9-91d3-4f2178b5813c.png)

ところが、データーシートにはその詳細がありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/3e20ec93-b3d7-a55d-47ae-6587fcc6f9c2.png)

これを見てなにか思いませんか？

0x11aから0x12fが不自然に抜けています。

実はここにその機能が隠されています。

データーシートを読むためには透視能力が必要です。

というのは冗談でgithubを探したところ下記のコードが見つかり、処理が書いてあります。

https://github.com/search?q=filename%3Aadmmod.c

MACテーブルはコンフィデンシャルになる部分があるので、データーシートから抜いたのかもしれませんね。

コンフィデンシャルな部分なので、defineとかせずに直値でコーディングされてます。

上のコードはInfinionが書いたコードでGPLではないので、FreeBSDでつかっても問題ないですが、mipsがHEADから消されたので、もうレビューは出しません。

etherswitchのATUサポートコード作りました。

https://github.com/ZRouter/freebsd-src/commit/4562a4244ae0e46ffcfc65e734ecf4df7efc5ac5

削除の処理は参考にしたコードの処理をそのまま使っていますが、一覧を作る処理はちょっと違うようなので、etherswitchcfgコマンドのレジスタアクセス(reg)でいろいろ試してコマンドを探し出しました。

おそらく全削除やポート削除のコマンドが用意されているのではないかと思われますが分からないので、いったんリストを拾ってそれを元に処理するようにしました。
[TypeScript] 引数の型Tを型推論させない方法

TypeScriptで、ジェネリクスを型推論させずに手動で設定させるようにしたい場合があります。

具体的には、以下のようなコードです。

```ts
// DBやファイル等に値を書き込む関数を想定
function setValue<T>(value: T) {
  // ...
}

// ⭕ これは型エラーを出さない
setValue<number>(100);
// ❌ これをエラーにしたい（ジェネリクス部分を指定しない場合にエラーにしたい）
setValue(100);
```

上のコードにおいて、`setValue()`を呼び出す際に、必ず`setValue<number>(100)`と書かせるようにしたい（必ずジェネリクスの`<number>`の部分を書かないといけないようにしたい）という事です。

通常ではTypeScriptによる型推論が効くため、`T`型が自動で引数の型に推論されてしまいます。
TypeScriptによる引数の型推論を避けるには、以下のようにします。

## 引数の型`T`の型推論を避ける方法

以下のように、途中に`infer`による推論を挟むことで型推論を回避することができました。

```ts
// ↓このようにすると型推論を回避できる
function setValue<T = never>(value: T extends infer V ? V : never) {
  // ...
}

setValue<number>(100); // これは型エラーにならない
setValue(100); // これは型エラーになる
```

まず`<T = never>`で型のデフォルト値を設定することで、関数呼び出し時にジェネリクスが指定されなかった時に`T`が`never`型になるようにしています。
次に、`value`の型指定において`T extends infer V ? V : never`とすることで、必ず`T`が指定されており、かつ、valueが`T`型に代入可能であることをチェックしてくれます。

## これの何が嬉しいのか

このような事をしても、`setValue()`は依然として引数にどんな値でも受け取るため、特に安全になるわけではありません。

<details>
<summary>雑談</summary>
（記事等で紹介されているものの中には、型エラーを解決する方法と称して型エラーを握りつぶしているだけのものが時々あります。筆者としては、そういった方法を用いるくらいなら`as`でキャストする方が危険な操作を自明に表すことができ、ベターだと考えています。）
</details>

ただし、後々のリファクタリングでうっかり引数の型が変わってしまうことを防止する効果はあると思います。

## まとめ

- ジェネリクスの型`T`を引数から推論させないためには、`infer`を使う
- このようにしてもプログラム自体が安全になるわけではないが、リファクタリング時のうっかりミスを前もって防止する効果はあるかもしれない。
Unity 2020.3.20f1 で Python for Unity をインストールする方法

## はじめに
この記事では、Unity 2020.3.20f1 に Python for Unity をインストールする方法を紹介します。


## インストール環境
- Unity（Unity Hubを使用。Unity本体のバージョンは2020.3.20f1）
- macbook pro

## 手順
#### Python for Unity のインストール
まず、新規にUnity Projectを作成します（説明は省略）

次に、作成したUnity Projectの中で、Edit > Project Settings > Package Manager > Advanced Settings から "Enable Preview packages" にチェックを入れます。（下画面赤枠参照）
![Fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3310536/5d617675-49cb-a752-29e3-de22376f1033.png)




#### manifest.json の編集
次に、Unity Project のディレクトリ > Packages > manifest.json を開き、以下の行を追記して保存します。開く際のエディタは、今回はVSCodeを使用しました。

"com.unity.scripting.python": "4.0.0-pre-1"

注）追記する際には、前の行の後に","をつけることを忘れない
![Fig2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3310536/90a1edec-bb2c-3676-881d-3fe4c7c7c4dd.png)




#### Package Manager でパッケージをインストール:
1. Window > Package manager を開き、プルダウンから "Packages: Unity Registry" を選択します。
1. Package Manager ウィンドウの下部にある更新マーク横のプルダウンを開き、 "Reflesh list" ボタンをクリックします。
1. インストール可能なパッケージ一覧が表示されるので、"Scripting Python" を探します。バージョンを選択してインストールします
    1. 今回は 7.0.0 を選択
    1. Unity のバージョンに応じて対応可能なバージョンが異なることに注意してください。
1. Update to 7.0.0を押す

![Fig3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3310536/a7dbab01-0a1d-f0c3-a4e7-78cd22ed7b52.png)





以上で終了です。うまくいった場合、Unityのコンソールログに「Python installed successfully」と表示されるかと思います。

## おわりに

これで、Unity 2020.3.20f1 に Python for Unity がインストールされました。今後は実際にPython を使い、Unity プロジェクトを開発していきたいと思います。

## 参考リンク

- Unity Package Manager について：
    https://qiita.com/k7a/items/cc5d09bc1000551b203f

- Python for Unity Editor now available as a preview package!:
    https://forum.unity.com/threads/introducing-python-for-unity-editor.812748/#post-5526181

- Python Scripting 7.0.0-pre.1:
https://docs.unity.cn/Packages/com.unity.scripting.python@7.0/manual/installation.html
redmineに入れた実績時間が8時間に満たない人

redmineに入れた実績が少なかった人をあぶり出すcsvファイルの作成スクリプト

2023/4/13
GPT-4で書き直しました

```python
import csv
import os
import datetime
from collections import defaultdict
from redminelib import Redmine

# Redmineへのアクセス設定
redmine_url = 'https://your-redmine-url.com'
api_key = 'APIキー'
redmine = Redmine(redmine_url, key=api_key)
csvfile = os.path.basename(__file__) + ".csv"

# 前日の日付を取得
yesterday = (datetime.date.today() - datetime.timedelta(days=1)).strftime("%Y-%m-%d")

# 前日の時間の合計が8時間に満たない人を抽出
def find_users_with_less_than_8_hours():
    time_entries = redmine.time_entry.filter(from_date=yesterday, to_date=yesterday)
    user_time = defaultdict(float)

    for entry in time_entries:
        user_time[entry.user.id] += entry.hours

    users_with_less_than_8_hours = []
    all_users = redmine.user.all()
    for user in all_users:
        hours = user_time.get(user.id, 0)
        if hours < 8:
            users_with_less_than_8_hours.append((user.login, user.firstname ,user.lastname, hours))

    return users_with_less_than_8_hours

# CSVファイルに出力
def export_to_csv(users_with_less_than_8_hours):
    with open(f'less_than_8_hours_{yesterday}.csv', 'w',  encoding='utf-8', newline='') as csvfile:
        fieldnames = ['username', 'First Name' , 'Last Name' , 'total_hours']
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

        writer.writeheader()
        for user in users_with_less_than_8_hours:
            writer.writerow({'username': user[0], 'First Name': user[1], 'Last Name': user[2], 'total_hours': user[3]})

if __name__ == "__main__":
    users_with_less_than_8_hours = find_users_with_less_than_8_hours()
    export_to_csv(users_with_less_than_8_hours)

```
ChatGPTにJavaScriptからアクセスする

## はじめに

なぜか、ChatGPTにJavaScriptからアクセスするコードは
普通に検索しても得られないのですが、なんとかこれで動くようですのでお試しください

### 参考にしたコード
「ChatGPTにJavaScriptでAPIにアクセスする方法」でChatGPTに質問して得られたコード→古くてエラー
「example」の「json」のコード等

## コード
```index.html
<!doctype html>
<html>
<meta charset="utf-8">
<script>
const apiKey = "APIキー";

// APIエンドポイントを設定します
const endpoint = "https://api.openai.com/v1/completions";

// リクエストデータを設定します
const requestData = {
	"model": "text-davinci-003",
    prompt: "CM動画を閲覧してどのように感じられましたか？",
	"temperature": 0,
	"max_tokens": 450,
	"top_p": 1.0,
	"frequency_penalty": 0.0,
	"presence_penalty": 0.0
};

// リクエストヘッダーを設定します
const requestHeaders = {
    "Content-Type": "application/json",
	"OpenAI-Organization": "org-mEa6WWqeF4GNLJ6KojVd1OOY",
    "Authorization": `Bearer ${apiKey}`
};

// APIにPOSTリクエストを送信します
fetch(endpoint, {
    method: "POST",
    headers: requestHeaders,
    body: JSON.stringify(requestData)
})
.then(response => response.json())
.then(data => {
//    console.log(data.choices[0].text);
	  alert(data.choices[0].text);
})
.catch(error => {
    console.error(error);
});
</script>
</html>
```
【裏技】BurpSuiteで余計なトラフィックを検知しないようにする


本記事はBurpをセットアップし終わった方向けです。
もし済んでなかったり、セットアップの方法を見たい方は以下の記事をご覧ください。

https://qiita.com/LabPixel/items/e42f8f28ccb78d057cb1

# Burpの問題点

トラフィックを見ているときに、GoogleやFireFoxのトラフィックが入ってきて頭がクラッシュすることがある。

# 対処法

Proxy -> Options -> TLS Pass Through の順に開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/3ac68585-6cd6-cffb-a7cb-3f47540436ca.png)

ここでAddをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/fd3bf711-30cc-df4a-3ffe-626ee240d4a9.png)

`Host or IP range:` のところに以下を追加してください。

```
.*\.google\.com 
.*\.gstatic\.com
.*\.mozilla\.com
.*\.googleapis\.com
.*\.pki\.goog
```

以下のようにすればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/b4ea3d64-61ad-5d47-c50f-458d8b018d0a.png)

これでもう余計なトラフィックは見なくて済みます！

# 参考サイト

https://twitter.com/NinadMishra5/status/1636029487623094272
KaliのBurpSuiteの簡単なセットアップ方法

# BurpSuite

Web周りの攻撃を支援するようなツールです。

# セットアップ

## Burpのローカルプロキシ

kaliにはデフォルトで入っていると思いますので、インストールは省略します。

検索欄で`burp`と検索するとこのようなアイコンが出てきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/7af37c61-ef69-5d35-af59-6236d3484b02.png)

開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/5b50c2c8-8152-dd8d-0805-4d5586cd6cb3.png)

Proxy -> Proxy setiingsを選択してください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/60fd6e23-4ae5-24de-cd41-e429e9d46de1.png)

Proxy Listenarsの`Edit`を選択すると以下のような画面が出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/5f80bca1-e8eb-5f22-fde8-68ba5612e622.png)

`Bind to port: 8081`と設定します。

## FireFoxのプロキシ設定

FireFoxのアドオン管理画面を開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/ea7360c9-40c8-af7f-d670-41230f6d9649.png)

`FoxyProxy`と入力してEnterします。

出てきた拡張機能をインストールしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/b3592df3-cc4f-b2cb-6e79-598031942f25.png)

右端の狐のアイコンをクリックして Options -> add を押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/2523f0e3-091a-eaf3-f08a-8329fbba6fd3.png)

以下のように編集します。

|||
|---|---|
|Proxy IP address or DNS name|127.0.0.1|
|Port|8081|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/ae940c20-3ae7-6983-8679-bdb9e5e6998c.png)

`127.0.0.1:8081`を選択します。


## BurpSuiteのCA証明書

http://127.0.0.1:8081/ にアクセスします。

このような画面が出るので、`CA Certificate`を押して証明書をダウンロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/46d83292-bc90-29f5-1914-f254aee46fcf.png)

次に、設定を開いて、証明書を追加します。

Privacy & Seculity -> Seculity -> View Certificates... をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/f220e1e6-9b1f-0071-486b-259b3018a140.png)

Import をクリックして、先ほどダウンロードしたファイルを読み込ませます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/bd02a61d-7cec-027f-2256-d154114a3339.png)

以下画面が出るので、上のチェックボックスに印をつけて、OKします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/a26f609e-9cab-5422-9efe-62a39bd774a6.png)

これで終了です！

# より快適にする

使ってる際に、GoogleやFireFoxなどのトラフィックを検知することがあります。
それらを除外してより快適に使いたい方は以下の記事をご覧ください。

https://qiita.com/LabPixel/items/b702d4bcbf2389459afc

# 使い方

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/f32e8c85-817a-49c7-143d-8e8f536faacb.png)

`Intercept is on`にした状態で、FireFoxでサイトにアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/ac061135-6d0a-b4cf-eb53-3e2ae9916c3c.png)

そうすると、Burpの方で何が送信されているか確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/22914feb-2872-2e58-ea36-f3d545d7b634.png)

Forwardを押すと、FireFoxにパケットが送られるので以下のように表示できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1523062/a9975662-16f0-3aea-0cf1-1c3197ae4aad.png)

# まとめ

Burpのセットアップ少し忘れやすいけど、セットアップしたらめっちゃいい感じに使えるからおすすめ！

Burpを開かないでFireFox使うときは拡張機能でProxyを踏まないようにしよう。
Makefileでオブジェクトファイルを生成する3つの方法


# 目次
[1.オブジェクトファイルの作り方](#-オブジェクトファイルの作り方)
[2.べた書き](#-（１）べた書き)
[3.パターンルール](#-（２）パターンルール)
[4.サフィックスルール](#-(３)サフィックスルール)
[5.沼った部分](#-自分が沼った部分2つ)
[6.おわりに](#-最後に)

# オブジェクトファイルの作り方

### （１）べた書き

```makefile
$(TARGET) : $(OBJCS)
	$(CC) -o $@ $^

test1.o : test1.c
	$(CC) -c test1.c

test2.o : test2.c
	$(CC) -c test2.c

main.o : main.c
	$(CC) -c main.c
```
少し面倒ですが、ひとつづつ書いていく作戦です
```
gcc -c test1.c
gcc -c test2.c
gcc -c main.c
gcc -o target test1.o test2.o main.o
```
ターミナルでしっかりと実行されていることが確認できます

### （２）パターンルール

```makefile
$(TARGET) : $(OBJCS)
	$(CC) -o $@ $^

%.o : %.c
	$(CC) -c $<
```
パターンルールを用いたものです！
拡張子以外が同じ名前ファイルからオブジェクトファイルを作るというルールを追加しています。
TARGETを生成する時にtest1.oが無ければ「％」の部分に「test1」が置き換えられて実行されます。そのため（１）と同じ実行結果が得られます。
### (３)サフィックスルール
```makefile
$(TARGET) : $(OBJCS)
	$(CC) -o $@ $^

.c.o :
	$(CC) -c $<
```
サフィックスルールを用いたものです(時代遅れらしい）
やっていることはパターンルールと全く同じです。
なんとこれだけでさっきと同じように実行されます。
（依存関係の部分に何も書かなくていいのが不思議）

# 自分が沼った部分2つ
### そもそもオブジェクトファイルを生成してなかった
```makefile
SRCS = test1.c test2.c main.c
OBJCS = $(SRCS:.c=.o)
TARGET = target

$(TARGET) : $(OBJCS)
	$(CC) -o $@ $^
```

上のコードを書くだけで実行ファイルを生成できると思っていましたが下記のエラーが出てしまい、実行ファイルを生成できません。
```terminal
No rule to make target 'test1.o', needed by 'target'.  Stop.
```
ところが不思議なことにechoでOBJCSを見ると中身があるではないですか？！
```
test1.o test2.o main.o
```
OBJCSの中身にはしっかりとオブジェクトファイルが入っているのになぜtest.oが無いとエラーで言われるのでしょうか？
```makefile
OBJCS = $(SRCS:.c=.o)
```
それはこの部分はSRCSの値を受け取り.cを.oに置き換えた文字列リストを作っているだけだからです。
そのため、TAEGETを作る際にtest.oという文字列のファイルねーじゃねーーか(# ﾟДﾟ)と怒られてしまうわけです。。
そこで実際にOBJECTを作る必要があるのです。
### 間違った作り方
```makefile
$(TARGET) : $(OBJCS)
	$(CC) -o $@ $^

$(OBJCS) : $(SRCS)
	$(CC) $^ -c -o $@
```
ターゲットと依存関係について学んだ自分はこのように書きました
もちろんエラーが起きるのですが、はじめは原因がわからずにいました
makefileが悪いのではなくgccのコマンドが原因です。
```
gcc test1.c test2.c main.c -c -o test1.o
```
このようなコマンドを実行することになりエラーが起きてしまいます。
一度に3種類のオブジェクトファイルを作るgccコマンドは無いので上の方法で作るしかないです。もちろんSRCとOBJECTが一種類の場合は実行できます。
# おわりに
やっていることは同じなのに複数の書き方があると、どれが正しいのか不安になり不要なことをしてしまいがちですよね、、上に書いた３つは同じ結果が得られるので好きな方法を使ってみてください！

## 参考文献
サフィックスルールとパターンルールについて
https://www.miraclelinux.com/tech-blog/q1kxz6
サフィックスルールについて
http://nenya.cis.ibaraki.ac.jp/TIPS/makefile2.html
makefileの入門
https://qiita.com/mizcii/items/cfbd2aa17f6b7517c37f
lolipopのレンタルサーバーをVSCode上で編集したかった話

# 概要
vimは悪くないのですが飽きたのでvscodeを用いて環境を変化させ楽をしたかった。

## ssh接続拡張

https://code.visualstudio.com/docs/remote/ssh

これを使うことによってできるらしいので試してみた。

## 結果

できませんでした。同じ人が複数人いたことも確認しました。

https://blog.zebrasoft.co.jp/archives/8853

対応してくれる事を願います。
returnの位置について

# はじめに
レビューで指摘された際に、忘れないようにしたいと思ったのでメモを残します

### 実装内容
・planのcostが０以下のの場合は、SlackNoticeServiceでSlack通知
・planのcostが０以上の場合は、planのstatusをreceivedへ変更

### レビュー指摘前のソースコード
```
if plan.cost > 0
  SlackNoticeService.notice_plan(plan)
else
  plan.update(status: :received)
end
```
### レビュー指摘①のソースコード
ネスト深くならないように修正

```
cost = plan.cost
return SlackNoticeService.notice_plan(plan) if cost > 0
plan.update(status: :received)
```
### レビュー指摘②のソースコード
このメソッドの戻り値にSlackNoticeService...の戻り値を返したいわけではない為、この書き方の方が良い
```
if plan.cost > 0
  SlackNoticeService.notice_plan(plan)
  return
end
plan.update(status: :received)

```
### ポイント
単純にネストを深くしないようにreturnを記載していましたが、
returnの内容・位置も考えて記載する必要があると勉強になりました
EC-CUBEプラグインを管理画面から削除できない場合の対処法

# FTPでファイルを削除

root/app/Plugin/[削除したいプラグインディレクトリ]　を削除

# composer.jsonから削除

 "ec-cube/[削除したいプラグインコード]": "^4.1",

# データベースからプラグインテーブルを削除

plg_[削除したいプラグイン名]　テーブルごと削除

# データベースのdtb_pluginから列を削除

# コマンドライン
rootで
php bin/console eccube:generate:proxies