OmniAICreator/Qiita-1.07M huggingface dataset:
【Swift】App Tracking Transparencyでアップル審査Rejectされた

# 概要

iOSアプリがアップルの審査でリジェクトされた。
内容は、App Tracking Transparencyの許可を求めるポップアップが出てこないというもの。
原因は、iOS15で仕様変更により、アプリがアクティブなとき（UIApplicationStateActive）にのみApp Tracking Transparencyの許可リクエストを行う必要があるということでした。
その対処方法をまとめる。
![Screen Shot 2022-03-18 at 16.54.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413207/34a3e704-837f-f19c-3623-f621fdd3fffd.png)



# ソースコード　これまで
```
	override func viewDidLoad() {
		// これまでは、ここにATTの処理を書いていた
	}
```

# ソースコード　これから
ATTの処理は、viewDidLoad()ではなく、applicationDidBecomeActive()に記述する。
```
import AppTrackingTransparency // IDFA対応
import AdSupport // IDFA対応

func applicationDidBecomeActive(_ application: UIApplication) {

        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
        // IDFA対応
        if #available(iOS 14, *) {
            switch ATTrackingManager.trackingAuthorizationStatus {
            case .authorized:
                print("Allow Tracking")
                print("IDFA: \(ASIdentifierManager.shared().advertisingIdentifier)")
            case .denied:
                print("😭拒否")
            case .restricted:
                print("🥺制限")
            case .notDetermined:
                showRequestTrackingAuthorizationAlert()
            @unknown default:
                fatalError()
            }
        }
        else {
        // iOS14未満
            if ASIdentifierManager.shared().isAdvertisingTrackingEnabled {
                print("Allow Tracking")
                print("IDFA: \(ASIdentifierManager.shared().advertisingIdentifier)")
            } else {
                print("🥺制限")
            }
        }
    }

    private func showRequestTrackingAuthorizationAlert() {
        if #available(iOS 14, *) {
            ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in
                switch status {
                case .authorized:
                    print("🎉")
                    //IDFA取得
                    print("IDFA: \(ASIdentifierManager.shared().advertisingIdentifier)")
                case .denied, .restricted, .notDetermined:
                    print("😭") 
// viewDidLoad()に記述した場合、ここを通るが、ポップアップは表示されていなかった
                @unknown default:
                    fatalError()
                }
            })
        }
    }
```

# 参考

iOS15でATT(App Tracking Transparency)の許可ダイアログが表示されない場合に確認すること
https://qiita.com/shindyu/items/10f5ee8d92eea9f80be4
Observerパターン デザインパターン

# Observerパターン 
(別名: Publish/Subscribeモデル) パブサブモデル

観察する側(=Observer)と観察される側(=Subject)のふたつの役割が存在し、Subjectの状態が変化した際にObserverに通知されるデザインパターンです。
そのため、状態変化に応じた処理を記述する時に有効です。「何かの動きに合わせて、別な何かを動かしたい」ような要件に使われる。

### 実装方法
#### Subjectクラス
* Observerを保持する変数を持つ
* Observerの追加や削除を行うメソッドを持つ
* 何か動きがあった時に、それを保持する全てのObserverに知らせるメソッドを持つ
#### Observerクラス
* Subjectに変更を通知してもらうためのPublicメソッドを持つ
* 変更の通知を受けた時の何かしらの処理を書く
【Swift】MVP

## MVP
#### Model :
ビジネスロジック、データ処理(内部ロジック)、純粋なドメインロジック
#### View :
表示と入出力処理、描画、イベント通知
#### Presenter :
Model操作、View更新、表示用のデータ保持
#### JournalsModel.swift
```
protocol JournalsModelInput {
}

class JournalsModel: JournalsModelInput {

}
```
#### JournalsViewController.swift
```
class JournalsViewController: UIViewController {

    private var presenter: JournalsPresenterInput!

    func inject(presenter: JournalsPresenterInput) {
        self.presenter = presenter
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        presenter = JournalsPresenter.init(view: self, model: JournalsModel())
        inject(presenter: presenter)
        
        presenter.viewDidLoad()
    }
}

extension JournalsViewController: JournalsPresenterOutput {

    func setupViewForViewDidLoad() {
    }
}
```
#### JournalsPresenter.swift
```
protocol JournalsPresenterInput {
}

protocol JournalsPresenterOutput: AnyObject {
}

final class JournalsPresenter: JournalsPresenterInput {

    private weak var view: JournalsPresenterOutput!
    private var model: JournalsModelInput
    
    init(view: JournalsPresenterOutput, model: JournalsModelInput) {
        self.view = view
        self.model = model
    }

    func viewDidLoad() {
        view.setupViewForViewDidLoad()
    }
}
```
【React】Refのメモ

業務でDOMを取得する際に使用したのでメモ。


# div要素を取得してみる

```react:
import { useEffect, useRef } from "react";

const Component = () => {
    const ref = useRef(null);

    useEffect(() => {
        console.log(ref.current);
    }, []);

    return (
        <div ref={ref}>hello</div>
    );
};

export default Component;
```

コンソールの結果は
```:Console
<div>hello</div>
```

# ためしにdivの小要素を取得してみる

```react:
import { useEffect, useRef } from "react";

const Component = () => {
    const ref = useRef(null);

    useEffect(() => {
        let child = ref.current?.children;
        console.log(child);
    }, []);

    return (
        <div ref={ref}>
          <h2>タイトル01</h2>
          <p>テキスト</p>
          <h2>タイトル02</h2>
          <p>テキスト</p>
        </div>
    );
};

export default Component;
```

コンソールの結果は
```:Console
HTMLCollection {0: HTMLHeadingElement, 1: HTMLParagraphElement, 2: HTMLHeadingElement, 3: HTMLParagraphElement, length: 4…}
0: <h2>タイトル01</h2>
1: <p>テキスト</p>
2: <h2>タイトル02</h2>
3: <p>テキスト</p>
```

# ためしにdivの小要素を取得して配列にしてみる

```react:
import { useEffect, useRef } from "react";

const Component = () => {
    const ref = useRef(null);

    useEffect(() => {
        let child = ref.current?.children;
        let elms = [].slice.call(child)
        console.log(elms);
    }, []);

    return (
        <div ref={ref}>
          <h2>タイトル01</h2>
          <p>テキスト</p>
          <h2>タイトル02</h2>
          <p>テキスト</p>
        </div>
    );
};

export default Component;
```

コンソールの結果は
```:Console
(4) [HTMLHeadingElement, HTMLParagraphElement, HTMLHeadingElement, HTMLParagraphElement]
0: <h2>タイトル01</h2>
1: <p>テキスト</p>
2: <h2>タイトル02</h2>
3: <p>テキスト</p>
```

最後の方は何してるのって感じですね。
公式ではRefを使いすぎるなと書いてあるので気をつけます。
Firebaseのプロジェクトを本番用と開発用に分ける方法



# 概要

Crashlyticsのデータを本番用と開発用に分ける場合の手順をまとめる。




#### Firebaseコンソール上で、本番用(Product)と開発用(Staging)のプロジェクトを作成する
https://console.firebase.google.com/
![Screen Shot 2022-03-18 at 18.01.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413207/67dffcaa-ed0e-081e-f5b7-3384b9a08a03.png)

##### GoogleService-Info.plistをダウンロードする
プロジェクトの設定 > 全般 
本番用(Product)と開発用(Staging)のプロジェクトから、それぞれ、GoogleService-Info.plist をダウンロードする



##### ダウンロードした GoogleService-Info.plist をXcodeのプロジェクトへ設置する
以下のような構成にしてみた。
![Screen Shot 2022-03-18 at 17.57.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413207/9e0d2885-b540-1ec2-73ae-d24816bbcfc3.png)


##### Xcode TARGETSを本番用(Product)と開発用(Staging)に分ける

GoogleService-Info.plistの設定をする
TARGETS > 本番用 > Build Phrases > Copy Bundle Resources > 「+」
本番用のGoogleService-Info.plist を追加する (開発用(Staging)も同様に設定する)
![Screen Shot 2022-03-18 at 18.32.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413207/a9a27371-3a0e-b2cc-b9fa-3a5e743b936f.png)

これで、Firebase Crashlyticsのデータを本番用(Product)と開発用(Staging)のプロジェクトでそれぞれ確認することができる。
【Swift】Xcode13対応

