OmniAICreator/Qiita-1.07M huggingface dataset:
【メモ書き】無数のPDFをPythonで黒塗りする

## 背景
所属している組織で数十枚のpdf形式のエントリーシート(ES)の黒塗りをする機会がありました。Adobe Acrobatでもpdfの黒塗りは手動できたのですが、黒塗りする箇所はpdfごとに変わらない & 枚数多すぎて手作業でやるのは面倒だったので黒塗り作業をPythonで自動化しました。統一された形式のPDFの特定箇所を黒塗りする需要は他にもありそうな気がしたので、その手順についてここで紹介していきます。


## 手順
以下でPython3で数枚のPDFの同じ箇所を黒塗りする方法について説明します。

### Step1 PyMuPDFをインストール
```bash
$ pip3 install PyMuPDF
```

### Step2 pdfの黒塗りしたい箇所の座標を決める

残念ながらPDF上の座標を簡単に取得できるツールはあまりありません。このため、「テキトウに座標を指定」→「黒塗り」を繰り返して座標を特定するのが一番速いかもしれません。

PDFは左上が原点(0,0)となっているはずなので、ページの高さや幅から、大まか座標を推定し、そこから微調整することをお勧めします。なお、座標は以下の形式で指定します。

```python
#黒塗り箇所を2ヶ所指定
#座標形式：(右上のx座標、右上のy座標、左下のx座標、左下のy座標)
redact_coords = [(37,80,550,219),(37,220,289,242)]
```

### Step3 黒塗り関数の定義

```python3
import fitz
 
class Redactor:
 
    def __init__(self, path,outpath,redact_coords):
        self.path = path
        self.outpath = outpath
        self.redact_coords = redact_coords
 
    def redaction(self,page_nums):
        # PDF開封
        doc = fitz.open(self.path)
        # 各ページをiterate
        for i,page in enumerate(doc):
            if i in page_nums:
                areas = [fitz.Rect(coord) for coord in self.redact_coords]
                [page.add_redact_annot(area, fill = (0, 0, 0)) for area in areas]
                # 黒塗りする
                page.apply_redactions()    
        # 新しいPDFとして保存
        doc.save(self.outpath)
        print("Successfully redacted")
```

### Step4 1枚のPDFに対して黒塗り関数を実行
```Python3
# 入力PDF
path = '<入力pdfへのパス>'
# 出力PDF
outpath = '<黒塗り済みpdfの書き出しパス>'
# 黒塗り座標
redact_coords = [(37,80,550,219),(37,220,289,242)]
# 黒塗りページ
pages_to_redact = [0,1]

redactor = Redactor(path,outpath,redact_coords)
redactor.redaction(pages_to_redact)
```
上のコードでは、`test.pdf`というPDFの、0ページ目と1ページ目の両方の`(37,80,550,219)`と`(37,220,289,242)`が黒塗りされます。

### Step5 複数枚のPDFに対して黒塗り関数を実行
`directory`変数に黒塗りしたいpdfが入ったフォルダのパスを渡し、あとはfor文で処理します。
```Python3
import os
import glob

# 黒塗り座標
redact_coords = [(37,80,550,219),(37,220,289,242)]
# 黒塗りページ
pages_to_redact = [0,1]

# 黒塗りしたいpdfが入ってるフォルダの指定
directory = '<pdfファイルが置かれたフォルダへのパス>'
pdf_files = glob.glob(os.path.join(directory, '*.pdf'))
for path in pdf_files:
    outpath = f"{path[:-4]}_redacted.pdf"
    redactor = Redactor(path,outpath,redact_coords)
    redactor.redaction(pages_to_redact)
```

### Step6 複数枚のPDFに対して、ページごとに異なる箇所を黒塗りする

```Python3
import os
import glob

# 黒塗り座標
redact_coords_list = list(
[(37,80,550,219),(37,220,289,242)], #0ページ目で黒塗りしたい箇所
[(50,80,300,400)] #1,2ページ目で黒塗りしたい箇所
)

# 黒塗りページ
pages_to_redact_list = list(
[0], #0ページ目の指定
[1,2]#1,2ページ目の指定
)

# 黒塗りしたいpdfが入ってるフォルダの指定
directory = '<pdfファイルが置かれたフォルダへのパス>'
pdf_files = glob.glob(os.path.join(directory, '*.pdf'))
for path in pdf_files:
    for redact_coords, pages_to_redact in zip(redact_coords_list,pages_to_redact_list):
        outpath = f"{path[:-4]}_redacted.pdf"
        redactor = Redactor(path,outpath,redact_coords)
        redactor.redaction(pages_to_redact)
```
パレートの法則をPythonで表現する

# はじめに
パレートの法則というものがあります。80:20の法則とか呼ばれたりもします。
https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%AC%E3%83%BC%E3%83%88%E3%81%AE%E6%B3%95%E5%89%87

このグラフをPythonで書きたいと思い、いろいろ模索しました。

# 要件
まず、以下を第一要件とします。
>仕事の成果の8割は、費やした時間全体のうちの2割の時間で生み出している。（Wikipedia「パレートの法則」より）

これをグラフにするため、(20,80),(100,100)を通るようなグラフにしたいと思います。

# べき分布・べき乗則
べき分布とかべき乗則とか呼ばれるものがあるそうです。
https://design.kyusan-u.ac.jp/OpenSquareJP/?Distribution
ここに書かれている以下の式
y=ax^(-k)+b
を使って(20,80),(100,100)を通るようなグラフを書ければよいのではないかと考えました。

# プログラム
そうとわかればあとはPythonで書いてみるだけです。

```Python:graph.py
#!/usr/bin/env python
# coding: utf-8

# matplotlibのインポート
import matplotlib.pyplot as plt
import japanize_matplotlib  # <- 日本語の出力に必要
import numpy as np

# x座標は0.1単位で用意
x  = np.linspace(0.1, 100, 1000)
# 単なる反比例
y1 = (-480) / (x) + 104

# グラフの設定
plt.plot(x, y1, label='反比例カーブ', color='blue')
plt.scatter(x_points, y_points, color='blue', marker='o')
plt.xlabel('費やした時間 (%)')
plt.ylabel('仕事の成果 (%)')
plt.title('工数と品質の関係')
plt.xticks(np.arange(0, 110, 10))
plt.yticks(np.arange(0, 120, 10))
plt.grid(True)
plt.xlim(0, 100)
plt.ylim(0, 100)
plt.legend(loc='lower right')

# グラフの表示
plt.show()
```

y=ax^(-k)+b の一番シンプルな式は k=-1、つまり反比例の式ですので、まずはそれでモデル化を試みました。
これは(20,80)を通り、(100,100)に近づきます。

# おわりに

モデル自体が非常に単純なので、もっとモデル自体を見直して改善してみたいと思いました。

（おわり）
Swift PlayGroundsファイルを利用したSwift Package用のExampleを作ってみる。

Swift PlayGroundsファイルを利用したSwift Package用のExampleを作成します。

# 環境
OS: macOS 13.3.1
PlayGrounds.app: バージョン4.1 (1676.15)
Xcode.app: Version 14.3 (14E222b)

# PlayGroundsでExample用のAppを作成する。
Appを作成します。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519522/50cc4a8e-c843-0228-4c48-9da1347a52aa.png)
もしくは
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519522/fcbf4926-cd0e-883f-3299-5a425f9baaea.png)

名前を変更します。ExampleAppとしました。
一度バツボタンで閉じます。

作成されたPlayGroundsのディレクトリを表示します。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519522/970e1281-7d61-f4f4-8139-58242c1dc807.png)

.swiftpmファイルの名称もAppの名称と同じにします。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519522/cbe7cd40-a71f-7e35-8a77-bade00d16815.png)

PlayGroundsでの作業は以上です。

# Swift Packageの作成。
Swift Packageを作成するディレクトリに「Example」というディレクトリを作成します。
このディレクトリの中に「PlayGroundsでExample用のAppを作成する。」で作成したAppを移動もしくはコピーします。

XcodeでSwift Packageを作成します。今回はOpenSwiftPackageInSwiftPlaygroundsとしました。
ここで注意点です。名称は必ず配置するディレクトリと同じにするようにしてください。
理由としては次の作業でライブラリの名前を冠したディレクトリを削除するためです。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519522/c0fb5cfd-d73f-547f-6248-7d5206a1c47f.png)

一度バツボタンで閉じます。

画像にあるファイルを1階層上に移動させます。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519522/f360cc9d-17f2-8ea3-ab67-f79d111b1277.png)