#### GitHub
[ソースコード](https://github.com/YingZheng02590218/iKingdom/blob/master/iOS/Accountant/v1.0/Program/src/Accountant/View/FinancialStatement/BS/BSViewController.swift)


### テーブルビューのセクションヘッダに余分なpaddingがある
iOS15ではデフォルトでテーブルビューのセクションヘッダの上部に20px程度のpaddingがある。 
iOS15未満と同じ外観にするには、sectionHeaderTopPadding を0に指定する。

```
    private func setTableView() {
        tableView.dataSource = self
        tableView.delegate = self
        tableView.register(UINib(nibName: "BSTableViewCell", bundle: nil), forCellReuseIdentifier: "BSTableViewCell")

        if #available(iOS 15.0, *) {
            tableView.sectionHeaderTopPadding = 0
        }
    }
```
【Swift】お問い合わせ機能を作る　アプリ内でメーラー起動する


# 概要

iOSアプリ内でメールを送信する機能を作る手順をまとめる。


# 目次
- フレームワークを追加
- ソースコードを書く
    1. MessageUIをimportする
    2. MFMailComposeViewControllerインスタンスを生成し、宛先や本文などを設定する
    3. MFMailComposeViewControllerDelegateで終了処理を実装する



## フレームワークを追加

### MessageUI.frameworkを追加する

プロジェクト > Build Phases > Link Binary With Libraries > 「＋」> MessageUI.framework


## ソースコードを書く

### 1. MessageUIをimportする
```
import MessageUI
```
### 2. MFMailComposeViewControllerインスタンスを生成する
今回はテーブルビューセルがタップされた場合
```
		case 2:
            // お問い合わせ機能
            if MFMailComposeViewController.canSendMail() {
                let mail = MFMailComposeViewController()
                mail.mailComposeDelegate = self
                mail.setToRecipients(["paciolist@gmail.com"])   // 宛先アドレス
                mail.setSubject("問い合わせ")                     // 件名
                mail.setMessageBody("", isHTML: false)          // 本文
                present(mail, animated: true, completion: nil)
            }
            else {
                print("送信できません")
            }
        break
```
### 3. MFMailComposeViewControllerDelegateで終了処理を実装する
```
extension SettingsTableViewController: MFMailComposeViewControllerDelegate {
    // お問い合わせ機能
    func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
        
        switch result {
        case .cancelled:
            print("キャンセル")
        case .saved:
            print("下書き保存")
        case .sent:
            print("送信成功")
        default:
            print("送信失敗")
        }
        
        dismiss(animated: true, completion: nil)
    }
}
```


# 参考

【Swift】 お問い合わせ機能の実装
https://qiita.com/_mkt_/items/fd73c10dcc5a760149e3

【Swift】アプリ内でメーラーを起動しメール送信するやり方
https://qiita.com/kobaboy/items/60a2f44ad53675cfa0ae
【Swift】アイコン画像の色を変える

## 完成形
![IMG_7921.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413207/507226bc-2ab1-c907-c4ba-d50b974d8702.png)
![IMG_7920.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413207/92a4a445-f9a0-964b-417d-b72e38a6d507.png)
## 修正前
![IMG_7925.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413207/654080b5-ba8f-8d2b-7bba-e21b239c6f9c.png)
![IMG_7926.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413207/4e036d7f-38ef-ebb5-91f2-8118a24bb010.png)


## UIImageView の場合

#### 色を指定する
色は TintColor に従うので、 TintColor を変更することで色を変えることができます。
```
class WithIconTableViewCell: UITableViewCell {

    @IBOutlet var leftImageView: UIImageView!
    @IBOutlet var centerLabel: UILabel!
    
    override func awakeFromNib() {
        super.awakeFromNib()
        // アイコン画像の色を指定する
        leftImageView.tintColor = .TextColor
    }
```
#### 画像のα値だけで描画させる
UIImage で指定できる UIImageRenderingMode.AlwaysTemplate は画像のα値だけで描画してくれます。
```
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        if indexPath.section == 0 {
            switch indexPath.row {
            case 0:
                //① UI部品を指定　TableViewCell
                let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! WithIconTableViewCell
                cell.centerLabel.text = "アップグレード"
                cell.leftImageView.image = UIImage(named: "icons8-シェブロン-25")?.withRenderingMode(.alwaysTemplate)
                return cell
            default:
                return WithIconTableViewCell()
            }
        }
```
## UIButton の場合
```
            // 画像のα値だけで描画させる
            let backImage = UIImage(named: "icons8-戻る-25")?.withRenderingMode(.alwaysTemplate)
            Button_Left.setImage(backImage, for: UIControl.State.normal)
            // アイコン画像の色を指定する
            Button_Left.tintColor = .TextColor
```
【Swift】Sandbox アプリ内ファイル保存取得削除


SandboxのDocumentディレクトリにフォルダを作成して、そのフォルダへPDFファイルを保存、取得、削除する

GitHub
[ソースコード](https://github.com/YingZheng02590218/iKingdom/blob/master/iOS/Accountant/v1.0/Program/src/Accountant/PDFMaker.swift)

### PDFファイルを保存
```
    func saveToDocumentDirectory(data: NSData) -> URL? {

        guard let documentDirectory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) else { return nil }
        let pDFsDirectory = documentDirectory.appendingPathComponent("PDFs", isDirectory: true)
        do {
            try FileManager.default.createDirectory(at: pDFsDirectory, withIntermediateDirectories: true, attributes: nil)
        }
        catch {
            print("失敗した")
        }
        // PDFファイルを保存
        let filePath = pDFsDirectory.appendingPathComponent("receipt-" + UUID().uuidString + ".pdf")
        do {
            try data.write(to: filePath)
            print(filePath)
            return filePath
        } catch {
            print(error.localizedDescription)
            return nil
        }
    }
```
### PDFファイルを取得と削除
```
    var PDFpath: [URL]?
    
    func initialize() {
        // 初期化
        PDFpath = []
        guard let documentDirectory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) else { return }
        // PDFsというフォルダを作成
        let pDFsDirectory = documentDirectory.appendingPathComponent("PDFs", isDirectory: true)
        do {
            try FileManager.default.createDirectory(at: pDFsDirectory, withIntermediateDirectories: true, attributes: nil)
        }
        catch {
            print("失敗した")
        }
        do {
            // ファイル一覧を取得
            let directoryContents = try FileManager.default.contentsOfDirectory(at: pDFsDirectory, includingPropertiesForKeys: nil)
            // if you want to filter the directory contents you can do like this:
            let pdfFiles = directoryContents.filter{ $0.pathExtension == "pdf" }
            let pdfFileNames = pdfFiles.map{ $0.deletingPathExtension().lastPathComponent }
            // PDFファイルを取得
            for fileName in pdfFileNames {
                let content = pDFsDirectory.appendingPathComponent(fileName + ".pdf")
                do {
                    // PDFファイルを削除
                    try FileManager.default.removeItem(at: content)
                }
                catch let error {
                    print(error)
                }
            }
        }
        catch {
            print(error)
        }
    }
```
Firebase dSYMアップロード方法

# 概要

Firebaseコンソールで
“このアプリには未処理のクラッシュが n 件あります。これらを処理するには、n個の dSYM ファイルをアップロードしてください。”
と言われた場合の対処法をまとめます。
![Screen Shot 2022-03-18 at 15.11.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413207/522bcbf7-d59d-a057-8852-3c235ae983db.png)



# 目次
- dSYMのダウンロード
- dSYMのアップロード
    - スクリプトファイルを作成する
    - シェルスクリプトを実行する



# dSYMのダウンロード

App Store Connect > My Apps > TestFlight > 任意のバージョン、ビルド > Build Metadata > Includes Symbols

Yes | Download dSYM

※ ダウンロードができない場合は、「dSYMをダウンロード」のリンク先をコピーして、アドレスバーで実行する

ダウンロードしたappDsyms.zipを開く。
![Screen Shot 2022-03-18 at 15.22.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413207/8d904cbe-1bfe-bc3b-4966-c6d6da96de4b.png)



# dSYMのアップロード

### スクリプトファイルを作成する

#### ファイル名
upload_dsyms.sh

テキストエディタでファイルを作成して、ファイル名を上記と同じにする。
ユーザーディレクトリに置くと楽。

#### ファイル内容
内容は以下をペースにして、パスは自身の環境のものに修正する。
アプリのプロジェクトからupload-symbols、GoogleService-Info、を探してそのパスをコピペする。
ダウンロードしたappDsymsフォルダのパスをコピペする。
```
#!/bin/sh

# CrashlyticsにdSYMsをアップロードする 

# upload-symbolsの場所 CocoaPodsの場合 Pods/FirebaseCrashlytics/upload-symbols
UPLOAD_SYMBOLS=“/フルパス/Pods/FirebaseCrashlytics/upload-symbols"

# GoogleService-Infoファイルを指定する（ファイル名はなんでもOK。ここでは本番用に設定したファイル名にしてます）
GOOGLE_SERVICE_INFO_PLIST="/フルパス/GoogleService-Info.plist"

# App Store ConnectからDownloadしたdSYMフォルダのパスを指定する。（zipファイルから解凍するとappDsymsフォルダができる）
DSYMS="/フルパス/appDsyms"

"$UPLOAD_SYMBOLS" -gsp "$GOOGLE_SERVICE_INFO_PLIST" -p ios "$DSYMS"
```


### シェルスクリプトを実行する

#### ターミナルでコマンドを実行する
ターミナルで、upload_dsyms.shが配置されているディレクトリへ移動して、以下のコマンドを実行する。
```
$ sh upload_dsyms.sh
```
※ ターミナルにSuccessfully uploaded Crashlytics symbolsが表示されてもしばらく待たないとコンソールに反映されないケースもある
WSL2+VcXsrvでGUIアプリケーションを使う

忘れるのでメモ

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

https://docs.microsoft.com/ja-jp/windows/wsl/install

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

https://sourceforge.net/projects/vcxsrv/

## VcXsrvを起動する

Multiple windowsを選択して次へ
![スクリーンショット 2022-03-28 174456.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561682/056fadb9-8e47-8fe6-871c-900295ee8962.png)

Start no clientを選択して次へ
![スクリーンショット 2022-03-28 174510.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561682/8bbbec90-ea7e-b473-5af5-c7d41340f8a1.png)

Disable access controlを選択して次へ
![スクリーンショット 2022-03-28 174527.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561682/e068d431-2ba5-2f6b-57c7-663278f8c9fe.png)

完了
![スクリーンショット 2022-03-28 174545.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561682/198dbde7-7f31-bf44-9c65-f78ceef83732.png)

タスクバーにアイコンが表示されます。終了は、右クリックしてExit...を選択する。
![スクリーンショット 2022-03-28 175137.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561682/3bd50897-9fb0-9e9e-9d06-5f800e0a8710.png)


## 環境変数の設定とGUIの確認

```sh
// ホスト側（Windows）のIPアドレスを設定（自身の環境に合わせてください。）
$ export DISPLAY=192.168.1.2:0
// テスト用のアプリをインストール
$ sudo apt install x11-apps
$ xeyes
```

目玉が表示されれば完了です。
![スクリーンショット 2022-03-28 175458.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561682/199d9ec0-cb68-fe52-e559-7ee91f77cb15.png)


# 起動時に環境変数を設定する

ホストのIPアドレスがDHCPで動的に割り当てられていると、変わる度に設定するのは面倒です。

/etc/resolv.confやhostnameを設定する情報が見つかりますが動かなかった。

PowerShellからIPアドレスが取れないか試行錯誤する。

```powershell
PS> Get-CimInstance -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=$true | Where-Object -FilterScript { $_.DefaultIPGateway } | Select-Object -ExpandProperty IPAddress | Where-Object -FilterScript { $_ -like "*.*.*.*" }
192.168.1.2
```

一応、必要なIPアドレスが取れたので、このコマンドを使ってWSL2を起動したら環境変数が設定されるように~/.bashrcへ追記する。

```~/.bashrc
export DISPLAY=`powershell.exe -Command '& {(Get-CimInstance -Class Win32_NetworkAdapterConfiguration -Filter IPEnabled=$true | Where-Object -FilterScript { $_.DefaultIPGateway } | Select-Object -ExpandProperty IPAddress | Where-Object -FilterScript { $_ -like "*.*.*.*" }) + ":0" | Write-Host -NoNewLine }'`
```

再度WSL2を起動しても正しく設定されるので良しとする。

```sh
echo $DISPLAY
192.168.1.2:0
```
input(TextField)内にペーストした画像を表示する方法 (React, MUI(旧Material UI))

# 目的

SlackやTeamsのように記入欄に画像をペーストし表示するのを目的としています。
ペーストした瞬間にDBに送りそのURLを取得して画像を表示するのではなく、ペーストした画像からデータを取得し表示します。
ただSlack、Teams、Qiitaはペーストした瞬間にDBに送ってるみたいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/483460/2ea919d7-4a2b-5003-f24b-4784b7e4f764.png)

# 方法
React, Material UIを使っています。

```typescript:index.tsx
import * as React from 'react';
import TextField from '@mui/material/TextField';
import { Button, Stack } from '@mui/material';
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
import ImageListItemBar from '@mui/material/ImageListItemBar'
import IconButton from '@mui/material/IconButton';
import Box from '@mui/material/Box';
import DeleteIcon from '@mui/icons-material/Delete';

type imageDataType = {
  src:string;
  imageFile:File;
}

export default function TextFieldImg() {

  const [newPostValue, setNewPostValue] = React.useState('');
  const [imageData, setImageData] = React.useState<imageDataType[]>([]) 

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setNewPostValue(event.target.value);
  };

  const handlePasteImage  = async(event: React.ClipboardEvent<HTMLDivElement>) => {
    const isImage = event.clipboardData.items[0].type.indexOf("image") != -1
    if(isImage){
      const imageFile = event.clipboardData.items[0].getAsFile()
      if(imageFile!==null){
        const URLObj = window.URL || window.webkitURL
        const imgSrc = URLObj.createObjectURL(imageFile)
        setImageData([...imageData, {
          src:imgSrc,
          imageFile
        }])
      }
    }
  }

  const handleClickDeleteImg = (imgSrc:string) => () => {
    setImageData(imageData.filter(img=>img.src !== imgSrc))
  }

  return (
    <>
    <TextField
      sx={{margin:8,width:490}}
      multiline
      rows={8}
      onChange={handleChange}
      onPaste={handlePasteImage}
    />
    <Box>
      <ImageList cols={3} gap={8}>
        {imageData.map((item) => 
          <ImageListItem key={item.src}>
            <img src={item.src}/>
            <ImageListItemBar
              position="below"
              actionIcon={
                <IconButton onClick={handleClickDeleteImg(item.src)}>
                  <DeleteIcon />
                </IconButton>
              }
            />
          </ImageListItem>
          )}
      </ImageList>
    </Box>
    <Stack direction="row" spacing={2}>
      <Button variant="contained">Post</Button>
    </Stack>
    </>
  );
}

```

# ブラウザーの互換性

Chrome, Edge, FireFoxは確認済み

`ClipboardEvent.clipboardData`がすべて対応してるみたいなので大体動くと思います。

# 環境
React v16.9.0
node v12.22.1
MUI(旧Material UI) v5.5.1

# 参考

https://www.nightswinger.dev/2021/11/clipboard-api-in-javascript/

https://qiita.com/volpe28v/items/dc100c75c2371bb82a3b

https://qiita.com/tatesuke/items/00de1c6be89bad2a6a72

https://www.petitmonte.com/javascript/clipboard_paste.html

https://developer.mozilla.org/ja/docs/Web/API/ClipboardEvent/clipboardData
Google Chromeの自動アップデートを阻止する

Google Chromeが勝手にアップデートしないようにする方法をご紹介します。
知らぬ間にChromeが勝手にアップデートして、chromedriverとのバージョン一致でSeleniumがエラーを起こすというのが割とよくあります。(Chromeのバージョンアップ毎に)

macOSの場合以下のディレクトリ配下にある(他OSは適宜読み替えを)
```
$ /Applications/Google Chrome.app/Contents/
```

`Info.plist`を編集します。
```
sudo vi /Applications/Google\ Chrome.app/Contents/Info.plist
```

250行目付近の`KSUpdateURL`値<https://tools.google.com/service/update2>を適当なものに書き換えます。`hogehoge`とか`あいうえお`とかで良いと思います。

``` 
<key>KSUpdateURL</key>
<string>https://tools.google.com/service/update2</string>
```

↓

``` 
<key>KSUpdateURL</key>
<string>あいうえお</string>
```
これにてChrome自動アップデート阻止完了です。
1回目（アウトプット）

初めましてくっぴき（kuppiki）と申します。

アウトプットする機会を作ろうと思い、参考にしていることが多かったプラットフォームであるQiitaを選びました。
これからの3か月間で、Qiitaの記事を100個を目標にまとめていこうとおもっています。

![アウトプット1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2606732/e856eae7-8b08-112e-039a-517a615ce809.png)

ルールは3つ
マインドマップで階層を意識する。
文章は3分で読めるぐらいの量にする
画像は一つは必ず入れるようにする
![アウトプット2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2606732/f5a76df6-4b25-7e72-088a-125b764b74ae.jpeg)

第一回目の投稿になります。
次回は「インフラ」がテーマです。
GCPのCloudDNSでDNS-01チャレンジ

## したいこと
- 諸事情で80番ポートは使えないけどlet's encryptでSSL認証したい（ので、DNSチャレンジを使う）
- GAEで動かしているサイトのサブドメインを使いたい

## やること
認証はDNSチャレンジでやる。

### 1.準備
Nginxを停止させる
```
$ systemctl stop nginx 
``` 

Certbotを入れる
```
$ sudo apt install epel-release
$ sudo apt install certbot
```

### 2. 証明書を作成
```
sudo certbot certonly --manual -d fuga.hogehoge.hoge --preferred-challenges dns
```

`Are you OK with your IP being logged?` にYesを押すと、こんなメッセージが出てくる
```
Please deploy a DNS TXT record under the name
_acme-challenge.fuga.hogehoge.hoge with the following value:

[キー]

Before continuing, verify the record is deployed.
```

GCPのCloud DNSを開く

（ドメインの設定とかは終わってるものとします）

レコードセットを追加
→　タイプはTXT
→　DNS名を _acme-challenge.fuga.hogehoge.hoge　にする
→　TXTデータにさっき出てきたキーを入力
→　反映されるまで時間がかかるらしいので、水分補給とかして少し待つ（10分くらい？）
→　cmdでEnterを押す

```
IMPORTANT　NOTES：
    Congraturations! (ry
```

って出てきたら成功
cscの作法 その70

# 概要
cscの作法、調べてみた。
mdbにsql投げて合計求めて、円グラフにしてみた。

# 参考にしたページ

https://qiita.com/icywhite/items/730db15fc200b8af9483

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/5b413bfc-5430-dec6-a529-7292b429a6d7.png)