ライブラリの名前を冠したディレクトリを削除します。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519522/1d07705c-cc6b-b19f-a0b2-251b10f2d5b4.png)

## Swift PackageとPlayGroundsで作成したExample用のAppを紐づける。
「PlayGroundsでExample用のAppを作成する。」で作成したAppの.swiftpmファイルをVSCodeなどのエディタアプリで開きます。

Package.swiftのtargetsのすぐ上に下記のコードを追加します。
```
dependencies: [
    .package(path: "../..")
],
```

次に.executableTargetコード内にnameの下に下記のコードを追加します。
```
dependencies: [
    .product(name: "OpenSwiftPackageInSwiftPlaygrounds", package: "OpenSwiftPackageInSwiftPlaygrounds")
],
```

保存し、エディタアプリを閉じます。

.swiftpmファイルをXcodeで開きます。
画像のように表示できたら紐付けは完了です。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519522/f29c59e1-d3e7-9243-0197-5b31dc69e5c2.png)

試しにPackagesを確認するとローカルファイルを参照できていることがわかります。
![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519522/049e70d3-6a4d-d44a-f236-16b006235e7f.png)

今回作成したSwift PackageとそのExampleはこちらで公開していますのでご参考になればと思います。
https://github.com/kokiTakashiki/OpenSwiftPackageInSwiftPlaygrounds

最後までお読みいただきありがとうございました。
Laravel9でログイン認証laravel/Breezeをインストールしようとしたらエラーの発生とその対処方法

laravelにはログイン認証を実装するための便利なコマンドがあります。
今回、ログイン認証ツールである「laravel/Breeze」をインストールしようとした際に、エラーの内容と対象方法を
共有させていただきます。

【検証環境】
OS：Windows11
Laravelのバージョン：9.52.9

```Laravelのバージョン確認コマンド
php artisan -V
```

下記のコマンドを叩いてlaravel/Breezeをインストールするとエラーが発生。
```Laravel/Breezeをインストールするためのコマンド
composer require laravel/breeze --dev
```

![laravelトラブルシューティング1_0618.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/ab852b3b-fdf7-1ccd-928a-1060b2ecea64.png)

いくつかのバージョンでコンフリクトが起こっているので、インストールができないとのこと。
ですが、このエラー文をよく見ると「このようにコマンドを叩けばよいのでは？」と親切に入力コマンドの候補が記載されています。

```エラーを解消したコメンド
composer require laravel/breeze:*
```

そうすると、無事にlaravel/Breezeをインストールすることができました。

![laravelトラブルシューティング2_0618.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/e73b27f4-2ff4-e9ef-c83f-fb93ddb543ed.png)
Railsアプリ作成からコントローラ、ルーティング作成まで

## 概要
改めて、Railsアプリ作成からコントローラ、ルーティング作成までを、備忘録として記述させて頂きます。

## Railsアプリ作成
まずは、ターミナルを開き、作成したいRailsアプリを作成したいディレクトリに移動し、そこで以下のコマンドを実行します。その結果、直下にRailsアプリに必要なディレクトリが新規作成されます。

以下のコマンドの場合は`myapp`ディレクトリが作成されます。

```zsh:
$ rails new myapp
```

作成完了後、`myapp`ディレクトリに移動します。

```zsh:
$ cd myapp
```

そして、現在いるディレクトリをエディターで開きたいので、現在いるディレクトリをエディターで開く以下のコマンドを実行しましょう。

```zsh:
$ code .
```
これで、エディターを起動することができます。

## コントローラーの作成
:::note
**ユーザー(ブラウザ)からのリクエストを受けるコントローラー**を作成します。
:::
まずは、Railsが提供するジェネレーターコマンドを使って、以下のようにコントローラーを作成します。

```zsh
$ rails generate (自動生成する種別) (種別毎の指定)
```

`generate`は`g`に省略可能です。また、コントローラーを作成する例は以下のようになります。

```zsh
$ rails g controller (コントローラー名) (アクション名)
```

では、実際にコントローラーを作ってみましょう。

以下は、アクション(コントローラーのメソッド)を省略して`Usersコントローラー`を作成した例です。

```zsh
$ rails g controller users
```

これにより、`ビューディレクトリ`、`テストコード`、`ヘルパーメソッド`、`アセット`、`CoffeeScript/SCSS`が生成されます。

`Usersコントローラー`を作成したところで、続いてはアクションを作成していきましょう。
まず、`app/controllers`直下に`users_controller.rb`というファイルが作成されているのを確認して下さい。開くと`ApplicationControllerクラス`を継承した`UsersControllerクラス`が空で定義されているのを以下のように見つけることができると思います。

```rb:users_controller.rb
class UsersController < ApplicationController
end
```

このクラスに、インデックスメソッドを追加してみましょう。

以下のように行います。
```rb:users_controller.rb
class UsersController < ApplicationController
    def index
        render plain: 'Hello World!'
    end
end
```
`render`はコントローラーに備わってるメソッドです。`クライアントに返すレスポンスを定義します`。また`plain:`は`その後の値で指定された文字列をクライアントに返すための指定です`。この場合は、画面に「Hello World!」が表示されます。

## ルーティングの設定
:::note
**ユーザー(ブラウザ)からのリクエストを、コントローラーのアクションに割り振る**役割を担うのが**ルーティング**です。
:::
ルーティングは`config/route.rb`のファイルで設定を行うことができます。

以下のように記述して下さい。

```rb:routes.rb
Rails.application.routes.draw do
    get '/users' => 'users#index'
end
```

ここで、ここまでの工程が上手くいっているか、サーバーを起動して確認してみましょう。

以下のコマンドをターミナルで実行して下さい。

```zsh:
$ rails s
```
その後に、ブラウザで`http://localhost:3000/users`にアクセスして動作を確認します。その結果、「Hello World」と表示されるのが確認できると思います。