# コンパイル手順

```
>set PATH=C:\Windows\Microsoft.NET\Framework\v4.0.30319;%PATH%

>csc pie0.cs  /platform:x86 /r:System.Windows.Forms.DataVisualization.dll

```

# サンプルコード

```
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Data.OleDb;
using System.IO;

namespace WindowsFormsApplication1
{
	public partial class Form1 : Form {
		public Form1() {
		ClientSize = new Size(900, 600);
			var chart1 = new Chart {
				Dock = DockStyle.Fill,
			};
			this.Controls.Add(chart1);
			Series mySeries = new Series("series");
			int[] data = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
			string[] moku = new string[] { "光熱・水道", "家具・家事用品", "教養娯楽", "住居", "保健医療", "交通・通信", "交際費", "非消費支出(直接税・社会保険料)", "食料", "被服及び履物", "雑費", "貯蓄" };
			OleDbConnection con = new OleDbConnection();
			con.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=mydb.mdb;";
			con.Open();
			for (int i = 0; i < 11; i++)
			{
				string sSQL = "SELECT SUM(金額) FROM sisyutu WHERE 項目 = '" + moku[i] + "'";
				OleDbCommand cmd = new System.Data.OleDb.OleDbCommand(sSQL, con);
				object sum = cmd.ExecuteScalar();
				if (sum != DBNull.Value)
				{
					int k = Convert.ToInt32(sum);
					Console.WriteLine(k);
					data[i] = k;
				}
			}
			mySeries.Points.DataBindXY(moku, data);
			mySeries.ChartType = SeriesChartType.Pie;
			mySeries["PieLabelStyle"] = "Inside";
			mySeries["PieDrawingStyle"] = "Default";
			ChartArea myArea = new ChartArea("area");
			chart1.ChartAreas.Add("area");
			chart1.Series.Add(mySeries);
			mySeries.ChartArea = "area";
			chart1.Legends.Add(new Legend("Default"));
			LegendCellColumn firstColumn = new LegendCellColumn();
			firstColumn.ColumnType = LegendCellColumnType.SeriesSymbol;
			firstColumn.HeaderText = "";
			chart1.Legends["Default"].CellColumns.Add(firstColumn);
			LegendCellColumn percentColumn = new LegendCellColumn();
			percentColumn.Text = "#PERCENT";
			percentColumn.HeaderText = "Percentage";
			percentColumn.Name = "nameColumn";
			chart1.Legends["Default"].CellColumns.Add(percentColumn);
			chart1.Legends["Default"].LegendStyle = LegendStyle.Table;
			chart1.Legends["Default"].TableStyle = LegendTableStyle.Tall;
			chart1.Legends["Default"].DockedToChartArea = "area";
			chart1.Legends["Default"].IsDockedInsideChartArea = false;
			chart1.Legends["Default"].Docking = Docking.Right;
		}
		[STAThread]
		static void Main() {
			Application.Run(new Form1());
		}
	}
}





```