また、その他、ルーティングのやり方は以下をご参照下さい。
[Railsでよく使いそうなルーティングのやり方 - Qiita](https://qiita.com/kimascript/items/ebb3de84e4d8f5157535)

## まとめ
ここまでの工程をまとめると以下のようになります。
```plantuml
ブラウザ->ルーティング: リクエスト送信
ルーティング->コントローラー: マッピングされているコントローラーにルーティング
コントローラー-->ブラウザ: 'Hello World!'をレスポンス
```

## 参考
- [render | Railsドキュメント](https://railsdoc.com/page/render)

## 続き
続きの記事は⤵︎
[Railsのビューの基本 - Qiita](https://qiita.com/kimascript/items/672b5e7b37ebf42ffc23)
PDFをテキスト変換

# １．なにこれ
PDFからテキストに変換する簡単なPythonスクリプトを紹介します。

# ２．前準備
### 2.1. 環境について
　環境：Python3.11.3
　　　　llama-index==0.6.27

### 2.2. llama-index
　最近はやりのChatGPTに外部データを食わせるためのライブラリです。
　今回はPDFのドキュメントの抽出に、この機能の一部を利用します。

# ３．実行
```:構成
pdf2txt.py
　　└─ document
　　　　　├─ pdf   # ここにPDF配置
　　　　　└─ txt   # 変換後のTEXTの置き場
```

```:pdf2txt.py
import glob
from pathlib import Path
from llama_index import download_loader

READ_PATH = "XXX\\document\\pdf\\*"
WRITE_PATH = "XXX\\document\\txt\\"
EXTENSION_TEXT = ".txt"

CJKPDFReader = download_loader("CJKPDFReader")
loader = CJKPDFReader()

files = glob.glob(READ_PATH)
for file in files:
    org_file = Path(file)
    out_file = Path(WRITE_PATH + org_file.stem + EXTENSION_TEXT)
    if not out_file.exists():
        documents = loader.load_data(file=org_file)
        with open(out_file, mode='w', encoding='UTF-8') as f:
            f.write(documents[0].text)
```

初回起動は必要なライブラリのダウンロードで時間がかかります。
UIInterpolatingMotionEffectの簡単実装【デバイスの傾きによってViewを動かす】

# はじめに
UIInterpolatingMotionEffectをつかってデバイスの傾きによってViewを動かせたのでメモ
### こんな感じに動きます
![ezgif-1-346bc396a9.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883687/98852993-735a-c164-3c7e-174d46905533.gif)

# 実装
ストーリーボードでこんな感じでレイアウトを組みます｡
中央に配置された赤い色のmotonViewを動かします
![スクリーンショット 2023-06-19 0.04.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883687/43f0197b-b2a0-dab9-6b8d-babb8ff5c518.png)


ViewContorollerのコード
```swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var motionView: UIView! {

        didSet {
            // 水平方向
            let xMotion = UIInterpolatingMotionEffect(keyPath: "center.x", type: UIInterpolatingMotionEffect.EffectType.tiltAlongHorizontalAxis)
            // 左右の動きの幅
            xMotion.minimumRelativeValue = -200.0
            xMotion.maximumRelativeValue = 200.0
            // 垂直方向
            let yMotion = UIInterpolatingMotionEffect(keyPath: "center.y", type: UIInterpolatingMotionEffect.EffectType.tiltAlongVerticalAxis)
            // 上下の動きの幅
            yMotion.minimumRelativeValue = -200.0
            yMotion.maximumRelativeValue = 200.0
            // モーションエフェクトの指定
            motionView.motionEffects = [xMotion, yMotion]

        }
    }

}
```
これで実機でビルドすればmotionViewがデバイスの傾きによって動きます!!!

# 参考
https://developer.apple.com/documentation/uikit/uiinterpolatingmotioneffect

https://dev.classmethod.jp/articles/ios_ui_parallax/

[改訂新版]Swiftポケットリファレンス (POCKET REFERENCE)
ReactのBottomNavigationでページ遷移

# はじめに
マルチデバイス対応させたいため，スマホの時はBottomNavigationでページ遷移できたらいいなと思った．
muiのBottomNavigationを使用した．

# 今回使用したコンポーネント

```react
export default function SearchTemplate({ isPhone }) {
  const navigate = useNavigate();
  const location = useLocation();

  const urlToValue = {
    "/": 0,
    "/products": 1,
    "/news": 2,
  };
  const value = urlToValue[location.pathname] || 0;

  const handleNavigation = (url) => {
    navigate(url);
  };

  if (isPhone) {
    return (
      <Paper
        sx={{ position: "fixed", bottom: 0, left: 0, right: 0 }}
        elevation={3}
      >
        <BottomNavigation showLabels value={value}>
          <BottomNavigationAction
            label="Bookmark"
            icon={<BookmarkIcon />}
            onClick={() => handleNavigation("/home")}
          />
          <BottomNavigationAction
            label="cook"
            icon={<ShoppingCartIcon />}
            onClick={() => handleNavigation("/products")}
          />
          <BottomNavigationAction
            label="News"
            icon={<NewspaperIcon />}
            onClick={() => handleNavigation("/news")}
          />
        </BottomNavigation>
      </Paper>
    );
  }
  return null;
}
```

# 解説


まず，JSXから解説．BottomNavigationタグで何番目のアクションが行われているかvalueで判定する．showLabelsでlabel表示．
BottomNavigationActionタグは，クリック時にhandleNavigationを呼び出してページ遷移．

```react
        <BottomNavigation showLabels value={value}>
          <BottomNavigationAction
            label="Bookmark"
            icon={<BookmarkIcon />}
            onClick={() => handleNavigation("/home")}
          />
          <BottomNavigationAction
            label="cook"
            icon={<ShoppingCartIcon />}
            onClick={() => handleNavigation("/products")}
          />
          <BottomNavigationAction
            label="News"
            icon={<NewspaperIcon />}
            onClick={() => handleNavigation("/news")}
          />
        </BottomNavigation>
```

## ページ遷移 
navigateにurlを入れるとページ遷移する．

useNavigateはあまり推奨されていないため，loadersやactionsを使うべきと[ドキュメント](https://reactrouter.com/en/main/hooks/use-navigate "useNavigate")に書いてたので．直す予定．2023/06/18の段階では動いた．

```react
  const navigate = useNavigate();
  const handleNavigation = (url) => {
    navigate(url);
  };
```

## 現在のページ番号取得
useLocationで現在のドメインなどを取得できる．それと一致するページの番号をvalueに格納することで切り替えることができた．

これをしなければ，useNavigateでページ遷移したときに再レンダリングされるため，useStateで初期化された値が一度入ってしまう．そのため，2回押さなければ反映されなかった．．．😢

```react
  const location = useLocation();
  const urlToValue = {
    "/home": 0,
    "/products": 1,
    "/news": 2,
  };
  const value = urlToValue[location.pathname] || 0;
```

# 参考
https://mui.com/material-ui/react-bottom-navigation/

https://reactrouter.com/en/main/hooks/use-navigate
docker コマンドメモ

# 概要
dockerの基礎的なコマンドをまとめます


# コマンド
・コンテナの作成
```
docker run -it コンテナ名  /bin/bash
```


・コンテナの起動
```
docker start コンテナ名
```


・コンテナに入る
```
docker exec -it コンテナ名 /bin/bash
```


・接続状態から抜ける
```
exit
```


・Rootユーザー以外でコンテナに入る
```
docker exec -it --user=ユーザ名 コンテナ名 /bin/bash
```


・特権モードでコンテナを作成
```
docker run -it --privileged コンテナ名 os名 /sbin/init
```


・パッケージのインストール
```
yum install パッケージ名
```


・使用しているosの確認
```
cat /etc/*-release
```
アプリ開発の基礎 「MVC」を知ろう

フレームワークをつかったアプリ開発において、MVCの知識はとっても大事です😇

ただ、わたしのような初心者には、とっつきにくいです。
そこで簡単にまとめました。


# MVCって何？

MVCは、アプリケーションの設定を整理するための「概念」です。
Webフレームワークで一般的に取り入れられています。

Model（モデル）-View（ビュー）-Controller（コントローラー）の略語。
「M＝モデル」、「V=ビュー」、「C＝コントローラー」、それぞれの頭文字をとり「MVC」と呼びます。

## MVCを使うメリットは？

- 変更時の作業が軽くなります。
- コードを再利用しやすいです。
- 機能を分割できます。
- 各エンジニアが役割分担しながら開発、保守を行いやすくなるため、効率化が図れます。

## MVCの役割は？

- データベースと連携しデータ処理を行う「Model」
- ユーザーが入出力を行う画面を生成する「View」
- ViewとModelを制御する「Controller」

### Model（モデル）：
システムの本体部分にあたります。アプリケーションのデータや処理を行います。システムの中でビジネスロジックを担当。

### View（ビュー）
画面を表示したり、入力する機能の処理を行います。例えば、Webブラウザに表示するHTMLを動的に生成します。

### Controller（コントローラ）
ユーザーの入力を受け取り、モデルとビューを制御します。Viewからの入力に応じて、必要なロジックの実行をModelに依頼し、その結果表示をViewに依頼します。


## MVCを使ったアプリケーション開発手順

MVCの前に、「ルーティング」という概念を理解する必要があります。
ルーティングは、URLとコントローラのアクションを紐付ける仕組みです。

ブラウザからサーバーへ送られてきたURLをもとに、サーバー側でどの部分のどの処理を実施すればいいか判断する機能です。


1. Modelの作成：アプリケーションのデータを定義するModelを作成。データベースへのアクセスやデータの操作を担当します。


2. Viewの作成：表示する画面のUIを設計し、Viewを作成。ユーザーが操作するUI部分を担当します。


3. Controllerの作成：ViewとModelを制御するControllerを作成。Controllerは、ユーザーからの入力を受け取り、ModelとViewを制御します。


## 参考サイト

- [@ IT MVCモデルとは何ですか・・・](https://atmarkit.itmedia.co.jp/fjava/javafaq/j2ee/j2e07.html)
- [MVCモデルとは！概念やそのメリットをわかりやすく解説！](https://www.geekly.co.jp/column/cat-technology/1911_040/)
- [MVCフレームワークとは？【WEBサービス開発の基礎】](https://read-engineer.com/2020/07/29/mvc/)
- [Ruby on Rails チュートリアル　1.3.3Model-View-Controller（MVC）](https://railstutorial.jp/chapters/beginning?version=7.0#sec-mvc)


実際に開発してみないと、MVCの便利さは実感しにくいと思います😇

まだまだ勉強中の身なので、修正などありましたら何卒ご指摘ください。
Raspberry Pi 3A+でFR24を使う

いつぞや手に入れて、しばらく使っていなかったRaspberry Pi 3 A+があります。
しかし何も使っていないのももったいないと思っていました。

そこで、私がたまに使っているFlightRadar24。
航空機の出すADS-Bの信号を自分の受信機で受信し、FlighRadar24のサーバに送信すればビジネスアカウントが得られると知ったので、これをやろうと思いました。

# 準備

用意したものは以下。

- Raspberry Pi 3A+
- MicroUSBケーブル
- 充電器（2.5A以上）
- MicroSDカード
- ADS-B受信機
  - 専用の機材もあるようですが、私は楽天で「RTL2832U」で出てきたTV/ラジオチューナーを使いました。

Raspberry Pi 3A+とADS-B受信機以外は全てDAISOで用意しました。
USBのADS-B受信機を1つだけあるUSBコネクタに刺して、アンテナを窓際に置いて準備完了。

# 手順

手順の最後の方で、ログを失ってしまいましたがご了承ください。

1. RaspberryPiのインストール
    詳細割愛。Raspberry Pi OSを[Raspberry Pi Imager](https://www.raspberrypi.com/software/)を使ってさくっとインストールしました。
    Raspberry pi 3A+は有線LANがないので、WiFiの設定は必須です。
1. IPアドレスの固定
    必ずしも必要ではありませんが、私は固定しました。
    ```bash
    $ sudo nano /etc/dhcpcd.conf
    ```
    以下は私の設定です。ここは環境によりけりです。
    ```text
    interface wlan0
    static ip_address=192.168.1.xxx/24
    static routers=192.168.1.1
    static domain_name_servers=192.168.1.1
    ```
    これができたら一度再起動させて、改めてsshでつなぎ直します。
1. FR24のアカウント作成（詳細割愛）
1. FR24のインストール
    公式の出しているスクリプトを使います。
    ```bash
    $ sudo bash -c "$(wget -O - https://repo-feed.flightradar24.com/install_fr24_rpi.sh)"
    ```
    以下は設定。
    ```text
    Step 1.1 - Enter your email address (username@domain.tld)
    # 自分のEメールアドレスを入力。
    Step 1.2 - If you used to feed FR24 with ADS-B data before, enter your sharing key.
    If you don't remember your sharing key, you can find it in your account on the website under "My data sharing".
    https://www.flightradar24.com/account/data-sharing
    # すでにデータを送っていた人は共有キーを入れてくれ、らしい。特に前に持っていたものはないので空欄でEnter。
    Step 1.3 - Would you like to participate in MLAT calculations? (yes/no)$:
    # 特に深く考えずyesした
    Step 3.A - Enter antenna's latitude (DD.DDDD)
    # 緯度。スマホのアプリで取得した。
    Step 3.B - Enter antenna's longitude (DDD.DDDD)
    # 経度。同上。
    Step 3.C - Enter antenna's altitude above the sea level (in feet)
    # 高度をフィートで。
    # このあと、場所が日本とか、緯度・経度・高度の確認が出る。
    Would you like to continue using these settings?
    Enter your choice (yes/no)$:yes
    # これらの設定でいいかの確認。
    Step 4.1 - Receiver selection (in order to run MLAT please use DVB-T stick with dump1090 utility bundled with fr24feed):
     1 - DVBT Stick (USB)
     -----------------------------------------------------
     2 - SBS1/SBS1er (USB/Network)
     3 - SBS3 (USB/Network)
     4 - ModeS Beast (USB/Network)
     5 - AVR Compatible (DVBT over network, etc)
     6 - microADSB (USB/Network)
    Enter your receiver type (1-7)$:1
    # レシーバーのタイプ。1を選択。

    （このあとログをうっかり消してしまって残っていませんが、ほぼyesや、Enterで大丈夫でした）
    ```

# 動作確認

```bash
$ sudo service fr24feed status
```

問題なく起動していることを確認。

PCからWEBブラウザを開き、`http://(RaspberryPiのIPアドレス):8754/`へアクセス。
![webpage.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/915263/834943da-4980-24f7-b42d-60d44935c712.png)
こんな感じに表示されているのを確認。

続いて`http://(RaspberryPiのIPアドレス)/dump1090/gmap.html`へアクセス。
こんな感じに捉えている飛行機の情報が見られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/915263/5c88a9a8-67fe-19a7-5244-d91f009bf232.png)

小さなアンテナだけど、南向きの窓際に置いたら、埼玉から銚子沖や大島くらいまでの範囲で電波を拾っている模様。
これハマるとアンテナの置き場所を考えたり、アンテナを変えてみたり、し始めるんだろうなあと。
【MySQL接続エラー】「SQLSTATE[HY000] [2002] Connection refused」でマイグレーションできない

## はじめに

Laravelをはじめて触った際、参考サイトを見ながら環境構築を進めて設定を変更。

いざマイグレーション！…できませんでした。

検索して同じエラーの方の解決を試してもダメ。

## 原因

**変更すべきファイルが違いました。**

エラー解消のために触っていたファイル：env.example

正しくは：.env

「DB_PASSWORD」で検索して変更ファイルを探していたので、.gitignoreに含めていた.envは表示されず…

.env.exampleが検索にヒットし、そこで `DB_PASSWORD` と `DB_HOST` を変更していたのでmigrationで参照される設定ファイルの.envは間違った状態のままでエラーが起きていた。ということです。

## おわりに

chatGPTに解決策を聞くべくファイル名を確認した際に「example????」となって気付きました笑。
Slack emoji を export する


## はじめに

Slack の Workspace は気軽に作成できる反面、個人用、家族用、....等様々存在する誤家庭も少なくないと思います。
それぞれの設定はもちろん大変ですが、今回挙げる emoji が特に面倒です。
Import は Google Chrome の拡張に任せればすぐ終わりますが、 export はそうも行きません。
また過去書かれた記事は、仕様変更等で動かなくなってしまったものも多く、記事にまとめることにしました。

## Slack emoji を export する

今回実施する内容は以下です

- 準備
  - Slack app 作成
  - Node.js の実行環境用意
- 実行

### 準備

#### Slack app を作成する

今回 emoji 情報の取得のみ行うことができる Slack app を作成します。
必要は権限は `emoji:read` のみです。
面倒な方は ⇩ JSON をコピーして Slack app を作成してください

```json
{
  "display_information": {
    "name": "Emoji export"
  },
  "features": {
    "bot_user": {
      "display_name": "Emoji export",
      "always_online": false
    }
  },
  "oauth_config": {
    "scopes": {
      "bot": ["emoji:read"]
    }
  },
  "settings": {
    "org_deploy_enabled": false,
    "socket_mode_enabled": false,
    "token_rotation_enabled": false
  }
}
```

後のステップで `API_TOKEN` を利用します。
Token を発行し安全な場所に控えておいてください。

#### Node.js の実行環境用意

今回は Node.js で export する定義を書きました。
実行環境に Node.js を install している、 yarn の実行も可能である前提で記載します。設定が終わってない方は設定してください。

```sh
cd path/to/emoji-export && cd path/to/emoji-expor
npm init -y
```

今回依存する npm package を install します。

- [image-downloader - npm](https://www.npmjs.com/package/image-downloader)
- [dotenv - npm](https://www.npmjs.com/package/dotenv)
- [@slack/web-api - npm](https://www.npmjs.com/package/@slack/web-api)

```sh
yarn add image-downloader dotenv @slack/web-api
```

`.evn` ファイルに先ほど作成した Slack app の API_TOKEN を記載します

```sh:.env
API_TOKEN=xoxb-xxxx-xxxx-xxxx
```

`index.js` に Slack emoji を export するコード記載します。

今回は `path/to/emoji-expor/images` 以下に emoji ファイルを書き出します。また、 alias として登録されている emoji は無視しています。

```js:index.js
"use strict"

require("dotenv").config()

const { WebClient } = require("@slack/web-api")
const download = require("image-downloader")

const apiToken = process.env.API_TOKEN
const web = new WebClient(apiToken)

web.emoji.list().then((res) => {
  // Response sample
  //
  // {
  //  ok: true,
  //  emoji: {
  //    sample: 'https://emoji.slack-edge.com/xxx/sample/f3ec6f2bb0.png',
  //    'サンプル': 'alias:sample',
  //    sample2: 'https://emoji.slack-edge.com/xxx/sample 2/6535867372d3b644.png'
  //  },
  //  cache_ts: '1668865194.021600',
  //  response_metadata: { scopes: [ 'emoji:read' ], acceptedScopes: [ 'emoji:read' ] }
  //}
  Object.keys(res.emoji).forEach((key) => {
    const fileUrl = res.emoji[key]
    if (fileUrl.startsWith("alias:")) {
      return
    }

    const fileName = key + fileUrl.match(/\.(\w+)$/)[0]
    download.image({
      url: fileUrl,
      dest: `${process.cwd()}/images/${fileName}`,
      extractFilename: false,
    })
  })
})
```

### 実行

それでは作成した Script を実行します。

```sh
yarn node index.js
```

images directory 以下に emoji が保存されていれば成功です。

## References

- [image-downloader - npm](https://www.npmjs.com/package/image-downloader)
- [dotenv - npm](https://www.npmjs.com/package/dotenv)
- [@slack/web-api - npm](https://www.npmjs.com/package/@slack/web-api)
- [emoji.list method | Slack](https://api.slack.com/methods/emoji.list)
TortoiseSVNの基本操作

初心者向け（になっているかは微妙。。）

gitが使用可能な環境であれば git使った方が多分よい。
やむを得ず(?)SVNを使う環境にいる方々向けになります。

<font color=red>※まずはフォルダ構成をちゃんと決めてから運用しましょう。【超重要】</font>

<b>local workspace</b>・・・SVN Checkout先のローカルフォルダ[^3]。Checkout済みの作業スペース。
<b>Repository</b>・・・通常Server内に置かれる[^4]。バージョン管理されているフォルダ＆ファイルの保管庫[^5]。

[^3]: ファイルサーバ内のフォルダでも可。運用的によいかどうかは不明。

[^4]: file:///～を指定することでローカルフォルダやネットワークドライブ内のフォルダも指定可能。ただし、アクセス過多になってディスク破損のリスクが高まる。1ファイルでも壊れると全滅するリスクがあるため、RAID組んでないと極めて危険。（そもそも分散管理されているgitのほうが災害リスクに対しては安全か）

[^5]: 実体は複雑に管理されたファイル群。


バージョン管理システムの目的と過去ファイルの取り出しとかのイメージの説明を入れたい。

# SVN右クリックメニュー基本操作

Repository Browserではなく、WindowsのExplorerの右クリック操作を想定

## SVN Checkout

端的にいうとdownload操作です。

Repositoryにあるフォルダのデータを空のlocal workspaceにコピーします。
local workspaceのある disk drive の空き容量が十分にある状態で行ってください。[^6]
この操作を行うと、SVNツールが管理用の隠しフォルダ.svnフォルダを生成します。
（Repositoryを共有していない他者にCheckoutしたファイルを展開する際は .svnフォルダは除外して展開したほうが無難です。相手がTortoiseSVNを使用している場合、管理フォルダが不整合を起す可能性あり。）

[^6]: pathを細かく指定して小さなファイルを取り出す場合は気にしなくてよいですが、一式Checkoutする場合、数GB以上となる場合がある。（格納されているデータによる。）

### SVN Checkout - 特定の revision のファイルを取り出したい場合

SVN Checkoutの操作画面でRevisionを指定してCheckoutする。

## Update

local workspace内の選択したフォルダorファイルを最新の状態に更新します。
local workspace内のファイルが更新されている場合、コンフリクト(編集の競合)する場合があります。

基本的にはファイルの編集作業を行う前にUpdate操作を実施しておき、local workspaceを最新に保ちましょう。

## Commit

端的にいうとupload操作です。

local workspaceのファイルをRepositoryに上書きします。
（履歴から更新前のファイルを取得可能であるため、「上書き」というよりは更新版を置くようなイメージ）
<font color=red>RepositoryへのWrite accessのため、慎重に実施してください。
一度Commitした場合、操作を取り消すことはできないため注意。（Repositoryに履歴が残る。）</font>[^1]

[^1]:Repository側を操作することで復旧可能な場合もあるが、深い知識を要し、作業ミスでデータを破損させるリスクがある。

## Add

Repositoryにまだ存在していないファイルをCommitするときに使用します。
local workspace上で、追加したいフォルダやファイルに対してAdd操作をします。
追加したいファイルの上位フォルダがまだAddされていない場合はAdd操作できないので、上位フォルダを選んでAdd操作してください。
Add操作そのものはlocal workspaceへの操作であり、CommitするまではRepositoryに影響は与えません。

## Undo Add

Commit前のlocal workspaceでのAdd操作の取り消し。
Commit後は取り消し不可。
（Commit後に登録ファイルを削除したい場合はDelete操作を使用する。ただしCommit履歴は残る。）

## Revert	

編集作業をロールバックします。
（local workspaceで更新しているフォルダorファイルを、Repositoryのデータで上書きします。）
この操作は元に戻せないため、必要に応じて事前にファイルをバックアップすることを推奨します。

## Clean up


コミットに失敗した場合のおまじない。

コンフリクト（編集の競合）やサーバーエラーでなければ大抵これで解消する・・・はず。。
（SVNの管理システムの知識が不足しておりメカニズムが理解できていない）

# 少し高度な操作

以下の操作は、練習用のRepositoryで実施して理解し、慣れてから操作することを推奨します。

## Delete

Repository上のファイルやフォルダを削除するときに使います。

この操作を実施すると、まず local workspace のファイル（またはフォルダ）が削除されます。[^7]
次いで、（操作を行ったファイル（またはフォルダ）の上位フォルダに対して）Commit操作を行うとRepositoryからもファイルが削除されます。

なお、local workspace上でExplorer等でただ単にファイルを削除した場合、SVNシステムとしては意図しないmissing（行方不明）ファイルとして扱われます。
（こうなってしまった場合、上位フォルダに対してRevert操作を行うことで削除ファイルを復元（RepositoryからDownload）可能です。）

もし、Repositoryから削除したファイルをRepository上で開きたい場合、その上位フォルダのCommit logから削除操作した履歴を探す必要があります。[^2]

[^2]: 削除前の時点でtagを付与している場合はtagsから探せるかもしれません。Repositoryの運用次第です。

[^7]: Windowsファイルシステム上でどういう扱いになっているのかは調べてないので分かりません。。

## Rename

ファイルやフォルダの名称変更や移動を行います。
SVNシステムの内部的にはAddとDeleteの組み合わせとして扱われます。
そのため、操作を行ったファイルそのものに対してのCommit操作は失敗します。（AddとDeleteが対にならず不整合となるため。）
上位フォルダに対してCommit操作を行ってください。
	
# 高度な操作

## Relocate

Repositoryのサーバが移動したときなどに使う。
local workspace のSVN管理上の最上位フォルダ(.svn隠しフォルダを子フォルダにもつフォルダ)に対して操作する。
【Tips】Typescript＋Jestでconfigモジュールをモック化する

# はじめに
Typescript＋Jestのモックはめちゃくちゃ便利な反面、色々なモック化のパターンがあって分かりにくい(自分がちゃんとTypescriptを理解しきれていないからなのだろうけど)。
今回、configモジュールをモック化する際に苦戦したので、方法をまとめておく。

# テストコード
以下のようにすれば問題なく動作するが、config.has()は動作だけしてくれればよいのに実装しなければいけないのがイケていない。公式ドキュメントの「[部分的なモック](https://jestjs.io/ja/docs/mock-functions#%E9%83%A8%E5%88%86%E7%9A%84%E3%81%AA%E3%83%A2%E3%83%83%E3%82%AF)」の通りにやっても上手くいかない(jest.resetAllMocks()が悪さをしているような気がするが、clearAllMocks()にするとそれはそれで他の場所で不都合があった)。何か良い方法はないものか。

```typescript
import { describe, test, jest, afterEach } from '@jest/globals';
import config from 'config';

jest.mock('config');
const configMock = config as jest.Mocked<typeof config>; // configのモック化

describe('Config Test Suite', () => {
  afterEach(() => {
    jest.resetAllMocks();
  });

  test('Test Pattern 1', () => {
    // モック対象外は元の情報を使いたいので、requireActual()で元の情報をロードしておく
    const originalConfig: any = jest.requireActual('config');
    // モック化したconfigでget()の実装
    configMock.get.mockImplementation((key: string) => {
      // 特定のキーが指定された場合のみ上書きしてreturn
      if (key === 'testKey') return 'testValue';
      return originalConfig.get(key);
    });
    // プロダクトコードでhas()を使っている場合はhas()も実装しておく
    configMock.has.mockImplementation((key: string) => {
      return originalConfig.has(key);
    });

    // 以下、assertionを書いていく
  });
});
```
ConstraintLayoutのBarrierとは？

ConstraintLayoutのBarrierを知らず最近発見したので今更ですが記事にしてみようと思います。


# 公式リファレンス
https://developer.android.com/reference/androidx/constraintlayout/widget/Barrier





```.xml
<androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/constraintlayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">

    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/vertical_barrier"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:barrierDirection="start"
        app:barrierMargin="28dp"
        app:constraint_referenced_ids="text_view" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

今回は対象とするViewなどは省略してます。

`constraint_referenced_ids`に対象とするViewのidを設定します。

`barrierDirection`に方向を指定します今回はstartなので`constraint_referenced_ids`に設定したViewの左側から`barrierMargin="28dp"`の位置に補助線を引いています。

Barrierは補助線だと思います。
Viewに表示はされないけど、ConstraintLayoutの制約をViewの少しずらした箇所に設定したい時などに使用すると良さそうです。
【未完】Javaエンジニアがcosmosdb-chatgptを読むのに苦闘している その2（ビルドツール編）

# 今回読んだ部分

今回は、ビルド、デプロイに関わる部分の大枠を読みました。
そもそもMicrosoftのシステム開発のエコシステムを判っていないので、当たり前だろ、という話を書いているかも。
そして誤解もあるかも知れない部分かも。

# 今回読んだコード

|#|サンプルのファイル|Javaで相当するもの|
|----|----|----|
|プロジェクト定義|cosmoschatgpt.sln|web.xml？？？全然違う気もする|
|ビルド定義|cosmoschatgpt.csproj|pom|
|エントリポイント|Program.cs|public static void main(String[] args)|
|IaC|azuredeploy.bicep<br>azuredeploy.json|AWS CloudFormation|
|プロパティファイル|appsettings.json|*.properties|
|ルータ|App.razor|faces-config.xml|

## cosmoschatgpt.sln

コードじゃないですが、プロジェクト定義です。
ダブルクリックするとVisual Studioが立ち上がりますが、小さなテキストファイルなのでテキストエディタでも読めます。
ターゲットのCPU、ビルドのパターン（Debug、Release）が定義されています。
それと、cosmoschatgpt.csprojへのアンカーがあります。

## cosmoschatgpt.csproj

要するにpomです。
MavenやGradleに相当するものがMSBuildというらしいのですが、このMSBuildが読むファイルがcsprojのようです。

## Program.cs

要するにエントリポイントです。
Blazorを起動しているのも、前回読んだServiceをDIしているのも、このファイルのようです。

## azuredeploy.bicep

IaCのコードです。BicepというDSLを記述します。
azuredeploy.jsonは、Bicepをコンパイルした結果です。

## appsettings.json

プロジェクトの設定値を書くようです。
gitignoreされているので、端末ごとに設定するべきもののようです。
前回十分理解できなかったOptions/*.csの値になるように見えます。
実際に行っている箇所はよくわからない……と思ったら、Program.csのRegisterConfigurationで読み込んでいるみたい。

## App.razor

プレゼンテーション層のフレームワークである（たぶん）BlazorのRouterみたいです。

# 感想

Microsoftのエコシステムについて、多少理解が深まった気がします。
appsettings.jsonでエンドポイントとキーを設定すれば、動くのか？
それともそれは、bicepで自動でなんとかしちゃうのか……？
FirefoxのYouTubeコメントのスクロールバーを消す

# やりたいこと
FirefoxでYouTubeを見ていると、短いコメントでもスクロールバーと「続きを読む」が表示されて気になる。
→Firefoxの拡張機能で、任意のJavascriptを実行できるTampermonkeyを使えば、削除できるかも

ちなみに私はJavascriptを書いたことがないので、賢いChatGPTのちからを借りてコーディングする。

# 環境（追記：2023/06/19）
この記事を書いた段階では、Windows10の入ったSurface Pro 4で、Firefox 114.0.1を使っている。

# 方法（追記：2023/06/20）
記事を公開してからもう一度考えてみると、単にスクロールバーを消せばいいのでは？と思ったので試してみるとうまく行ったので追記。
スクロールバーを消す方法は、スクロールを引き起こす要素にCSSで`overflow: hidden`を指定する。こうすると、はみ出た要素を隠したままスクロールバーを消せる。
開発者ツールで調べると、コメントの`ytd-expander`タグがスクロールバーを作り出す原因のようなので、これにスタイルを適用する。スクリプトは以下の通り。

```Javascript
// ==UserScript==
// @name         Delete Comment's Scrollbar on YouTube
// @namespace    http://tampermonkey.net/
// @version      0.4
// @description  Delete Comment's Scrollbar on YouTube
// @author       
// @match        https://www.youtube.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=rightcode.co.jp
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    let scrollCount = 0;
    window.addEventListener("scroll", function(){
        scrollCount++;
        if(scrollCount % 4 == 0){
            var elements = document.getElementsByTagName('ytd-expander');
            console.log(elements);
            for(var i=0; i < elements.length; i++) {
                elements[i].style.overflow = "hidden";
            }
        }
    }, false);
})();
```
これを実行したところ、きれいにスクロールバーが消えた。
また、スクロールバーが表示されていたときはコメントの上でマウスホイールを動かしてもスクロールが効かなかったのだが、スクリプト実行後はコメントの上でスクロールができるようになった。「続きを読む」の表示が残っているが気にならないだろう。

このスクリプトの方が確実かつ簡単に消せるのだが、前の方法も試行錯誤の跡として残しておく。


# 方法（旧）
開発者ツールでスクロールバーを表示している要素を探したところ、「続きを読む」を表示している`tp-yt-paper-button`タグがスクロールバーを表示しているようだ。
[Tampermonkey](https://addons.mozilla.org/ja/firefox/addon/tampermonkey/)でこれらの要素をすべて削除する。

### 削除できてない
しかし、スクリプトを実行しても、要素が削除できていない。色々調べてみると、どうもページを読み込んだときにしかスクリプトを実行していないことが原因だった（YouTubeはスクロールしないとコメントをロードしない）。
→スクロールするごとにスクリプトを実行する
ただし、毎回実行する必要はないので、4回に1回だけ実行する。

### コメントが見れない
しかし、「続きを読む」を削除すると、普通に長いコメントの続きが見れなくなる。
→削除する前にコメントを展開する
そのために、`ytd-expander`タグの`collapsed`属性を削除する。

# コード（旧）
最終的なTampermonkeyのコードはこのようになった。
```Javascript
// ==UserScript==
// @name         Delete Comment's Scrollbar on YouTube
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  Delete Comment's Scrollbar on YouTube
// @author       You
// @match        https://www.youtube.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=rightcode.co.jp
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    let scrollCount = 0;
    window.addEventListener("scroll", function(){
        scrollCount++;
        var expanders = document.getElementsByTagName('ytd-expander');
        for( var i=0; i < expanders.length; i++){
            expanders[i].removeAttribute('collapsed');
        }

        if(scrollCount % 4 == 0){
            var elements = document.getElementsByTagName('tp-yt-paper-button');
            console.log(elements);
            while (elements.length > 0) {
                elements[0].parentNode.removeChild(elements[0]);
            }
        }
    }, false);
})();
```

# 結果
Tampermonkeyを使って、Firefoxで見るYouTubeの煩わしいスクロールバーを削除した。まったく知らない言語でもチャットボット様のおちからを借りればコーディングできた。
【SwiftUI】傾きに応じて模様が変わる万華鏡を作る方法

# はじめに
iOS17からSwiftUIでMetal Shaderが使えるようになり、より複雑な表現が可能になりました。
この記事では、用意されたShaderを使って簡単にその魅力を体験できるライブラリ「MetalUI」を紹介します。

# 作るもの
本物の万華鏡のように傾きに応じて模様が変わる万華鏡を作ります。

# デモ
https://twitter.com/shota_appdev/status/1668591590879920128?s=61&t=MKFuCiJ6t_sepWZx2LLjXQ

# 準備
MetalUIをSwift Package Managerでインストールします。

https://github.com/heestand-xyz/MetalU

# コード全体
```swift
import SwiftUI
import MetalUI // MetalUIをインポート
import CoreMotion // センサーを使うためにインポート

// 万華鏡で使う図形を定義
struct Kaleidoscope: Shape {
    let sides: Int
    let angle: Angle
    
    func path(in rect: CGRect) -> Path {
        let center = CGPoint(x: rect.width / 2, y: rect.height / 2)
        let radius = min(rect.width, rect.height) / 2
        var path = Path()
        
        for i in 0..<sides * 2 {
            let sinAngle = sin(angle.radians * Double(i))
            let cosAngle = cos(angle.radians * Double(i))
            let rotation = CGAffineTransform(rotationAngle: CGFloat(angle.radians * Double(i)))
            let position = CGPoint(x: center.x + sinAngle * radius, y: center.y + cosAngle * radius)
            
            if i == 0 {
                path.move(to: position)
            } else {
                path.addLine(to: position)
            }
            
            path.addLine(to: position.applying(rotation))
        }
        
        return path
    }
}


// 図形を組み合わせる
struct KaleidoscopeView: View {
    private let colors: [Color] = [.green, .green, .blue, .pink, .orange, .purple, .yellow, .red]
    
    var body: some View {
        ZStack {
            ForEach(0..<8) { i in
                Kaleidoscope(sides: 6, angle: Angle.degrees(Double(i) * 45))
                    .fill(self.colors[i % self.colors.count].opacity(0.9))
            }
        }
    }
}

struct ContentView: View {
    // 傾きを保持する変数
    @State private var x: CGFloat = 0
    
    private let motionManager = CMMotionManager()
    
    var body: some View {
        ZStack {
            Rectangle().fill(Color.black)
            KaleidoscopeView()
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .ignoresSafeArea()
        .kaleidoscope(angle: Angle(radians: x)) // MetalUIのkaleidoscopeを使う
        .onAppear() {
            // センサーの更新間隔を設定
            motionManager.accelerometerUpdateInterval = 0.1
            
            // センサーの値を取得
            motionManager.startAccelerometerUpdates(to: .main) { data, error in
                if let data = data {
                    x = data.acceleration.x
                }
            }
        }
        .onDisappear() {
            // センサーを止める
            motionManager.stopAccelerometerUpdates()
        }
    }
}
```

# おわりに
たったこれだけで万華鏡が完成しました。
これからのSwiftUIは表現の幅がさらに広がりそうです！
【SwiftUI】Maskを使って図形に画像を合成する

# はじめに
`mask`を使うことがなかったのであまり知らなかったですが、意外と便利だったので記事にしておきます。

# やること
星を宇宙色にします！！

![utyuu.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/f7acb2e9-d32b-83a9-8e4e-824a0048b144.jpeg)

# 実装
```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        Image("space")
            .mask {
                Image(systemName: "star.fill")
                    .resizable()
                    .aspectRatio(contentMode: .fit)
                    .frame(width: 300, height: 300)
            }
    }
}
```

![simulator_screenshot_82D2FC41-8A1C-4C5A-AB07-E6A1C979A267.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/b37b9a84-7dc9-6f23-6557-0deafc3d2c5b.png)

# おわり
実務で使う時にはめっちゃいい感じに使えたんですが、簡略化がむずくて変な記事になったかもしれません笑
SymPy で三角形の外心を求める（ついでに半径も）

Python の sympy で書かれていたので，Julia で書いてみました。
https://qiita.com/masa0599/items/40dd6e26d57b3a0c61fa

(x1, y1), (x2, y2), (x3, y3) を与えて，外心および外接円の半径を求める。

3 つの頂点が外接円の円周上にあるという条件式で連立方程式を解く（外接円の半径もついでに求めておく）。
Julia の SymPy では Eq() を使った連立方程式の構成は deprecated になっている。

```julia
using SymPy

@syms R::positive, x, y, x1, y1, x2, y2, x3, y3;

(x1, y1) = (3, 5)
(x2, y2) = (2, -2)
(x3, y3) = (-6, 2)

eq1 = (x1 - x)^2 + (y1 - y)^2 - R^2
eq2 = (x2 - x)^2 + (y2 - y)^2 - R^2
eq3 = (x3 - x)^2 + (y3 - y)^2 - R^2

res = solve([eq1, eq2, eq3], (R, x, y))
```

    1-element Vector{Tuple{Sym, Sym, Sym}}:
     (5, -1, 2)

(x1, y1), (x2, y2), (x3, y3) を記号のままで解を求める。
この場合には R::positive の判定ができないので， R が負の場合の解も求まるが，R の絶対値は同じで，x, y も同じ。

```julia
@syms R::positive, x, y, x1, y1, x2, y2, x3, y3;

eq1 = (x1 - x)^2 + (y1 - y)^2 - R^2
eq2 = (x2 - x)^2 + (y2 - y)^2 - R^2
eq3 = (x3 - x)^2 + (y3 - y)^2 - R^2

res = solve([eq1, eq2, eq3], (R, x, y))
```

    2-element Vector{Tuple{Sym, Sym, Sym}}:
     (-sqrt((x1^2 - 2*x1*x2 + x2^2 + y1^2 - 2*y1*y2 + y2^2)*(x1^2 - 2*x1*x3 + x3^2 + y1^2 - 2*y1*y3 + y3^2)*(x2^2 - 2*x2*x3 + x3^2 + y2^2 - 2*y2*y3 + y3^2))/(2*(x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2)), (x1^2*y2 - x1^2*y3 - x2^2*y1 + x2^2*y3 + x3^2*y1 - x3^2*y2 + y1^2*y2 - y1^2*y3 - y1*y2^2 + y1*y3^2 + y2^2*y3 - y2*y3^2)/(2*(x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2)), -(x1^2*x2 - x1^2*x3 - x1*x2^2 + x1*x3^2 - x1*y2^2 + x1*y3^2 + x2^2*x3 - x2*x3^2 + x2*y1^2 - x2*y3^2 - x3*y1^2 + x3*y2^2)/(2*(x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2)))
     (sqrt((x1^2 - 2*x1*x2 + x2^2 + y1^2 - 2*y1*y2 + y2^2)*(x1^2 - 2*x1*x3 + x3^2 + y1^2 - 2*y1*y3 + y3^2)*(x2^2 - 2*x2*x3 + x3^2 + y2^2 - 2*y2*y3 + y3^2))/(2*(x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2)), (x1^2*y2 - x1^2*y3 - x2^2*y1 + x2^2*y3 + x3^2*y1 - x3^2*y2 + y1^2*y2 - y1^2*y3 - y1*y2^2 + y1*y3^2 + y2^2*y3 - y2*y3^2)/(2*(x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2)), -(x1^2*x2 - x1^2*x3 - x1*x2^2 + x1*x3^2 - x1*y2^2 + x1*y3^2 + x2^2*x3 - x2*x3^2 + x2*y1^2 - x2*y3^2 - x3*y1^2 + x3*y2^2)/(2*(x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2)))

少なくとも，今の場合は最初の解が適切。

R, x, y を返す関数はこんなふう。

```julia
f(x1, y1, x2, y2, x3, y3) = (-sqrt((x1^2 - 2*x1*x2 + x2^2 + y1^2 - 2*y1*y2 + y2^2)*(x1^2 - 2*x1*x3 + x3^2 + y1^2 - 2*y1*y3 + y3^2)*(x2^2 - 2*x2*x3 + x3^2 + y2^2 - 2*y2*y3 + y3^2))/(2*(x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2)), (x1^2*y2 - x1^2*y3 - x2^2*y1 + x2^2*y3 + x3^2*y1 - x3^2*y2 + y1^2*y2 - y1^2*y3 - y1*y2^2 + y1*y3^2 + y2^2*y3 - y2*y3^2)/(2*(x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2)), -(x1^2*x2 - x1^2*x3 - x1*x2^2 + x1*x3^2 - x1*y2^2 + x1*y3^2 + x2^2*x3 - x2*x3^2 + x2*y1^2 - x2*y3^2 - x3*y1^2 + x3*y2^2)/(2*(x1*y2 - x1*y3 - x2*y1 + x2*y3 + x3*y1 - x3*y2)));
```

描画関数(3, 5, 2, -2, -6, 2) で描いてみる。

(R, x, y) = (5.0, -1.0, 2.0)
![fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611739/fd589b2b-c302-c1cf-1a39-4f57b03b4c7b.png)


描画関数(1, 2, 5, 4, 6, 3)
(R, x, y) = (2.6874192494328497, 3.6666666666666665, 1.6666666666666667)

![fig2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2611739/0339ef9f-86c9-ad35-57c6-d5fe04bc6941.png)
【備忘録】ejsとscssの開発環境の構築

# 内容
ejsとscssの開発環境を作成したときのメモ
- SSI(server side include)を確認できるローカル環境
- EJSのビルド環境
- scssビルド環境
の構築　

- リポジトリ
https://github.com/koutatta13/ejs-sass

- 使用したモジュール
    - gulp 
    - gulp-sass　//scssコンパイルのため
    - gulp-sass-grapher //scssで@important対応の差分ビルド
    - path
    - gulp-watch //差分検知
    - browser-sync //自動リロード(未実装)
    - gulp-rename　//拡張子の変換
    - gulp-ejs //EJSのビルド
    - gulp-replace //置換
    - gulp-plumber //エラー無視
    - connect-ssi //SSI対応




# コードの解説 
なし git 見てください
Flutterコマンドを毎回打つのがめんどいので、しがないアプリ作りました

## 結論
- Flutterでよく使うコマンドをボタン一つで実行してくれるデスクトップアプリを作りました！！:smiley:
### 動作はこんな感じです
![flutter_commander_demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351982/95b916b3-263f-53bf-a684-b25ad3f93721.gif)

[コードはここ](https://github.com/NaokiNishizawa/flutter_commander/tree/main/flutter_commander)

## なんで作ったか(思考)
1. ここ1年程Flutterの業務を行なっていたり、　勉強を日々している
1. Android StudioやVS codeにもコマンド実行してくれるボタンはあるが限定的
1. 毎回Terminalに決まったコマンドを打ちたくないなぁ。。。
1. そうだ。勉強も兼ねてMac OSのデスクトップアプリで解決しよう！！

## 問題点(半日程度で作成したため、腐る程沢山ある:scream:)
1. **UIがクソダサすぎる:joy:**
1. 毎回起動時に実行したいプロジェクトの情報を入力しないといけない(メンド。。。)
1. コマンドが決め打ちなため、汎用性がない:fearful:
1. テストコード一切なし(:innocent:)
etc...

## 今後について
- どんどん機能はアップデートしていこうと思っています。
- もしよかったら、使ってみてフィードバックいただけると泣いて喜びます！:raised_hands:
deviseの導入

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/0727aaae-45fe-a066-1e28-b7e0593ba82a.png)
ログイン機能を実装するために、deviseというGemを使用します。

１：deviseデバイス
ユーザー管理機能を簡単に実装するためのGemです。実際に運用されている多くのRailsアプリケーションサービスで使用されています。

２：導入方法
Gemfileの最後の行に　gem 'devise'と記述します。
 コマンドを実行してGemをインストールしましょう
ターミナル
# 現在のディレクトリが~/projects/pictweetであることを確認
% pwd

# Gemをインストール
% bundle installを実行する。
DockerでLEMP環境を作成した際にLaravel Viteがエラーになった件

## Laravel Viteを起動するとCSSが適用されていなかった
Laravel10ではLaravelViteがデフォルトで入っており、Tailwind CSSを使用する場合、LaravelViteが起動されていないとCSSがローカルで適用されません。ですが、今回はviteを起動しても一部のCSSが適用されない問題が発生しました。  
localhostを検証(F12)で確認すると以下のエラーが発生していました。  
![スクリーンショット 2023-06-17 9.56.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1109509/ebe3d13f-abf4-808c-9fb4-0b754e172f8f.png)

```
GET http://127.0.0.1:5173/@vite/client net::ERR_EMPTY_RESPONSE
GET http://127.0.0.1:5173/resources/js/app.js net::ERR_EMPTY_RESPONSE
GET http://127.0.0.1:5173/resources/css/app.css net::ERR_EMPTY_RESPONSE
```

上記が発生していた原因としては以下でした。
- Dockerの設定でポート5173(Viteで使うポート)がDocker外に開かれていないこと
- ViteはLocal:は見ているがNetwork:は見ていない

## 改善する手順
### appサーバのポート5173の設定を追加する
以下、portsの部分を追記
```compose.yaml
  app:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
    volumes:
      - ./src/:/app
    ports:
      - 5173:5173
```
以下、docker/php/DockerfileにEXPOSEの設定を追加
```Dockerfile
FROM php:8.1.18-fpm
EXPOSE 5173
```
上記設定後にDockerコンテナを再起動する。

### --hostを追加する。
Viteの起動オプションを修正する。(server:を追記)
```vite.config.js
    plugins: [
        laravel({
            input: [
                'resources/css/app.css',
                'resources/js/app.js',
            ],
            refresh: true,
        }),
    ],
    server: {
        host: true,
        hmr: {
            host: 'localhost',
        },
    },
```

上記でコンソールのエラーが消えているはずです。

## 最後に
DockerでLEMP環境を作成して学習していた際に、エラーに遭遇したため、解消方法を記載しました。  
環境の構成は異なると思いますが、1からDockerfileを作成している場合は、Vite用のポートの設定もする必要があるという内容でした。
初めての記事なので、読みにくいところもあると思いますが、よろしくお願いします。


## 参考文献
https://qiita.com/hitotch/items/869070c3a9f474a358ea
『kaggleで勝つデータ分析の技術』を学ぶためのdocker環境の作り方・入り方

（2023年6月25日：追記）内容に一部誤りがあったので修正。

# 状況

データ分析技術を身に付けるため、以下の書籍『kaggleで勝つデータ分析の技術』を学ぼうとしている。xgboost等のライブラリをインストールする必要があったのだが、pipで自分のパソコンに直接インストールするのは何らかの副作用があると考え、pythonの仮想環境を導入してその中で実施することにした。

https://www.amazon.co.jp/gp/product/4297108437/ref=ppx_yo_dt_b_asin_title_o01_s00?ie=UTF8&psc=1

pythonの仮想環境はvenvで簡単に行うことができるが、dockerの扱いに慣れるためdockerで仮想環境を作成することにした。

# 手法

初めにdockerfileとrequirements.txtを作成し、以下の通りに記述する。そして下記のコマンドを入力することで仮想環境でpythonのコードを実行できる。

## dockerfile

```dockerfile
# ベースイメージを指定
FROM python:3.9

# 作業ディレクトリを設定
WORKDIR /app

# 必要なライブラリをインストール
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

# ソースコードをコピー
COPY . .

# コンテナ実行時のデフォルトのコマンドを指定
CMD ["bash"]
```

## requirements.txt

```
numpy==1.21.0
pandas==1.3.0
scikit-learn==0.24.2
matplotlib==3.4.2
seaborn==0.11.1
```

## コマンド

以下のコマンドを入力することで、コンテナ内に作成されたpythonの仮想環境に入ることができる。もし足りないライブラリが存在したら、`pip install 欲しいライブラリ`でインストールする。

```bash
# コンテナ立ち上げ
sudo docker build -t my-python .

# pythonコンテナをbashで起動
sudo docker run -it -v /mnt/c/Users/自分の名前/Documents/python/kaggle/kagglebook-master/kagglebook-master:/app my-python
```
SPA対応させたToDoサイトを作成してみた。

# はじめに

naritomoと申します。

サーバーレスサイト開発周りを調査したところ、SPAに対応させたフロント/バックエンドを分離したサイト開発に興味を持ち、第一号開発としてToDoサイトを作ってみました。

いろいろアドバイスいただけると嬉しいです。

## 立ち上げ概要

ToDoサイトの構成として以下のものとしています。

* フロントエンドはReactを使用する。
* バックエンドはFastAPIを使用する。
* DBはMySQLを使用する。
* フロンド/バック間通信はRESTAPIを使用する。

必要なソースは以下にまとめてあります。

https://github.com/naritomo08/todosite

## 参考書籍/サイト

[動かして学ぶ！Python FastAPI開発入門](https://www.shoeisha.co.jp/book/detail/9784798177229)

[ReactでToDoリストを作る。](https://trap.jp/post/1497/)

本書籍/サイトにより、かなり理解が進みました。
ありがとうございます。

## 構築方法

予め展開先でgit/docker-composeを使用できていること。

### 展開方法

```bash
git clone https://github.com/naritomo08/todosite.git
cd todosite
touch front/.env.development.local
vi front/.env.development.local

以下の内容で作成する。
REACT_APP_BURL = "http://localhost:8000"
→外のサーバーで動かす場合、localhostを任意のIPにする。

docker-compose build
docker-compose run --entrypoint "poetry install --no-root" fastapi-app
docker-compose up -d

５分程待ってから実施

docker-compose exec fastapi-app poetry run python -m api.migrate_db
```

## TODOサイト確認

以下のURLにアクセスし、ToDoサイトAPI管理画面が出てくること。

http://localhost:8000/docs

各種API操作ができること

以下のURLにアクセスし、ToDoサイトトップ画面が出てくること。

http://localhost:3000/

ToDoリストの登録/完了リストへの移動/リスト削除を行えること。

## 各種コンテナログイン

### Reactコンテナログイン

```bash
docker-compose exec react-app bash
```

### FastAPIコンテナログイン

```bash
docker-compose exec fastapi-app bash

DBマイグレートコマンド
poetry run python -m api.migrate_db

APIユニットテストコマンド
poetry run pytest
```

### DBコンテナログイン

```bash
docker-compose exec db bash

DBバックアップ

cd /dump
mysqldump -u root -ppassword --single-transaction --all-databases --events > mysql_dump.sql

DBリストア
db/dump内にバックアップファイルを置く。

cd /dump
mysql -u root -ppassword < mysql_dump.sql
```

## DB管理ツールログイン

### adminer(DB管理ツール)

http://127.0.0.1:8081

* ログイン情報
  - サーバ: db
  - ユーザ名: admin
  - パスワード: password
  - データベース: demo

# おわりに

本サイトについて駆け足で作成しているところもあるため、
自分でも誤りや問題に気づきましたら修正いたします。

気になるところなどありましたら、色々コメント頂けますと
嬉しいです。