以上。
UbuntuでWifiを使えるようにする

完全に自分用メモです。
誰かがこの記事で助かることがあれば嬉しいです。

環境は
- kが頭のUbuntu 20.04
です。

Linuxで初期設定なしで使える無線LAN「[Alfa AWUS036NHA](www.amazon.co.jp/dp/B004YDT758)」を使っています。 
Kali linuxでWire Sharkとか使ってパケット解析ができたりするので、いろいろ遊べるようです。ebayあたりで安く手に入れましょう。
普通に使うだけなら、[TL-WN725N](https://amzn.to/3nYDc4i)という機種が動くようです。
もちろん作業は他の無線LANでもつながれば可能ですし、有線LANでも可能です。

手順としては
- lsusbでWifiの規格を調べる。
- 規格にしたがったドライバー入れる。
になりますが、個人的な環境のもと、説明をしていきます。

## lsusbで端末を調べる。

USBのWifiを差して`lsusb`しましょう。

![RTL8812AU.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285882/37f5983c-a78e-be00-d7bf-51b73ab6d8b5.png)

抜いて差して確認したところ、`RTL8812AU`が今回のWIFIです。
このRTL8812AUのドライバをLinux機、今回はUbuntuに導入することで動かします。

## ドライバをインストールする。

Githubでドライバが公開されていたりするので、それをインストールしていきます。

今回はふたつ検索にひっかかりました。
[morrownr/8812au](https://github.com/morrownr/8812au)
[aircrack-ng/rtl8812au](https://github.com/aircrack-ng/rtl8812au)

このうち、今回はaircrack-ngさんのGitリポジトリより説明を受けながらインストールしていきます。

普通に
`make install`でもインストールできるようですが、dkmsというツールが便利そうなので使います。
[dkms](https://wiki.archlinux.jp/index.php/Dynamic_Kernel_Module_Support)はDynamic Kernel Module Supportのことでしょう。

まずはdkmsをインストールします。

```
$ sudo apt-get install dkms
```

今回のRTL8812AUのドライバのコードをダウンロードする。

```
$ git clone -b v5.6.4.2 https://github.com/aircrack-ng/rtl8812au.git
```

ダウンロードしたディレクトリに移動して、
```
$ cd rtl8812au
```

DKMSを使ってインストールする。(dkmsを使わない場合は`$ make && make install`)
```
$ sudo make dkms_install
```

インストールが済んだら、コンピュータを再起動しましょう。
再起動後、Wifiを掴んでくれます。

掴んでくれない場合は、SecureBoot等で問題があるようですので、こちらの記事なんか参考になるかもしれません。
[https://qiita.com/hisw/items/e37ecd50e311fa505e17](Ubuntu(Linux)でrtl8812auのドライバを入れてもWiFiが使えない時の対処)
JavascriptでJSONの配列の中からランダムに重複せず指定個数の配列を抜き出したい

# タイトルの通りなのだがやりたいことを説明
1. JSONの配列がある
1. その数より少ない要素数を指定して
1. 配列の中からランダムに
1. しかし重複なく
1. 新しい配列を作成したい

なんだかいろいろと方法がありそうだが、できるだけ組み込み関数を利用して実装したい。

##  ランダムな要素抽出
``` javascript
const objs = [
  { title: "apple", price: 500 },
  { title: "orange", price: 200 },
  { title: "melon", price: 5000 },
  { title: "tako", price: 2500 },
  { title: "ika", price: 100 },
  { title: "mochi", price: 150 }
];
// console.log(objs);

const new_len = 3;
// 配列に使用すると 要素の数分の数値を出せる
// obj_keys = 0, 1, 2, 3, 4, 5 となる（以下）
var obj_keys = Object.keys(objs);

for (let i = 0; i < new_len; i++) {
  // ランダムに obj_keys の範囲内で整数を選択して
  // オブジェクトを表示する
  var ran_key = obj_keys[Math.floor(Math.random() * obj_keys.length)];
  console.log(objs[ran_key]);
}
```
最初の実装はこんな感じ。配列の要素数を Math.random() に書けることでインデックスに乱整数を入れて、要素を表示する。
実際には実行結果に重複が含まれる。

### 重複をチェックするには？
これが一番わかりやすい方法なんだがこれで良いのだろうか？ってコード。

```javascript
const objs = [
  { title: "apple", price: 500 },
  { title: "orange", price: 200 },
  { title: "melon", price: 5000 },
  { title: "tako", price: 2500 },
  { title: "ika", price: 100 },
  { title: "mochi", price: 150 }
];

function intRandom(n_keys) {
  return Math.floor(Math.random() * n_keys);
}

// 既出ランダム配列のindexを格納する配列
let selected_rand = [];
// すでに選択されたタイトルを格納する配列
let selected_titles = [];

const new_len = 3;

// 取り出したい配列の数だけループする
for (let i = 0; i < new_len; i++) {
  // ランダム配列の取得ができるまでループする
  while (true) {
    var tmp = intRandom(objs.length);
    if (!selected_rand.includes(tmp)) {
      selected_rand.push(tmp);
      selected_titles.push(objs[tmp].title);
      break;
    }
  }
}

console.log(selected_objs);
```
できるだけ組み込み関数を利用するという部分で自信がないが。
SQLiteでテーブルのカラム名を変更したい時

SQLiteではカラム名変更のコマンドが用意されていないとのこと（驚き）
つまり、カラム名は変更できない。
なので、カラム名を変更したテーブルを新たに作成して、既存のテーブルの内容をインサートする。
インサートしおわったら既存の古いテーブルは削除する。

```
-- 既存のテーブルをリネーム
ALTER TABLE tbl_name RENAME TO tbl_temp;
-- 新しいテーブルを作成（元々のテーブル名と同じ名前で）
CREATE TABLE tbl_name(col1 TEXT, col2_new TEXT);
-- レコードを全て移す
INSERT INTO tbl_name(col1, col2_new) SELECT col1, col2 FROM tbl_temp;
-- 元のテーブルを削除
DROP TABLE tbl_temp;
```
## 上記コマンドの引用元
https://maku77.github.io/sql/rename-column.html
cscの作法 その69

# 概要
cscの作法、調べてみた。
mdbにsql投げて合計求めてみた。

# コンパイル手順

```
>csc sql1.cs /platform:x86

```

# サンプルコード

```
using System;
using System.Data.OleDb;
using System.Data;
using System.IO;
using System.Text;
using System.Collections.Generic;

public static class Program {
	public static void Main(params string[] args) {
		OleDbConnection con = new OleDbConnection();
		con.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=mydb.mdb;";
		con.Open();
		string sSQL = "SELECT SUM(金額) FROM sisyutu WHERE 項目 = '食料'";
		OleDbCommand cmd = new System.Data.OleDb.OleDbCommand(sSQL, con);
		object sum = cmd.ExecuteScalar();
		Console.WriteLine(sum + "円です。");
		int i = Convert.ToInt32(sum);
		Console.WriteLine(i);
	}
}
```


以上。
docker-compose.ymlを和訳したら思いの外わかりやすかった！

ふと、docker-compose.ymlを和訳してみたところ、日本語というだけで意外とわかりやすく感じたのでみんなにも共有したい : )
# docker-compose.ymlでよく使うオプション詰め合わせのサンプル

## 英語バージョン(通常)
```docker-compose.yml
version: '3'

services:
  db:
    image: mysql
    container_name: db
    restart: always
    environment:
      MYSQL_PASSWORD: foobar
      MYSQL_PORT: 3306
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - db:/var/lib/mysql
  app:
    build:
      context: .
      dockerfile: ./dev/docker/Dockerfile
    environment:
      APP_URL: http://localhost:${DEV_PORT:-8080}
      DB_CONNECTION: mysql
    ports:
      - ${DEV_PORT:-8080}:80
    volumes:
      - ./:/app
    depends_on:
      - db
    entrypoint: /app/dev/docker/entrypoint.app.sh
    extra_hosts:
      - "somehost:111.111.111.111"
 ```
全体を和訳すると、
## 日本語バージョン
 ```
バージョン：'3'

サービス名:
  データベースのコンテナ:
    イメージ名: mysql
    コンテナ名: db
    再起動:常に
    環境変数:
      MYSQL_パスワード: foobar
      mysql_ポート: 3306
    コマンド(デフォルトのコマンドを無効にする): --デフォルト-認証-プラグイン=mysql_元来_パスワード認証方式
    ボリューム(手元のOSのディレクトリをコンテナの中のディレクトリにマウントしてデータを永続的に保存するパスを指定):
      - db:/var/lib/mysql
  アプリのコンテナ:
    ビルド:
      背景(コンテキスト(Dockerfileを含むディレクトリへのパス、またはgitリポジトリへのURLのいずれかを指定)): .
      dockerファイル: ./dev/docker/Dockerfile
    環境変数:
      アプリ_URL: http://localhost:${開発_ポート:-8080}
      データベース_接続: mysql
    ポート:
      - ${開発_ポート:-8080}:80
    ボリューム:
      - ./:/app
    依存関係(サービス同士の):
      - db
    エントリーポイント: /app/entrypoint.app.sh
    特別_ホスト(ホスト名をマッピング):
      - "なんちゃらホスト:111.111.111.111 "
 ```

漢字は強い。優先的に認識＆変換されるし、なにより情報密度が高い。

やっぱり英語ネイティブスピーカーだとプログラミングの学習スピードも早いのだろうか:octocat:
digメソッドとは

## digメソッドとは
``digメソッド``とは、配列とハッシュから**安全に**値を取り出すことができるメソッド。

安全にとはどういうことか、それは``digメソッド``を使用して値を取得する場合、エラーは発生せず``nil``を返してくれる。
これが配列とハッシュから安全に値を取り出すことができる理由。

```rb
# digメソッドを用いて値を取得する場合

# 存在しないmusic、rockを取得しようとする
users.dig(1, :profile, :hobbys, :music, :rock)
=> nil
```

## 配列の場合
以下のような配列があると仮定し、``digメソッド``を用いてそれぞれの値を取り出してみる。

``digメソッド``の引数に取得したい値の``index``を指定していくことで値を取り出すことができる。

```rb
fruits = [[ "apple",[ "banana", "grape" ]]]

# appleを取得する
fruits.dig(0, 0)
=> "apple"

# bananaを取得する
fruits.dig(0, 1, 0)
=> "banana"

# grapeを取得する
fruits.dig(0, 1, 1)
=> "grape"
```

## ハッシュの場合
以下のようなハッシュがあると仮定し、``digメソッド``を用いてそれぞれの値を取り出してみる。``digメソッド``の引数に取得したい値のキーを指定していくことで値を取り出すことができる。

```rb
users =
{
  name: {
    first_name: '太郎',
    last_name: '田中'
  },
  age: 25
}

# ageを取得
users.dig(:age)
=> 25

# first_nameを取得
users.dig(:name, :first_name)
=> "太郎"

# last_nameを取得
users.dig(:name, :last_name)
=> "田中"
```

## 配列とハッシュの入れ子構造の場合
以下のような配列とハッシュの入れ子構造があると仮定し、``digメソッド``を用いてそれぞれの値を取り出してみる。配列とハッシュの場合同様、それぞれ``index``もしくはキーを指定することで取得することができる。

```rb
users =
[
  "佐藤",
  profile: {
    age: 30,
    hobbys: {
      sports: "サッカー",
      manga: "ワンピース"
    }
  }
]

# nameを取得
users.dig(0)
=> "佐藤"

# ageを取得
users.dig(1, :profile, :age)
=> 30

# sportsを取得
users.dig(1, :profile, :hobbys, :sports)
=> "サッカー"

# mangaを取得
users.dig(1, :profile, :hobbys, :manga)
=> "ワンピース"
```

## 参考記事
[【Rails】配列・ハッシュから安全に値を取り出すことができるdigメソッドについて](https://techtechmedia.com/dig/)
Google Cloud SDKでの認証方法が面倒になった模様

# TL;　DR;
CLIだけで操作するマシン単体では認証が通らない
Webブラウザが使えるマシンにもSDKを入れて、そこでブラウザ経由での認証をしなければならない
`--no-launch-browser`オプションが必要

# 経緯
Google Cloud SDK (version 377.0.0)をインストールしたGCEインスタンス内でloginコマンドをしたら、見慣れないことを言われた。

```
$ gcloud auth login

 You are authorizing gcloud CLI without access to a web browser. Please run the following command on a machine with a web browser and copy its output back here. Make sure the installed gcloud version is 372.0.0 or newer.

gcloud auth login --remote-bootstrap="https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=xxxxxxxxx.apps.googleusercontent.com&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fappengine.admin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcompute+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Faccounts.reauth&state=hogehoge2&access_type=offline&code_challenge=fugafuga&code_challenge_method=S256&token_usage=remote"

Enter the output of the above command: ###←入力待ち状態

```

一旦今のコマンドを停止させて、出力されたコマンドを別途コマンドプロンプト(Windows 10にWSLで立ち上げたubuntu 18.04)に入力してもエラー
```
$ gcloud auth login --remote-bootstrap="https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=xxxxxxxxx.apps.googleusercontent.com&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fappengine.admin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcompute+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Faccounts.reauth&state=hogehoge2&access_type=offline&code_challenge=fugafuga&code_challenge_method=S256&token_usage=remote"

ERROR: (gcloud.auth.login) Cannot launch browser. Please run this command on a machine where gcloud can launch a web browser.
```

出力されているURL(--remote-bootstrapのパラメータ)をブラウザに入力しても400:invalid_request

ちなみに version 376.0.0の入ったインスタンスで実行した場合は、URLが出力されてそのURLから直接ブラウザ上でのGoogleアカウント認証が可能。

# 対応

```
$ gcloud auth login　--no-launch-browser
```

オプションを付けなかったらどうなるか
コマンドプロンプトから直接Webブラウザにアクセスできる環境にて、gcloudコマンドを実行する必要がある。
今回のケースだと
1. GCEインスタンス内で $ gcloud auth login実行(入力待ち状態になる)
2. 出力されたコマンドを、Windowsのコマンドプロンプトで(WSLのubuntuではだめ)実行
3. ブラウザ上でGoogleアカウント認証が行われる
4. 認証が通ると、Windowsのコマンドプロンプトに新たなコマンドが出力される
5. 出力されたコマンドを、GCEインスタンスで　`Enter the output of the above command:` にコマンドを入力する
6. 認証完了

WSL環境ではだめというのがまた困った話で、操作している端末でgcloudコマンド実行が必須になった。これまでであれば発行されたURLさえあればよかった。

# 正直かなり困っている
あまりに困ったので勢いだけで記事を書き上げてしまった。
更新: オプションを付けることで解消できていたが書くのを忘れていた
curlでJSONをPOSTするとPowerShellの時だけエラー

# はじめに

Windows上から [curl.exe](https://curl.se/download.html) を利用してJSONをPOSTする際に、
**コマンドプロンプト(cmd) 上で実行するとアクセスが成功**するのに、
**PowerShell 上で実行すると何故かエラー**になってしまう。
そんな時の回避方法をご紹介します。

# 回避方法

PowerShell 上で [curl.exe](https://curl.se/download.html) からJSONをPOSTする際は、
下記の様に **JSONの値を「シングルクォート」で囲む** とアクセスが成功します。

` curl.exe --data '"{\"id\": \"123\",\"name\": \"taro\"}"'  ～` 

` curl.exe --data シングルクォート + ダブルクォート + JSON(「"」を「\"」に置き換え) + ダブルクォート + シングルクォート`


```powershell:修正前(コマンドプロンプトだと成功するが、PowerShellから実行するとエラー)
curl.exe --data "{\"id\": \"123\",\"name\": \"taro\"}" --verbose --request POST --url "http://localhost:8080/api/test"
```

```powershell:修正後(PowerShellから実行すると成功、JSONの値を「シングルクォート」で囲む)
curl.exe --data '"{\"id\": \"123\",\"name\": \"taro\"}"' --verbose --request POST --url "http://localhost:8080/api/test"
```

# 補足

PowerShellの場合は、JSONの値を「シングルクォート」で囲まないと、` --data "{\" `と認識されてしまい、サーバに送信されるリクエストJSONが`{\`だけとなります。


または、下記の様にJSONのデータをファイル指定(` --data '@ファイルパス' `)にしても同様の動きとなります。

```powershell:JSONをファイルから読み込む
curl.exe --data '@data.json' --verbose --request POST --url "http://localhost:8080/api/test"
```

```data.json
{
    "id": "123",
    "name": "taro"
}
```

# 参考資料

https://curl.se/docs/manpage.html
【Laravel livewire】livewireが空の文字列フィールドをトリミングしてくれないのでtraitで対処

# どうしてそんな仕様なんだい。
Laravelには、リクエストで飛んできた空の文字列フィールドをnullに変換するミドルウェアが
デフォで発動するようになっています
>デフォルトでは、LaravelはアプリケーションのグローバルミドルウェアスタックにApp\Http\Middleware\TrimStringsとApp\Http\Middleware\ConvertEmptyStringsToNullミドルウェアを含めています。これらのミドルウェアは、App\Http\Kernelクラスによってグローバルミドルウェアスタックにリストされています。これらのミドルウェアは、リクエストに応じてすべての受信文字列フィールドを自動的にトリミングし、　**空の文字列フィールドをnullに変換します**　。これにより、ルートとコントローラでのこれらの正規化について心配する必要がなくなります。

参照：[Laravel 8.x HTTP Requests](https://readouble.com/laravel/8.x/ja/requests.html)

<strong>が</strong>

Livewireはトリミングしてくれません。開発者も意図した仕様のようです。
この仕様について議論されているissueがありました
[Empty nullable numeric field raises mysql exception #1950](https://github.com/livewire/livewire/issues/1950)

>「integer()->nullable()のカラムに空の値で更新しようとしたらエラーが出やがった！！何故トリミングしてくれない仕様なんだ？理由を教えてくれよ」

>『Laravelのトリム文字列ミドルウェアを使うとネットワークリクエストレベルで変更されるんだ。
チェックサムを使用してデータを保存してるからエラーが起きちゃうんだ』
『後Alpine.jsとの連携でhogehuga.....』

みたいな感じです。


<!-- 各チャプター -->
<a id="#解決策1 ミューテターを使う"></a>
# 解決策1 ミューテターを使う
モデルへ属性の値を設定しようとすると自動的に処理が動いてくれる凄い奴です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282328/5b54dae8-0d63-9d5c-4326-985d283d3cf4.png)

参照：[[Laravel] 空文字をDB登録時にnullにしたい](https://qiita.com/sgrs38/items/c14547a2ab2de49f2df0)
最近書いた過去記事です。以前はこちらで対処しました。


<a id="#解決策2 Traitを使う"></a>
# 解決策2 Traitを使う
以前に「これはバグでは」と立てられたissueです。
[TrimStrings and ConvertEmptyStringsToNull middlewares do not work on Livewire #823](https://github.com/livewire/livewire/issues/823)

その際に『このTraitを使えばいけるぜ！』と有志が作成していたTraitを共有してくれていたので、一例として引用させてもらいます。

```php:
<?php

namespace App\Http\Livewire;

trait TrimAndNullEmptyStrings
{
    public function updatedTrimAndNullEmptyStrings($name, $value)
    {
        if (is_string($value)) {
            $value = trim($value);
            $value = $value === '' ? null : $value;

            data_set($this, $name, $value);
        }
    }
}
```

```php:
<?php

namespace App\Http\Livewire\Orders;

use App\Http\Livewire\TrimAndNullEmptyStrings;
use App\Models\Order;
use Livewire\Component;

class CreateOrder extends Component
{
    use TrimAndNullEmptyStrings;
}
```

>メソッド名が``updatedTrimAndNullEmptyStrings``？？ 全ての各フィールドの値変更時に動くのは``updated``メソッドのハズだぜ？
何で``updatedTrimAndNullEmptyStrings``が全てのフィールドの変更に発動するんだ？

という疑問を抱くと思います。

trait内で``updated``というメソッド名を書いてしまうと、livewire内で``updated``を書けなくなってしまうので、
Livewireはtrait使用時にメソッド名が重複しない様に構文を定期称してくれているのです。
この場合、``updated + trait名`` と命名する事でlivewire内の``updated``の後に実行してくれます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282328/92e0cf5a-3a36-18a5-097b-40aa9672bf1d.png)
参照：[Livewire 2.x トレイト](https://readouble.com/livewire/2.x/ja/traits.html)


<a id="#おわりに"></a>
# おわりに
traitでやるのがベストなのかなぁと思います。
他に良い手段があれば教えて頂きたいです。
[個人メモ]n-gramの計算方法

# n-gram
### 辞書

```
私は社会人です。
あの本は私のものです。
彼は学生です。
学生の生活は楽しいです。
私は先生です。
```

↑によって、`私は学生です`という言葉の確率を計算するなら↓：

P(s) = P(私 | *BOS*) x  P(は | 私) x P(学生 | は) x P(です | 学生) x P(*EOS* | です)
     = (2 / 5) x (2 / 5) x (1 / 5) x (1 / 5) x (1 / 5) x (5 / 5)

### 備考
  - P(M | N): Nが確定事件という前提として、Mの確率
  - *BOS*: 文言の開始のマーク
  - *EOS*: 文言の終わりのマーク

### 参考
- https://blog.csdn.net/best_of_times/article/details/101099598
uniqとは？distinctとの違い

## uniqメソッドとは
``uniqメソッド``とは、Railsで取得した重複レコードを1つにまとめるためのメソッド。
``uniqメソッド``を実行すると、Railsは内部的にSQLのDISTINCTを実行する。

その他に、``uniqメソッド``とまったく同じ働きをする``distinctメソッド``も存在する。

両者の違いは、

・``uniq``  → Rails5以降で非推奨(または使えなくなる)
・``distinct`` → Rails5以降で正式メソッド

[![Image from Gyazo](https://i.gyazo.com/cd1b8f95d43d11acc068d9c04698082e.png)](https://gyazo.com/cd1b8f95d43d11acc068d9c04698082e)

## 使い方
```rb
モデル.select(カラム).distinct
```

## 参考記事
[【Rails入門】uniqで重複が無いモデルにしよう！distinctにも対応](https://www.sejuku.net/blog/69900)
Logic Pro Xでvocaloid5使ってるけど再生・停止とかがvocaloid5上からできなかった

# はじめに
M1 MacBook proに買い換えた
Logic Pro Xとvocaloid5とミクさんたちをインストールした
いざLogic Pro Xを開いてvocaloid5を立ち上げてvocaloid5上の再生・停止ボタンを押してみたけど再生・停止できない
logic pro Xの方のボタンではできる
スペースキーも同様

そんな状況に陥ったので解決方法を書く

# 解決方法

Logic Pro X →環境設定→オーディオをクリック

![スクリーンショット 2022-03-28 16.49.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855735/e559ca5b-5fe7-cb07-cc59-af2ee41054c2.png)

そしたらこんな設定画面が出てくる

![スクリーンショット 2022-03-28 16.49.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855735/911b93e8-1d08-78b1-c7a1-266b15657774.png)

一番下のRewireの設定をライブモードにする

![スクリーンショット 2022-03-28 16.50.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/855735/171ae814-1251-7a78-723e-0a56c46a23f1.png)

最後に適用ボタンを押す

# 結果
vocaloid5上でもスペースキーや再生・停止ボタンで操作できるようになった
HTTP通信(学習中・メモ)

・HTTPとは?
　クライアントとサーバーの間でハイパーテキストを転送するための通信プロトコルです。

・HTTPの仕組み
　クライアントがサーバーにHTMLファイル等を要求し、サーバーがクライアントに要求されたHTMLファイル等を返すことでブラウザにWebページが表示されています。
HTTPにはステートレス性という特徴があります。
*ステートレス性->過去のデータを処理結果に反映させない仕組み

![about-http.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2600460/4432828b-010a-efed-ae73-bc23195803dd.png)

クライアントがサーバーに要求することをリクエストという。
サーバーがクライアントに返答することをレスポンスという。


・HTTPリクエストの構成

リクエスト行 - リクエスト行にはWebサーバーに依頼した処理情報（メソッド、URL、HTTPのバージョン）が記載されています。

メッセージヘッダー行 - Webブラウザの情報、データのタイプ、圧縮方法、言語等が記載されています。

メッセージボディ行 - WebブラウザがWebサーバーにデータを送りたい情報がある時、使用されます。

・HTTPレスポンスの構成

ステータス行 - WebブラウザにWebサーバーでの処理結果を伝える情報が記載されています。
             WebサーバーはWebブラウザにステータスコードという3桁の数字で処理結果を伝えます。

メッセージヘッダー行 -  Webサーバーのソフトウェア情報、データのタイプ、圧縮方法等が記載されている。

メッセージボディ行 - Webブラウザに送るHTMLや画像等のデータが記載されており、Webブラウザはこれらのデータを読み取り表示している。

HTTPメソッドとは?
HTTPメソッドはHTTPリクエスト内のリクエスト行に記載されています。

HTTPメソッド名 :	用途
HEAD : ヘッダーの情報を要求するメソッド
GET	 : HTMLファイルや画像等を取得するメソッド
POST : フォームに入力したデータをサーバーに転送するメソッド
DELETE : データを削除するメソッド
CONNECT	: Webサーバーに接続するまでに別のサーバーを中継する場合に使用するメソッド
OPTIONS : 利用できるメソッドを問い合わせるためのメソッド
TRACE	: クライアントとサーバーの経路をチェックするためのメソッド

ステータスコードとは?
ステータスコードはWebブラウザからHTTPリクエストを要求すると、HTTP通信の結果をステータスコードでレスポンスするという仕組みになっています。
ステータスコードは3桁の数字で構成されている。

・100番台
  HTTPリクエストを処理中であることを示しています。

・200番台
HTTPリクエストを正常に処理したことを示しています。

・300番台
HTTPリクエストを正常に処理するため、新しいURLへ転送させる等、追加の処理が必要であることを示しています。

・400番台
HTTPリクエストを正常に処理できないことを示しています。

・500番台
Webサーバーがエラーや高負荷状態で一時的に転送できないことを示しています。

補足
・httpとhttpsの違い
httpは「データが暗号化されていない」状態になっているので第三者にこの通信データが傍受された場合には、通信内容が簡単に漏洩してしまいます。
これらを暗号化した通信の方法がHTTPSです。

・SSL
HTTPS通信において暗号化を行い、安全性を高めているのはSSLと呼ばれるプロトコルです
通信を暗号化するために使用する鍵の情報と誰がサイトの管理をしているのかというサイトの運営者情報を記したSSLサーバ証明書をインストールすることで実現しています。
URLの横に鍵のマークが出ている場合、参照しているサイトはSSLを使用していることが分かります。


間違いやアドバイスなどあれば、遠慮なく言って欲しいです。

参考文献:https://securitynews.so-net.ne.jp/topics/sec_20051.html
URDFの構造を可視化する

URDFの可視化ツールのインストールを使ってみました。
インストール方法と使い方を簡単にメモしておきます。

# インストール
``` bash
sudo apt-get install liburdfdom-tools
```

# 使い方
引数にURDFファイルを指定するだけです。
``` bash
urdf_to_graphiz foo.urdf
```
成功するとPDFファイルとGVファイルが生成されます。
（すみません、GVファイルの用途はわかりません…）

# 一例
例としてこちらの人型のURDFファイルを使わせていただきましょう。

https://github.com/robotology/human-gazebo/blob/master/humanSubject01/humanSubject01_48dof.urdf

![human-model.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159523/115f6304-0a9c-5f52-3fcc-eb3b47615154.png)

ダウンロードして引数に指定するだけです。
``` bash
urdf_to_graphiz 　humanSubject01_48dof.urdf
```
下記のようなツリー構造を示すPDFファイルが生成されました。
XML形式で読むよりは理解し易いかもしれません。

![XSensStyleModel_template-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159523/fc376dcc-4223-6bda-ede2-7dd246816da2.jpeg)
Canon製プリンター(LBP661CとLBP9510)とPCを接続する

#ドライバのインストール

## ドライバインストールから印刷エラー時の対処まで
#### はじめに
電子媒体で論文を読むこともかなり普及してきましたが，
紙媒体で読むならば，プリンターが不可欠なのは言わずもがな．
今回は弊研究室におけるドライバのインストールから印刷エラーが
生じた場合の対処法まで，蓄積されたノウハウを公開します．
プリンターは2種類を想定していますが，エラー時の対処は
他の機種でも有用かと思われますので，よろしければご一読ください．
なお，Windowsユーザーを想定しております．

**１．プリンタドライバのインストール**

プリンタを利用できるよう，パソコンの環境を整えるインストールファイル 
[LBP661C][link-1] と[LB9510C][link-2]をダウンロードしてください．
ご自身の使うOSとbit数に適したバージョンを選択してください．
Setup.exeがダウンロードされますので，クリックして実行
表示されるタブにて，
**次へ→同意する→標準セットアップ→新しいプリンターを追加する**
と進んでいきましょう．
新しいプリンターを追加するを選ぶとプリンターを検索し始めますが，
**プリンターとPCが同じネットワークに接続されているか確認してください**
例えば，PCがXXX-wifiという名前のネットワークに接続されているならば，
検索するプリンターもXXX-wifiに接続されている必要があります．
これが守られていない場合，いつまでも検索が終了しません．
つまずくステップです．

:::note info
**使用するネットワークが一致しているか確認を**
プリンターの接続されているwifiとPCの接続先wifiは一致していますか？
:::

無事にお目当てのプリンターが表示されれば，選択して追加し，
適用を押して完了です．お疲れ様でした．

[link-1]:https://canon.jp/support/software/os?pr=4924
[link-2]:https://canon.jp/support/software/os?pr=3272

**２．IPの自動更新による印刷エラー**
前日は印刷できたのに，プリンターとPCを起動したら印刷できなくなる出来事が頻発し，
何度もドライバを再インストールしていました．
終止符を打つため調べたところ，IPアドレスの自動更新が原因であると判明し，
プリンターのIPアドレスを固定し，PCの設定を変更することで改善させました．
以下にそのステップを記しておこうと思います．

１．プリンター本体の操作
まず，プリンターのIPアドレスを固定します．
環境設定からネットワーク設定→IPアドレス→自動更新をOFFに．
この後，同じくネットワーク設定→IPアドレス→IPv4から現在のアドレスを調べます．
忘れないようメモしときましょう．

２．PCでの設定
PCでは，プリンターとデバイスの設定から
該当するプリンターを選び，管理をクリックします．
タブが表れるので，ポートの設定からポートの追加を押しましょう．
するとポートの種類を選択する画面になりますので，スタンダードを選択します．
続いて，IPアドレスの入力画面が表示されます．
メモしたアドレスを入力し，検索しましょう．
成功すれば，プリンターが表示されるはずですので，あとは流れに沿って押すだけです．
このときもwifiのチェックは怠らないように．

お疲れさまでした．
[jQuery]ドラッグアンドドロップでe.dataTransfer undefinedになる

jQueryでドラッグアンドドロップ機能を作っている際、
```e.dataTransfer.files```でドロップされたファイルが取得できない問題に出会った。

# 先に結論
```e.originalEvent.dataTransfer.files```でアクセスすればいける。
jQueryのeventは独自のeventらしい。。わかりづらい
originalEventが生のjsのイベントのようです。

参考：[jQuery Event から DOM Event を取る](https://qiita.com/ygkn/items/90007451b1b7c150695d)

公式：https://api.jquery.com/category/events/event-object/


# 解決
元のコード
```元のコード.js
drag_area.on('drop', function (e) {
    e.preventDefault();
    // ...
    this.input.files = e.dataTransfer.files;
}
```

修正コード
```修正コード.js
drag_area.on('drop', function (e) {
    e.preventDefault();
    // 変更
    this.input.files = e.originalEvent.dataTransfer.files;
}
```
fetchメソッドとは

## fetchとは
fetchメソッドは引数にハッシュのキーを指定することにより、そのキーとセットになっているバリューを取り出してくれるメソッド。

## 使い方
```rb
hash.fetch(key)
```
基本的には「hash:[キー]」でバリューを取り出す動作と代わりないが、``fetch``では指定したキーが存在しない場合は例外を返す。

```rb
fruits = { "apple" => 100, "orange" => 80, "melon" => 450 }
 
puts fruits.fetch("apple")
=> 100

puts fruits.fetch("orange")
=> 80

puts fruits.fetch("melon")
=> 450
```

``fetch``の例外処理も見てみる。

```rb
fruits = { "apple" => 100, "orange" => 80, "melon" => 450 }
 
puts fruits.fetch("banana")
=> `fetch’: key not found: “banana” (KeyError)
    from Main.rb:5:in `<main>’
```

ここではハッシュに存在しないキー「banana」を指定したため例外``KeyError``が発生した。

例外処理を発生させたい場合は、以下のように``fetch``メソッドを使用する。

```rb
begin
  fruits = { "apple" => 100, "orange" => 80, "melon" => 450 }
  puts fruits.fetch("banana")
rescue => e
  puts e
end

=> key not found: “banana”
```

また、``fetch``メソッドでは第二引数にデフォルトのバリューを設定することができる。
第二引数にデフォルトのバリューを設定しておくことで、キーが存在しなかった場合は例外ではなく、指定したデフォルトの値を返す。

```rb
fruits = { "apple" => 100, "orange" => 80, "melon" => 450 }
puts fruits.fetch("banana", :"このキーは存在しません")

=> このキーは存在しません
```

## 参考記事
[はじめてのRuby！fetchメソッドの使いかたをマスターしよう！](https://www.sejuku.net/blog/58930)
【メモ】個人的に良さげな異常検知モデル、ネットワーク

# ★内容
正常画像のみで学習できるものしか実運用ではもたないと考えている。
あくまで個人的な理解。

# ★オートエンコーダ
構成図はごろごろしているので検索すればよい。

## 構成
・色々な構成があるが基本簡単なCNN
・入力　：元画像
・出力　：元画像から復元した画像（これが元画像と一致するように学習する）

## 考え方
構成の中間層を見ると、元画像より情報減っているが、出力では復元している。
⇒　画像の特徴と捉えているといえる（他のDNNに通じる捉え方）

## 異常検知
【学習】
⇒　正常画像を入力
⇒　入出力の差が小さくなるように学習する

【推論】
⇒　検査対象の画像を入力
⇒　入出力の差が小さい＝正常
　　逆に、差が大きい＝異常　と判定する（判定閾値はユーザー設定）

## いいとこ
・正常画像のみで学習できる
・考え方が分かりやすい
・ユーザーは閾値調整すればいいので現場運用しやすいのでは？
・差をとるので異常疑惑箇所のヒートマップを作成しやすそう


# ★Gaussian-AD
ネットワークというか手法に分類されるが、2020年登場、インパクトを与えた模様。

## 構成
・GoogleのEfficientnetを使ってる
　しかも、Imagenetで学習済みのものを利用しちゃう（転移学習！）
・普通にお尻の出力を使わずEfficientnetの中間層の出力たちを“特徴ベクトル”と名付けて取り出して使う（新手法？横から取り出す）

## 異常検知、どうやっているか？
【学習】
⇒　正常品画像の特徴ベクトルを学習
⇒　各層の平均と分散を求める（ガウス分布していると仮定。仮定なので本当かは不明？）

【推論】
⇒　検査対象の画像を入力し、学習した平均との差を求める
⇒　各層について、学習した平均からどの程度外れているか計算
⇒　各層のはずれ具合を結合、マハラノビス距離を求める
⇒　マハラノビス距離から異常か判定する

※マハラノビス距離はカイ二乗分布に従うことから、閾値も理論的に算出可能とのこと。これはガウス分布の前提から来ている？

## いいとこ
・正常画像のみで学習できる
・ユーザーがマハラノビス距離の判定閾値で調整できる
・異常に少ないサンプル数で精度が出るとのこと

## 疑問
・異常疑惑箇所のヒートマップ出せるか？