OmniAICreator/Qiita-1.07M huggingface dataset:
Next.js+TailwindにGoogleフォントを追加する

## 完成コード

```css:styles/globals.css
@import url('https://fonts.googleapis.com/css2?family=Reggae+One&display=swap');

@tailwind base;
@tailwind components;
@tailwind utilities;
```

```javascript:tailwind.config.js
module.exports = {
  purge: [],
  darkMode: false, // or 'media' or 'class'
  theme: {
    extend: {
      // フォントを追加
      fontFamily: {
        reggae: ['Reggae One'],
      },
    },
  },
  variants: {
    extend: {},
  },
  plugins: [],
}
```

```javascript:about.jsx
import React from 'react'

const About = () => {
  return (
    <>
      <div className="font-reggae">
        About Page.
      </div>
    </>
  )
}

export default About
```

## 解説

Tailwind CSSのフォント設定をカスタマイズします。

[Googleフォント](https://fonts.google.com/specimen/Reggae+One)にアクセスし、任意のフォントの@importを取得します。

![cc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166484/612d62bc-8481-1031-f50d-0f203eb07ae1.png)

Next.jsのglobals.cssに@importを追加します。

```css:styles/globals.css
@import url('https://fonts.googleapis.com/css2?family=Reggae+One&display=swap');
```

tailwindにフォント設定を追加します。

```javascript:tailwind.config.js
module.exports = {
  theme: {
    extend: {
      // フォントを追加
      fontFamily: {
        reggae: ['Reggae One'],
      },
  // ...
```

tailwindの書式でフォントを適用します。

```javascript
<div className="font-reggae">
  About Page.
</div>
```

フォントが無事適用されました。

![a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166484/1fb31fab-4a63-a940-b4b5-9513d8a4705b.png)


## 参考

- https://tailwindcss.com/docs/configuration
- https://nextjs.org/docs/basic-features/built-in-css-support
JavaScript/TypeScript async関数を順番に実行したい

# 非同期関数をそのまま実行した場合

## コード

sleepで最初に実行した関数ほど遅く終了させます

```ts
const sleep = (time: number) => new Promise((resolve) => setTimeout(resolve, time));

const test = async (v: number) => {
  await sleep(100 - v * 10);
  console.log(v);
};

for (let i = 0; i < 10; i++) {
  test(i);
}
```

## 実行結果

当然のごとく、実行順序が入れ替わります

```console
9
8
7
6
5
4
3
2
1
0
```

# 直列実行させた場合

## コード

いろいろなやり方がありますが、ライブラリを使わない書き方でおそらく一番簡潔な方法です

```ts
const sleep = (time: number) => new Promise((resolve) => setTimeout(resolve, time));

const test = async (v: number) => {
  await sleep(100 - v * 10);
  console.log(v);
};

let p: Promise<void> | undefined;
for (let i = 0; i < 10; i++) {
  p = p?.then(() => test(i)) || test(i);
}
```

## 実行結果

次に実行する関数をPromise.thenで指定しているので、順番通りに実行されます

```console
0
1
2
3
4
5
6
7
8
9
```
ministaのwebpack設定が上書きできるようになったからオレオレwebpack公開する

ReactからHTMLを生成する静的サイトジェネレーターのministaを試してみました。
[Zennでの紹介ページ](https://zenn.dev/qrac/articles/7537521afcd1bf)

素晴らしいnpmを開発されたクラクさんはこちら([@Qrac_JP](https://twitter.com/Qrac_JP))

[追記]alias追加しました！

## 事前準備

```bash:bash
# 追加
yarn add import-glob-loader --dev
```
## webpack追加

```js:webpack.config.js
const webpack = require('webpack')

const webpackConfig = {
  module: {
    rules: [
      {
        test: /\.(sass|scss)$/,
        use: ['import-glob-loader']
      }
    ],
  },
  // import React　from 'react'書かなくていい！
  plugins: [
    new webpack.ProvidePlugin({
      React: 'react'
    }),
  ],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  },
}

module.exports = webpackConfig

```

これでsassのまとめてインポートができます。やったね！

```scss:index.scss
/* ~ 略 ~ */

@import "./foundations/**/*";
@import "./layouts/**/*";
@import "./objects/**/*";
```


JSXの相対path地獄もこれで改善

```js
import AppLayout from "../../components/app-layout"
↓
import AppLayout from "@/components/app-layout"
```


ちなみにstyled-componentsを採用しようと思ったんですが、ビルドうまくいかなかったです。。gununu
【PUG】Pugファイルで宣言した変数を別ファイルのJSで読み込む

Pugファイルの中で宣言した変数

```
- const sample = #{sampleFromServer}
```

これを別ファイルのJavaScriptで使いたいという場面は多々あるのではないでしょうか？
よくある方法として下記を見受けます。

```
// PUGファイル内でidをキーにdataを持つ
script(src='sample.js' id='sampleValue' data=#{sampleValueFromServer})

// JavaScriptファイル内で読み込む
const param = JSON.parse(document.getElementById('sampleValue').getAttribute('data'));
```

ですが、これでは少し複雑すぎる気がします。
もっと簡単にしましょう。

```
// PUGファイル内で変数を宣言
script.
  const sample = !{JSON.stringify(sampleValueFromServer)};

// 変数宣言より後でJavaScriptファイルを読み込む
script(src='./path/to/file.js');

// JavaScriptファイル内で使う
console.log(sample);
```

これだけで十分です。
ただ、どちらの方法も開発者モードでソースを確認すると表示されてしまうので取り扱いには注意するようにしましょう。
作業の記録｜はてなテンプレート

## はじめに（この記事の背景）

はてなブログを書くにあたって、作業開始時のハードルを下げるためにテンプレートをつくり、コピーしてつかっている。

ちょこちょこいじっているが、しばらくこれで安定しているので、記録として残しておくと良い気がした。

## 環境

ここで書いていることは、下記のバージョンで実施しました。

- Obsidian v0.12.5
- macOS BigSur 11.4


## テンプレート

下記のテンプレートを、[お気に入りのツールであるObsidian][01]に入れて使っている。

```

作業ログ｜

[:contents]

---

## はじめに（コメント）

<!-- more -->

## やったこと

## できたこと、できなかったこと

## これからやりたいこと

## 表スケルトン

|   |   |   |
|---|---|---|
|   |   |   |


## リソース（参考サイト）

[01.][01]
[02.][02]
[03.][03]

[01]: 
[02]: 
[03]: 

## 環境

- Windows 10 Home バージョン 20H2 OSビルド 19042.804
- Parallels Desktop for Mac Home Edition バージョン 15.1.5 (47309)
- macOS BigSur 11.4
- MacBook Pro (13-inch, 2020, Four Thunderbolt 3 Ports)

```




### ポイント補足

- 画像サイズを調整するhtmlタグがすぐにわからなくなるので、テンプレートに入れてある。

- そのほか、忘れそうなやつ・めんどうくさいやつ（表とか）はテンプレートに入れてある。

削除するのは簡単なので……。

## リソース

[Obsidian｜使いかたの記録（目次）][01]

[01]: https://qiita.com/hann-solo/items/ac8fa75394813e8adcf4
作業の記録｜Qiitaテンプレート

## はじめに（この記事の背景）

Qiitaの記事を書くにあたって、ハードルを下げるためにテンプレートを作成している。

ちょこちょこいじっているが、しばらくこれで安定しているので、記録として残しておくと良い気がした。

## 環境

ここで書いていることは、下記のバージョンで実施しました。

- Obsidian v0.12.5
- macOS BigSur 11.4


## テンプレート

下記のテンプレートを、[お気に入りのツールであるObsidian][01]に入れて使っている。



```
## はじめに（この記事の背景）



## 環境

ここで書いていることは、下記のバージョンで実施しました。

- iOS 14.4
- Xcode 12.4
- macOS BigSur 11.3.1


## 

<img src="" width=45%>

## リソース


[01.][01]
[02.][02]
[03.][03]

[01]: 
[02]: 
[03]: 

```

### ポイント補足

画像サイズを調整するhtmlタグがすぐにわからなくなるので、テンプレートに入れてある。

そのほか、忘れそうなやつはテンプレートに入れてある。

## リソース

[Obsidian｜使いかたの記録（目次）][01]

[01]: https://qiita.com/hann-solo/items/ac8fa75394813e8adcf4
Next.jsとRustのTypeScriptなWebAssembly生活

# なんかwasmを直読み出来るんだけど

Webpackがwasmの読み込みをサポートしているので、Next.jsでもちょっと設定を追加するだけで動作が確認出来ました
TypeScriptの定義まで自動で行われるので、連携する上で迷うところがありません

今回作成したコード
https://github.com/SoraKumo001/next-wasm

## RustでWebAssemblyの生成

### Rustのインストール

https://www.rust-lang.org/tools/install
これが無いと話にならないので、とりあえずインストールする必要があります

### wasm-packのインストール

以下を実行してwasm-pack入れるとRustでWebAssemblyが簡単に生成できるようになります
`cargo install wasm-pack`

### 必要なコード

```toml:Cargo.toml
[package]
name = "wasm"
version = "0.1.0"
edition = "2018"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2.74"
```

```rust:src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn sums(value: i32) -> i32 {
    let mut a: i32 = 0;
    for i in 1..value+1 {
        a += i;
    }
    a
}
```

1から任意の値までの合計を計算するプログラムです
RustからWebAssemblyを使う場合、あんまりゴチャゴチャ設定を書く必要はありません

### コンパイル

`wasm-pack build`

以上のコマンドでWebAssemblyが出力されます

### 出力ファイル

以下の6つのファイルが生成されます

```bash
.gitignore
wasm.d.ts
wasm.js
wasm_bg.js
wasm_bg.wasm
wasm_bg.wasm.d.ts
```

### 必要なファイル

Next.jsから利用するときに必要なのは、以下の2つのファイルだけです
TypeScriptの定義も含まれています

```bash
wasm_bg.wasm
wasm_bg.wasm.d.ts
```

## Next.js側

### 環境を整える

必要最低限のパッケージをインストールします

```bash
yarn init -y
yarn add next react react-dom
yarn add -D typescript @types/node @types/react
```

### WebPackの設定

本来はasyncWebAssemblyをtrueにするだけで良いはずなのですが、webassemblyModuleFilenameの設定をいじらないと`next dev`では動くものの`next build`でエラーになります

```next.config.js
module.exports = {
  webpack: (config, { isServer }) => {
    config.experiments = {
      asyncWebAssembly: true,
    };
    config.output.webassemblyModuleFilename = (isServer ? '../' : '') + 'static/wasm/webassembly.wasm';
    return config;
  },
};
```

### Next.jsからwasmを呼び出すコード

なんとwasmを直接import出来ます
TypeScriptの定義も自動生成されているので、何事も無かったかのように普通に呼び出して使えます

```src/pages/index.tsx
import { sums } from "../lib/wasm_bg.wasm";
import { useState } from "react";

const App = () => {
  const [value, setValue] = useState(0);
  return (
    <div>
      <input
        onChange={(e) => {
          const v = Number(e.target.value);
          !isNaN(v) && setValue(sums(v));
        }}
      />
      <div>{value}</div>
    </div>
  );
};

export default App;
```

### 動作確認

- コマンド  
`next dev`
- 確認  
<http://localhost:3000/>


WebAssembly化したRustの関数から値が返ってきています

![シーケンス 03.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267629/9cbb5b14-e5eb-d78f-7911-77e4c8da56fd.gif)


## まとめ

ヤバいほど超簡単にWebAssemblyが使えます
wasmの中身がよく分かっていなくてもTypeScriptの定義があるので、何の違和感も無く使えます
Rustのソースを監視してファイル更新時にwasm-packを実行すると、さらに開発効率が上がりそうです

今後、WebAssemblyでスレッドサポートが標準化したら、そのパワーを生かした様々なライブラリが作られることが予想されます
そんな時代が来たらWeb開発の手法が大きく変わっていくかもしれません
CORSについて知る

# CORSとは

CORSとは、オリジン間リソース共有（Cross-Origin-Resource-Sharing）のことで、読み方は一般的に「コルス」。
コンテンツを読み取ることを許可しているオリジンを、サーバがクライアントに共有する機能。

オリジンとは、ホスト（ドメイン）やスキーム（プロトコル）、ポート番号のこと。
詳しくは下記から↓

https://developer.mozilla.org/ja/docs/Glossary/Origin


そもそも、ウェブのセキュリティにおいて同一オリジンポリシーが存在し、あるオリジンから別のオリジンにアクセスできないよう制限されている。
詳しくは下記から↓

https://developer.mozilla.org/ja/docs/Web/Security/Same-origin_policy

つまり、CORSとは、アクセス先のオリジンがアクセス元のオリジンに対して、アクセス許可を与える際に使われる。

# 注意点！

## CORSはサーバ（バックエンド）での設定が必要！

CORSエラーが発生して解決策見つけるためにググると、
「HTTPヘッダーに設定すれば動くようになるよ」
という情報が散見される。
これを取り違え、クライアント側のリクエストヘッダーに設定しても期待通りに動くことはない。
前述した通り、CORS設定はサーバ側の問題であり、HTTPヘッダーというのは、レスポンスヘッダーのことであるからだ。

## CORSはあくまで各ブラウザが勝手に対応しているだけ！

例えば、PostmanからCORS設定をしていない（＝同一オリジンからのみアクセスを許可している）APIを叩いてみる。
この場合、リクエストは問題なく送れてしまう！
というのも、そもそもCORSは各ブラウザが勝手に対応しているものであるからだ。

つまり、ブラウザの仕様によってこの辺の動きが異なってくる（※1）し、CORS設定はAPIの脆弱性対策には大して効果がないことがわかる。

※1：CORSというかオリジンの話だが、IEでは同一オリジンの確認にポート番号を含まない、といった例が挙げられる。

# 参考
## MDNの定義は目を通しておく価値あり
- [オリジン間リソース共有 (CORS) - HTTP | MDN](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)
- [同一オリジンポリシー - ウェブセキュリティ | MDN](https://developer.mozilla.org/ja/docs/Web/Security/Same-origin_policy)
- [Origin (オリジン) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN](https://developer.mozilla.org/ja/docs/Glossary/Origin)

## わかりやすい日本語の参考記事
- [CORSまとめ - Qiita](https://qiita.com/tomoyukilabs/items/81698edd5812ff6acb34)
- [Same Origin Policy（同一オリジンポリシー）とCORS（オリジン間リソース共有） - Qiita](https://qiita.com/tkek321/items/bd36b1541d22c8645eb9)

## 英語に抵抗なければこちらも目を通したい
- [Understanding Cross-Site Resource Sharing (CORS)](https://academind.com/tutorials/cross-site-resource-sharing-cors/)
Next.jsでページ間でデータを共有する方法

## 🍣 コード

```javascript:components/DataContext.js
import React, { createContext } from 'react'

export const DataContext = createContext()
```

```javascript:_app.js
import '../styles/globals.css'
import { DataContext } from '../components/DataContext'

function MyApp({ Component, pageProps }) {
  const sushi = { name: 'ikura', price: 1000 }

  return (
    <DataContext.Provider value={sushi}>
      <Component {...pageProps} />
    </DataContext.Provider>
  )
}

export default MyApp
```


```javascript:about.jsx
import { useContext } from 'react'
import { DataContext } from '../components/DataContext'

const About = () => {
  const sushi = useContext(DataContext)
  console.log(sushi)

  return (
    <>
      About Page.
    </>
  )
}

export default About
```

## 🍣 解説

ページ間でデータを共有するには、[React Context](https://ja.reactjs.org/docs/context.html)を使用します。

まず`createContext`を使用し、コンテキストを作成します。

```javascript:components/DataContext.js
export const DataContext = createContext()
```

次に、データを共有するページをコンテキストのプロバイダを使用しタグで包みます。

```javascript:_app.js
// ...
  return (
    <DataContext.Provider value={sushi}>
      <Component {...pageProps} />
    </DataContext.Provider>
  )
// ...
```

最後に、各ページで`useContext`を使用しコンテキストを呼び出します。

```javascript:about.jsx
import { useContext } from 'react'
import { DataContext } from '../components/DataContext'

const sushi = useContext(DataContext)
console.log(sushi)
```

## 🍣  参考

- https://ja.reactjs.org/docs/context.html
esbuildでGASのコードをビルドする

モジュールバンドラーを見つけると「GAS向けのコードを作れるか」と考えてしまうようになりました。
というわけでesbuildプラグインを作ったので紹介します。

https://github.com/mahaker/esbuild-gas-plugin

## esbuild

[爆速](https://www.kabuku.co.jp/developers/ultrafast-tsx-build-tool-esbuild)と話題ですね。
ちなみにプラグインAPIはexperimentalなので、今後の動向は気にかけていきたいと思います。

https://esbuild.github.io/plugins/

```
The plugin API is new and still experimental. 
It may change in the future before version 1.0.0 of esbuild as new use cases are uncovered. 
```

## 「GAS向けのコード」とは

esbuildでビルドしたコードをスクリプトエディタにコピペしても、関数を選択できませんでした。
この記事中にあるように `グローバルオブジェクトに関数宣言文で関数を定義`する必要があります。

https://qiita.com/fossamagna/items/7c65e249e1e5ecad51ff#google-apps-script-%E7%89%B9%E6%9C%89%E3%81%AE%E5%AF%BE%E5%BF%9C

しかも、関数宣言文は[gas-entry-generator](https://github.com/fossamagna/gas-entry-generator)で生成できます（すごい）

## esbuildプラグイン

作ったプラグインでは

1. ビルド後のソースに対して[gas-entry-generator](https://github.com/fossamagna/gas-entry-generator)を実行して、関数宣言文を生成
1. ビルド後のソースの先頭に1.を追記

をやってます。
大したことはやってないですね。

追記) Denoにも対応しています！
      [こんな感じ](https://github.com/mahaker/esbuild-gas-plugin#deno)で使ってみてください！

## まとめ

だいぶ雑な実装ですが、実務のコードでも使っていて今のところ問題はないです :thumbsup: 
ぜひ使ってみてください！

`appsscript.json`を自動でコピーしてくれる機能とかも追加していこうと思っています！
WSL2でUbuntu(20.04.2 LTS)にAWS SAMをインストールする

# はじめに
こんにちは。新しいPC買ったにもかかわらず、古いPCにいろいろ入れて、あとの仕事を増やしている筆者です :innocent: 

移行しようにも、作業するにはまるまるもっていかないとなので重い腰があがらず...
:bow:

さて、今回はWindowsマシンでWSL2でLambdaの動作確認のために**AWS SAM**のインストールをしていこうと思います。

# WSL2(20.04.2 LTS)にAWS SAMをインストールする
公式ドキュメントを参考に進めていきます。

https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install-linux.html#serverless-sam-cli-install-linux-sam-cli

## 1. zipファイルをダウンロードします。
wgetでもcurlでもお好みで :pray:
(余談ですが、結構302でリダイレクトされます :pray:)

```sh
$ wget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip
```

## 2. ハッシュ値確認
以下コマンドで生成されたハッシュ値がGitHubのリリースノートに記載のものと一致すればOKです :ok_hand: 

```sh
$ sha256sum aws-sam-cli-linux-x86_64.zip
```

https://github.com/aws/aws-sam-cli/releases/tag/v1.24.1

## 3. zipファイルを解凍します

```sh
$ unzip aws-sam-cli-linux-x86_64.zip -d sam-installation
```

## 4. インストール

```sh
$ sudo ./sam-installation/install
```


## 5. バージョン確認
インストール完了です :v:

```sh
$ sam --version

SAM CLI, version 1.24.1
```

# おわりに
何事もなくインストールができました :smile:
それでは！
アロー関数を使わない記法

### アロー関数を使わない記法

今日勉強したことを記録に残しておこうと思って記事にしました。
CodeMafiaさんのUdemyのコースを受講しています。SassやCSSのアニメーション
Java ScriptなどProgate終わったあとにフロントエンドの勉強をしたいと思っている人には
最適なコースなのでおすすめです。
***
ご興味ある方は下記のリンクからどうぞ！
[Code Mafiaさんのコース](https://www.udemy.com/user/codemafia/)

早くMarkdown記法にもなれていきたいです☺️
***

###アロー関数を使わない記法で書いた場合
```javascript 
const hello = function(name){
  console.log('hello' + name);
}

hello('code mafia');
hello('code mafia2');
hello();
```

- const hello = function (name = 'Tomo') とデフォルト値を設定してあげることも可能

### アロー関数を使った場合
```javascript 
const hello = name => console.log(name);

hello('code mafia');
hello('code mafia2');
hello();
```

#### 戻り値が設定してある場合
```js
const hello = (name, age) => {
  return 40;
}
```
アロー関数を使うと

```js
const hello = (name, age) => 40;
```
と記述することができる。

```js
const arry = [1,2,3,4,5,6];

arry.forEach(value => console.log(value));
```
と配列の値をそれぞれ出すことができる。少し見慣れなかもしれないけど
書いていくうちに慣れていきたいと思う
CakePHP4でログイン認証のテストする時にセッションの取得できなくてハマった

CakePHP4でログイン認証のテストを書こうとした時に、セッションが取得できなくてハマったのでメモ

## やりたかったこと（失敗コード）

### UsersController

```php
    public function login()
    {
        $this->request->allowMethod(['get', 'post']);
        $result = $this->Authentication->getResult();

        // 認証成功
        if ($result->isValid()) {
            $target = $this->Authentication->getLoginRedirect() ?? '/';
            return $this->redirect($target);
        }
        // ログインできなかった場合
        if ($this->request->is('post') && !$result->isValid()) {
            $this->Flash->error('ログインIDかパスワードが間違っています');
        }
    }
```
### UsersControllerTest
```php
    public function testLoginOk(): void
    {
        $this->enableCsrfToken();   // CSRFトークン生成

        $this->post('/users/login', [
            'username' => 'misato', // idが2のユーザー
            'password' => 'misato',
        ]);

        // セッションのユーザーidが2であることを確認
        $this->assertSession(2, 'Auth.User.id');
    }
}
```

### テスト結果
セッションが見つからなくて、失敗する

```bash
There was 1 failure:

1) App\Test\TestCase\Controller\UsersControllerTest::testLoginOk
Failed asserting that 2 is in session path 'Auth.User.id'.
```

## 【改善案】 テストコードは$_SESSIONを使う
### UsersControllerTest

```php
    public function testLoginOk(): void
    {
        $this->enableCsrfToken();   // CSRFトークン生成

        $this->post('/users/login', [
            'username' => 'misato',
            'password' => 'misato',
        ]);

        $this->assertEquals(2, $_SESSION['Auth']->id);
    }
```
これなら、テスト通る！！

```bash
OK (1 test, 1 assertion)
```

## 【おまけ】PHPUnitはリクエストごとにセッションがリセットされる
PHPUnitは同じテストメソッド内でも、リクエストごとにセッションがリセットされる仕様のようです。
そのため、セッションを引き継ぎたいときは、明示的にセッションを引き継がせる必要があります。

具体的には以下のような感じです

### UsersController
```php
    public function home()
    {
        $session = $this->getRequest()->getSession();

        if (empty($session->read('works'))) {
            $session->write('works', 'on');
        } else {
            $session->write('works', 'off');
        }
    }
```
### UsersControllerTest

```php
    public function testHome(): void
    {
        $this->get('/');
        $this->assertSession('on', 'works'); //ここは成功します。

        $this->session($_SESSION); // セッションを引き継ぐ

        $this->get('/');
        $this->assertSession('off', 'works'); //セッションを引き継がないと、ここで失敗
    }
```

## 参考
- [テスト - 4.x](https://book.cakephp.org/4/ja/development/testing.html)
- [認証によるテスト - 2.x](https://book.cakephp.org/authentication/2/ja/testing.html)
- [CakePHP3で認証処理のテストケースをつくる - Qiita](https://qiita.com/frost_star/items/2d4fe1641c2f26ae67b1)
- [CakePHP - CakePHP3 PHPunit テスト時のセッションを保持したい｜teratail](https://teratail.com/questions/60263)
- [cakephp3でのphpunitの備忘録 - Qiita](https://qiita.com/ota40264/items/29e231e7e6556ccc5465)
自分のPCにログインできなくなって超焦った話

### Windows11が発表になりましたね！

**ず～っとWindows10じゃなかったの...？**
...とか色々と思う所はありますが(※1)、きっと皆さんも自分のPCがWindows11へのアップデート対象か気になりましたよね！？

※1 「**アメリカ人の永遠は5年ぐらい**なんだよきっとっ！！」と、娘からは偏見に満ち満ちた感想を貰いました(^^;　お父さんは人/法人によると思うよ？(^^;

### ここで前提

私のPCでは、ローカルアカウントでログインしています。

### 「PC正常性チェック」ツールでアップデートできるか確認すると...

MS謹製ツール「PC正常性チェック」(※2)でアップデート対象か確認できると聞いて、早速試してみました！

※2 敢えてリンクは張りません...

![20210629_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954879/3362e554-7769-572a-f340-56b5036290fb.png)

が～ん(>_<)

### 何がダメなのか？...と弄り始めたのが運の尽き

買って数年経ったとはいえ現役稼働中のIntel Core i7-7700機がNGとは辛い...

で、システム要件やらを調べているうちに、↓の「MSアカウントでサインイン」していないからNGって出るのでは...？

![20210629_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954879/291f8291-417f-8e36-0355-8454a96ad2e2.png)

...と思って**サインインしてしまったのが運の尽きでした(>_<)**

### その時は気付かなかった...

サインイン後に改めてチェックを走らせるも、やっぱりNG(\*_\*)
その日は諦めてPCをシャットダウン、その翌日に悲劇に見舞われたのです！

### 朝のログイン画面で

翌日の朝、普通にPCを起動、ログインしようと思ったら**アカウントが↑のMSアカウントに**なっていました。

そして**パスワード覚えてない...！**

MSアカウントのメアドを、普段使いのメアドとは違うものにしていたのが悲劇を加速。
パスワードリセットも、そもそもPCにログインできないと受信したメール読めないし...
普段使いじゃないから他メアドへの転送もしていない...
パスワードのメモもPCの中...

色々なパスワードを試しましたが、ダメ...(><)
ローカルアカウントでのログインも無理そう...(><)

そもそも↑で**PCログインのアカウントまで変わるとは夢にも思ってなかった**し...！

そしてアカウントの復元依頼を試すも(※3)、

> お客様の Microsoft アカウント ***** の復元依頼をいただいておりますが、誠に残念ながら、弊社システムの判断により、この度お客様から提供いただいた情報からは、お客様が対象アカウントの所有者様であることを確認できませんでした。

**詰んだ／(^o^)＼**

※3 「確認できませんでした」という判断そのものには不満はありません。確認されてしまう方が、簡単に乗っ取られそうで怖い(^^;

### そして...一筋の光明！？

そこでふと浮かんだのがMSアカウントのメアド、プロバイダのブラウザでのメール閲覧機能で読めるのでは...！？

...で試した所、**何とかパスワードリセットに成功(T_T)**

**無事にログイン出来ました！**

### 教訓

- パスワードのメモ、PCの中だけじゃイザという時に困る
- 連携機能とか、気軽に使ったらハマる

そしてログインはローカルアカウントに速攻で戻しました...

### 参考：要件チェックのオススメ

MS謹製ツールの欠点は、**どこが**ダメなのかが判らない所(バージョンアップしたらしいですが試してないです)。

こちらの記事のツールでは、細かく判定してくれました。

https://www.nichepcgamer.com/archives/how-to-use-whynotwin11.html

因みに自宅PCは、CPU「Core i7-7700」が対応する/しないが不明という判定でした(泣)
できるだけ理屈でデザインを説明する - ランダムな配置を演出する

## これは何

- 「感覚」とか「センス」で語られがちなデザインをできるだけ理屈に基づいて説明してみる試みの記事
- 対象読者
    - これからデザインにも手を出してみたいエンジニア
    - 既にデザインもやっているけど、良い悪いの判断に自信が持てないエンジニア
- また、記事投稿イベント「[3000文字Tips - 知ると便利なTipsをみんなへ届けよう](https://qiita.com/official-events/d523df99d6479293ffa7)」への投稿記事でもあります

## ランダムな配置を演出する難しさ

ランダムを演出する、変な言葉ですね。
しかし「適当に配置する」と「ランダムに見えるように配置する」は結構違うのです。

例えば「矩形の中にランダムに四角形を配置してください」というオーダーがあったとき、割と陥りがちなのがこういう状態。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/e8de0602-0308-8b18-6c7a-92762c88c9ed.png)

ランダムに並べたつもりがいつのまにか四角形同士の距離がほぼ等間隔になってしまうんですね。

適当に配置しているつもりでも、強く意識しないと「空いている箇所に配置する」動きをしがちなので、結果的にほぼ等間隔になってしまうのが人間の性です。

## 意図的に粗密を作る

ランダムに配置しようとして等間隔になってしまっている画は、（強い言い方になってしまいますが）表情が乏しめ。
グリッドシステムを採用するならする、ランダムを演出するならする、どちらかにした方が見た目が良くなる可能性が高いです。

というわけでランダムを演出するための方法ですが「意図的に粗密を作る」のがポイントです。

いきなり全ての要素を置かず、最初は3つの四角だけ配置してみました。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/dbc575df-a22b-3f8c-78b7-535496356655.png)

この時点で、正三角形や二等辺三角形にならないように配置してあげましょう。

次に、2つだけ四角を足します。
良い例とイマイチな例を出してみました。

| 元の状態 | 良い例 | イマイチな例 |
| --- | --- | --- |
| ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/dbc575df-a22b-3f8c-78b7-535496356655.png) | ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/8b8d2765-3572-22a3-dafe-a07b9be15ac2.png) | ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/96e352d5-8ce1-cf36-ec2a-41f2449d5d73.png) |

たった2つの追加だけなのにイマイチな例は急にランダムっぽさが消えているのがお分かりでしょうか？

**そうはならんやろ**と思われるかもしれませんが、現実では**なっとるやろがい**と言わざるを得ない場面が多いです。
見ての通り、5つの四角形同士の距離がほぼ同じなのが大きな原因です。

対して、良い例では極端に距離の差をつけています。
左上のゾーンはスカスカで、ともすれば不安になるかもしれません。
しかしこれくらい思い切りをつけないとランダムっぽさは演出できないのです。

まとめると、新たに要素を配置するときには以下を気にしましょう。

- 周りの要素と
    - 等距離に配置していないか
    - 辺が揃いすぎていないか

こんな具合に要素を少しずつ追加していきます。

| 少し足す | もう少し足す | 更に足す |
| --- | --- | --- |
| ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/806997d5-0b4d-b930-20ba-803c13f40da3.png) | ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/4ff8cbb1-590e-2cfc-1016-c2d7f9920b76.png) | ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/ba4ac8c2-5220-a45d-f442-9ed67746dfd1.png) |

## はみ出させる

現段階で、配置している四角の数は両者同じなのですがだいぶ「ランダムっぽく」見える度合いが違うのではないでしょうか。

| 最初に出したもの | 配置に気をつけたもの |
| --- | --- |
| ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/e8de0602-0308-8b18-6c7a-92762c88c9ed.png) | ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/ba4ac8c2-5220-a45d-f442-9ed67746dfd1.png) |

更にもう少しランダムに見せる工夫として「枠からはみ出させる」があります。
人間、ついつい枠の中に収めてしまいがちなのですがいくつか飛び出ちゃってる方がよりランダムっぽいです。

ですから、枠の外側に近いものは外に出しちゃいましょう。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/68985b89-fea5-02ae-08fc-cdaf105b3aca.png)

枠の外側に出すのは、ランダムを演出する以外にも「更に外側に空間が広がっている」ように見せられるため、汎用的に使えるテクニックだと思います。

もう一度比較しておきましょう。

| 最初に出したもの | 配置に気をつけて外側にもはみ出させたもの |
| --- | --- |
| ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/e8de0602-0308-8b18-6c7a-92762c88c9ed.png) | ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/68985b89-fea5-02ae-08fc-cdaf105b3aca.png) |
エクセル用のNPSスコア計算式

## NPS の計算方法おさらい

基本的な計算方法はこのように

```
NPSスコア = Pの割合 - Dの割合
```

を出すことです。

P = プロモーター、推奨者。NPS質問の回答が 10, 9 の人たち。
D = デトラクター、批判者。NPS質問の回答が 0, 1, 2, 3, 4, 5, 6 の人たち。
N、つまり「ニュートラル、中立者」（PとDのどちらでもない人、NPS回答が 7, 8 の人たち）は、NPSスコアの計算に入れません。

## 数式の説明

やっていく計算は：

- 全回答の件数を算出
- P としてカウントする回答の件数を算出し、全体の中の割合を計算
- D としてカウントする回答の件数を算出し、全体の中の割合を計算

## 数式

簡単ではあるが、以下のとおりです。
前提： NPS回答データが A 列にあり、回答データのシート名は「回答データ」である。

```
=(COUNTIF(回答データ!A:A,">=9")/COUNTIF(回答データ!A:A,">=0"))-(COUNTIF(回答データ!A:A,"<=6")/COUNTIF(回答データ!A:A,">=0"))
```
備忘録: Doctrine Migration のマイグレーションスクリプトを手元でデバッグする時 

# 何をしたいのか

マイグレーションスクリプトをデバッガーで確認したい。

# コマンド

```
php bin/console doctrine:migrations:execute '*****'  --dry-run
```

この時、更に ```-n``` または ``` --no-interaction``` をつけると毎回 prompt されずに済む。

***** は、マイグレーションのバージョンIDを指定。

マイグレーションIDはスクリプトのクラス名から取ってくるか、

```
php bin/console doctrine:migrations:status --show-versions
```

の一覧から取得可能。
文字列を辞書型に変換(EdgeのF12のリクエストヘッダー向け)

# ConvertFrom-StringData
`ConvertFrom-StringData $text`で「name=value」の形式で書かれた文字列を辞書型に変換できます。

# Edgeで表示されるリクエスト/レスポンスヘッダへの応用
正規表現で「name=value」になるように変換します。

````powershell
$text -replace "(\r\n|^)(.*):", "`r`n$2="
````

PowerShellだとバッククオートでエスケープしますが、正規表現ではバックスラッシュです。

^は処理単位の先頭を表すらしいですが、今回のケースだと行頭を意味しないので\r\nで行頭を示しました。

また、素で書くと「foo-bar」のようなハイフンを含むキーは「"foo-bar"」としなければなりませんが、ConvertFrom-StringDataで処理する場合はいらないようです。
連動選択リストを考慮した入力規則

Caseを使うんだ。勉強になった。

[Validation Rule Help](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000BcvZ0SAJ)

```
AND(
TEXT( Status ) = "Closed",
	OR(
	ISBLANK(TEXT( Picklist_A )),
	AND(
	CASE( Picklist_A ,
	"Option-A",1,
	"Option-B",1,
	"Option-C",1,
	0 ) = 1,
	ISBLANK(TEXT( Picklist_B )),
	)
	)
)
```

[Help with Validation Rule for dependent picklist](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000Dcot6)
Gmail削除スクリプト備忘録

指定したラベルやカテゴリーのメールを、
指定期間でゴミ箱に移動する。
GoogleのAppsScriptにて作成。


```JavaScript:code.gs

function deleteOldGmails() {

  //ラベル・カテゴリ名
  var queries = [
    'label:ラベル１',
    'label:ラベル２',
    'label:ラベル３',
    'label:ラベル４',
    'category:social',     //ソーシャルタブ
    'category:promotions', //プロモーションタブ
  ];

  //削除期限
  var day = [
    '1d',   //ラベル１
    '2d',   //ラベル２
    '1d',   //ラベル３
    '3d',   //ラベル４
    '1d',   //ソーシャルタブ
    '2d',   //プロモーションタブ
  ];


  //forで回して削除していく
  for (var i = 0; i < queries.length; i++) {

    var query = queries[i]; //ラベル名の取得
    var num = day[i];       //期限の取得
    Logger.log('削除開始' + query);

    //num日以上たった、星・重要のついてないメールを既読未読関係なく消す
    var criteria = '' + query + ' older_than:' + num + ' -is:starred -is:important is:read -is:read';
    var deleteThreads = GmailApp.search(criteria);
    Logger.log(" " + deleteThreads.length + "件");

    for (var j = 0; j < deleteThreads.length; j++){
      deleteThreads[j].moveToTrash();
    }
    Logger.log('' + query + "完了");


  }
}

```

完成したら、トリガーを追加する。
TypeError: Cannot read property 'map' of undefined

## 概要

Reactで書いたコードで↓のエラーが発生したので対処方法メモ
``TypeError: Cannot read property 'map' of undefined``


## 原因
mapメソッドを使っている配列にデータが入ってなかった

```
cost abc[] = fetch('https://abcdef.com/') <=データ取れてない
const abc.map(function(val){
    return val
})
```

##解決方法
APIのエンドポイントが間違っていたため、修正。
NginxとuWSGIとDjangoでサーバーを建てるとき、躓いたところ

#内容
[ここ](https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html)を参考にしていたら、いくつか躓いたので解決策を記事にします。
#nginxサーバーが立ち上がらない
##問題点
```
/etc/init.d/nginx start
```
としてもfailとなってしまう。
##解決策
sudo権限が必要でした。

```
sudo /etc/init.d/nginx start
```
#502エラーが出てしまう
##問題点
nginxとuwsgiを起動しても、502Errorとなってしまう。
##解決策
/var/log/nginx/error.logを見ましょう。
>[emerg] 8919#8919: open() "/path/to/your/mysite/uwsgi_params" failed (2: No such file or directory) in /etc/nginx/sites-enabled/mysite_nginx.conf:

これは、サンプルコードの/path/to/your/mysiteを自分のプロジェクトフォルダのパスにすればいいです。

>[error] 532#532: *21 connect() failed (111: Connection refused) while connecting to upstream

これは、たしかuwsgiのほうを動かしていなかったから起こったエラーだと思います。

>connect() to unix:///path/to/your/mysite/mysite.sock failed (13: Permission
denied)

これは、mysite.sockへの権限がないみたいなので、

```
uwsgi --socket mysite.sock --wsgi-file test.py --chmod-socket=666
```
で実行するのと、

```
sudo adduser (username) www-data
id (username)
```
を実行するとできました。

#参考文献
https://www.cyberciti.biz/faq/ubuntu-add-user-to-group-www-data/
Ansible で ssh ログインできるユーザを作成する。


# これはなに？

単体で使える ansible-playbook の紹介です。
サーバで ssh ログインできるユーザを作成するものです。

主に Jenkins ノード用ユーザやデプロイ用ユーザの作成に利用できると思います。

# ファイルは？

## `main.yml`

```yml
- hosts: localhost
  connection: local
  become: yes
  vars:
    user_name: jenkins_worker
    group_name: ...
  tasks:
    - name: Useradd {{user_name}}
      user:
        name: "{{user_name}}"
        group: "{{group_name}}"
        generate_ssh_key: yes
        home: /var/lib/{{user_name}}
    - name: Allow {{user_name}} to ssh
      copy:
        src: /var/lib/{{user_name}}/.ssh/id_rsa.pub
        dest: /var/lib/{{user_name}}/.ssh/authorized_keys
    - name: Add user "{{user_name}}" to sudo
      lineinfile:
        path: /etc/sudoers.d/{{user_name}}
        line: '{{user_name}} ALL=(ALL) NOPASSWD: ALL'
        state: present
        mode: 0440
        create: yes
        validate: 'visudo -cf %s'
```

いくつか解説があります

### 1. local 接続している

`hosts`, `connection` でローカル接続設定をしています。
Ansible はリモートのPCへ実行することもできるので、その際はそれぞれ変更します。

### 2. ユーザ作成部分

```yml
    - name: Useradd {{user_name}}
      user:
        name: "{{user_name}}"
        group: "{{group_name}}"
        generate_ssh_key: yes
        home: /var/lib/{{user_name}}
```


ユーザは Ansible 標準の User モジュールで作成しています。
`generate_ssh_key` の設定で ssh キーを作成するようにしています。

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/user_module.html

### 3. authorized_keys 作成部分

ユーザのホームディレクトリに .ssh/authorized_keys に公開鍵を登録することで外部から ssh ログインができるようになります。
copy モジュールで `.ssh/id_rsa.pub` を `.ssh/authorized_keys` にコピーしています。

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/copy_module.html

### 4. 追加したユーザが sudo できるようにする

今回は Jenkins のノードになるようなユーザを作っています。
ジョブ内で sudo を使うことがあるので sudo をパスワードなしで実行できるようにします。

方法としては、「`/etc/sudoers` に設定を追加する」または「`/etc/sudoer.d/` ディレクトリ以下に設定を含むファイルを追加する（`/etc/sudoers` に `#includedir /etc/sudoers.d` の記載が必要）」があります。
今回は後者の方法を選んでいます。

lineinfile モジュールはファイルに文字列を追加するモジュールです。
複数回実行しても重複なく文字列を追加できます。（冪等性）

https://docs.ansible.com/ansible/latest/collections/ansible/builtin/lineinfile_module.html

# 実行方法は？

今回はローカルなので以下のように実行できます。

```bash
sudo ansible-playbook --connection=local main.yml
```

# ssh ログインするには？

作成したユーザの秘密鍵をクライアントにコピーします。
id_rsa というファイルにした場合、以下のコマンドで ssh ログインできます。

```bash
ssh -i id_rsa -l jenkins_worker localhost
```

今回はサーバでユーザと鍵を生成しましたが、秘密鍵をコピーして渡すのがよろしくないときはクライアントで鍵を作って公開鍵を渡す方法の検討が必要です。

# 参考

https://docs.ansible.com/ansible/2.9_ja/
プログラミング初心者向けC言語入門＃0　HelloWorld!!

##### 初心者がC言語ってどうなのよ
「Cの初心者はあっても、初心者のCはない」という言葉もあるぐらいには正直初心者向けとは言えない言語であるC言語。
まあでもいまだに大学の授業等で扱う以上「初心者のC」が発生するわけで。将来エンジニアになるのであればやって悪いことはないだろうけど、もっととっつきやすい言語なんざいまはうじゃうじゃあるわけで。あと実はCじゃなくてC++だったみたいなことがあってもVSとかだったら(なにもしてなければ)動いちゃうし...最初からC++としてやったほうが楽じゃね？(ここまで私見)

### そんなこと言われてもやらなきゃいけないんだから教えろよ　単位がかかってんだよ
はい。やります。

# 本編
## 環境
### 環境構築って難しいよねって。
まずプログラミング初心者が躓くところです。調べてたら`GCC`とかいうのが出てきて、入れようと思ったけどわけわかめ。なんて人大量にいます。`VisualStudio`っていうのが出てきて入れてみたけど、どれだけインストールに時間かかんのよ。もう飽きてきたんだけど。なんて人もいるかもしれません。それぐらい環境構築ってめんどくさいの。`VSCode`にしたっていろいろ設定せなならんわけでめんどくさい。あとそれってプログラミングの前にメンタル削ってるよねって。

### ぶっちゃけネットさえあればいいです。
いま世の中**令和**ですよ？プログラミングの勉強なんて全部ネットで済ませちゃいましょう。スマホでも(面倒だと思うけど)できちゃうはず。本？基本買わんくていいよ。そもそもプログラミング言語って結構頻繁に更新されたり、本に書いてあること大体ネットに転がってるし。だって今**令和**だぜ？
`paiza.io`だったり`runnable`[だったりというもの](https://qiita.com/tttamaki/items/2b009aa957cfa4895d50)が転がってるわけです。アプリでも`モバイルC`っていうのもあったりします。こちらはオフラインでC言語をつかえます。アプリなので導入も簡単ですし。これらを使えば、環境構築にかける時間分を勉強に回せるわけです。多分2時間ぐらいこれで浮きます。時間は作るものです。2時間でいっぱい手を動かしましょう。

## とりあえず動かしてみよう
### 準備
画面は[paiza.io](https://paiza.io/ja/projects/new)を使ってますが、ほかのサイトでも基本同じのはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286973/07694b99-5c1a-5d49-cb92-452b5181a60b.png)
左上の緑のところを`C`に変えてください。これでC言語として動かせます。画像のような表示になったら次行きます。

### 実装
さあ、コードを書いてみましょう。とりあえず入門定番の`Hello World!`と呼ばれるものを作ってみましょう。
ゴールは次の通りです。
**出力に`Hello World!`と表示させる**

ん？しょぼい？　まあそんなこと言わず。
では`// Your code here!`と書かれているところに追記して、画像のようにしてみてください。これくらい頑張って手打ちしてね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286973/9dd15742-b717-0f25-6e3c-5077e7e968f6.png)


できましたか？では実行ボタンを押してみましょう。
`出力`を押してみてください。画像のようになっていれば成功です。なってなければもう一回やり直してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286973/85759d25-9d37-f43b-74af-cec3b1c58469.png)

### エラーが出てたら
<details><summary>赤い×がついてたりしたら</summary><div>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286973/af301001-8151-68e7-727b-451457170fc6.png)
なんかこうなっちゃた、って人。
おめでとうございます。

何と親切にもなんでこうなっちゃたかが`コンパイルエラー`というところに英語ですが書いてあります。頑張って読みましょう。きっとあなたなら直せます。エラーが出た時は、エラーメッセージをとりあえず読みましょう。

わかんねえよって？
あらそうですか。では日本語にしてみましょう。上記の例ではこんな風に書かれてますね。

```
Main.c:4:27: error: expected ';' after expression
    printf("Hello World!")
                          ^
                          ;
Main.c:6:13: error: expected ';' after return statement
    return 0
            ^
            ;
2 errors generated.
```
とりあえず日本語に。

```
Main.cファイルの4行目でエラー：';'がないよ！
    printf("Hello World!")
    の後にね！
Main.cファイルの6行目でエラー：';'がないよ！
    return 0
    の後にね！
```

つまり何を教えてくれているかというと、`^のところに';'を書き忘れてるよ！`ってことです。なので、書き加えましょう。
</div></details>

<details><summary>何やってもダメ！って人</summary><div>
ほんとにダメでしたか？実はCじゃなくて`PHP`だったりしてません？もっかい全部みて。

なってないですか。
じゃあ下のコピーしていいよ。それでだめなら最初からやり直して。

```c:Main.c
#include <stdio.h>
int main(void){
    
    printf("Hello World!");
    
    return 0;
}
```
</div></details>

## 次回
ところで何が起こってたの？`;`がなんでいるの？`#include`って何？って思うでしょ？
次回(とその次も？)で見ていきます。
python で Photoshop形式のファイル（.psd）を扱う実行ファイルを作る

#はじめに
pytoshop を利用した実行ファイルを生成する際の環境設定に関する記事が少なかったので，まとめておこうと思います．

#やりたいこと
pytoshop と OpenCV を利用したコードについて，Pyinstaller を用いて実行ファイルを生成します．
このとき，どうやら OpenCV と Pyinstaller には互換性の問題（？）があるようで，ファイル生成を実行できた環境をまとめておきます．
pytoshop は anaconda で提供されていないので pip install を利用する必要があります．
そこで全て pipインストールを行ったのですが，自ら入れたパッケージとそれらに紐づいて自動で入ったパッケージなどが様々あるので，まとめておきます．

#環境

#利用したPython
```
Python = 3.7.10
```

#パッケージの管理
```
pip install Pyinstaller==3.4 (記事作成時の最新版は 4.3)
pip install pytoshop (記事作成時の最新版は 1.2.1)
pip install opencv-python==4.0.1.24 (記事作成時の最新版は 4.5.2.54)
pip install mkl==2018.0.0 (記事作成時の最新版は 2021.3.0)
pip install six (記事作成時の最新版は 1.16.0)
```
私が環境構築について各パッケージのバージョンをpytoshop については過去バージョンを利用すると初期化がおかしくなるなど微妙な不具合があった気がします．

#環境内のパッケージ（括弧をつけていないものは全て記事作成時の最新版）
```
altgraph       0.17
certifi        2021.5.30
Cython         0.29.23
future         0.18.2
intel-openmp   2018.0.0 (2021.3.0)
macholib       1.14
mkl            2018.0.0 (2021.3.0)
numpy          1.17.4 (1.21.0)
opencv-python  4.0.1.24 (4.5.2.54)
pefile         2021.5.24
pip            21.1.2 (21.1.3)
PyInstaller    3.4 (4.3)
pytoshop       1.2.1
pywin32-ctypes 0.2.0
setuptools     52.0.0.post20210125 (57.0.0)
six            1.16.0
wheel          0.36.2
wincertstore   0.2
```
numpy や intel-openmp 辺りは恐らく OpenCV に紐づいてダウンロード・インストールされるので，過去バージョンになっているようです．
pip や setuptools に関しては恐らく私の更新忘れです．

また，Pyinstaller で実行ファイルを作成する際には特定のモジュールのみを import するような記法（from sklearn import tree のようなもの）を使うと module not found error が出るため，最初に import sklearn を行い，tree を利用する際は常に sklearn.tree のように書くと問題なく利用できると思います．

#参考
pytoshop を利用する上で参考にしたサイトもまとめておきます．
https://pytoshop.readthedocs.io/en/latest/api.html
【Django】urls.py, views.pyファイルを理解したい

#今回のゴール
Djangoフレームワークで、Hello,worldをブラウザに表示

###■ プロジェクトフォルダ(first)を編集していきます
```＜settins.py＞変更前
	INSTALLED_APPS = [
	'django.contrib.admin',
	'django.contrib.auth',
	'django.contrib.contenttypes',
	'django.contrib.sessions',
	'django.contrib.messages',
	'django.contrib.staticfiles',
	]
```
に、自分のアプリケーションを登録します

↓

```＜first/settins.py＞変更後	
INSTALLED_APPS = [
'myapp.apps.Myappconfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
```	
●ちなみに、'myapp.apps.Myappconfig', のmyappはパッケージ(フォルダ).(アプリケーション名)で、appsはモジュール(ファイル)で、Myappconfigは、そのモジュールの中にある関数です
	

```＜first/urls.py＞変更前	
from django.contrib import admin
from django.urls import path
```	
↓

```＜first/urls.py＞変更後
from django.contrib import admin
from django.urls import path, include
```


また、プロジェクト側でpath関数の追加

```＜first/urls.py＞変更前
urlpatterns = [
path('admin/', admin.site.urls),
]
```
↓

```＜first/urls.py＞変更後
urlpatterns = [
path('admin/', admin.site.urls),
path('myapp/',include('myapp.urls')),
]
```

・path('myapp/',
path関数の最初の引数(myapp)はパスを表す文字列を書きます。今回であれば、127.0.0.1:8000/myappの『myapp』部分が第一引数
		
・include('アプリケーション名,urls') 
⇨この関数は、アプリケーション内のurls.pyファイルを参照するよ。 なやつ

・include('myapp.urls')),
第二引数には、”include関数”使います。include関数の()の中身には、アプリケーション名(myapp.urls)と書く。
			
・urls
urls.pyのurlパターンリストの中に、url(path)に対応した処理を定義していく


・一般的によく行われるのは、プロジェクトのurls.pyのファイル		
このように、プロジェクトのurls.pyにはincludeを用いて定義していくと考えていきます


###■それではアプリケーション(myapp)側にも”urls.py”を作成していきます
まず、アプリケーションフォルダ内に”urls.py”ファイルを作成します

```＜myapp/urls.py＞
from django.urls import path
from .import views
app_name = 'myapp'
urlpatterns = {
path('', views.index, name='index'),
}
```

・myapp/urls.pyの処理はfirst/urls.pyの処理の後に実行されます	

・アプリケーション側のurls.py(のpath)は、プロジェクトフォルダ(first)のurls.pyファイルに記載されている
<firsrt/urls.py>
path('myapp/',include('myapp.urls')),
を参照している


views.index
・指定した関数です
		
name='index'
・indexという名前をつけています

そしてブラウザに表示させるためには最後にviewsを設定します

```＜myapp/views.py＞
def index(request):
return HttpResponse("Hello,world")
```

index(request)
・urls.pyにも記載したindexという関数を定義します
・引数はrequest　※これは固定です

HttpResponse
・urls.pyで呼び出すように指定した関数はというHttpResponseオブジェクトを返します
・HttpResponseの引数はHello,worldです


####ターミナルにて下記実行
```
python3.8 manage.py runserver
```

Hello,worldと表示されれば成功
公証役場に提出するPDFの電子署名について

マイナンバーカードを使用して電子署名を行い、立川公証役場に提出した結果
＊２年ほど下書きで放置していたのを投稿します。現在は変わっているかもしれません。

経緯：
１社目は紙定款で設立したけど、いい機会なので電子定款を使用してできるだけオンラインで株式会社設立を目指す。

定款自体は１社目のものを目的を変更し、末尾の実印の代わりに「電子署名する」という内容を追加でOK
問題は電子署名。
公式は登記供託オンライン申請システムをインストールして、Acrobatを購入・インストールして、ＰＤＦ署名プラグインをインストールして署名。
・・・Acrobatなんて登記のときしか使わんわ！
（株主総会や役員会議の議事録の電子署名は別に方法がある）

署名方法１：JPKI PDF SIGNER（不可視署名）
https://jpki.osdn.jp/
→CN部分が氏名に置き換えられているけど、住所が確認できないのでアウト

署名方法２：OpenSC + Acrobat Reader DCの証明書ツールで署名
https://www.challenge-cf.jp/post/%E3%83%9E%E3%82%A4%E3%83%8A%E3%83%B3%E3%83%90%E3%83%BC%E3%82%AB%E3%83%BC%E3%83%89%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9Fpdf%E3%81%B8%E3%81%AE%E9%9B%BB%E5%AD%90%E7%BD%B2%E5%90%8D
証明書の名前の部分がカードIDで表示される、住所がカードの発行市町村の不安になる署名
→住所氏名等が確認出来ないと言われる
→X.509データにUTF-8で格納されていることを提示した
＊公証役場にX.509データを確認する方法が無いと、「実印＋印鑑証明書（住所、氏名）」の代わりとして利用するのは難しいと感じた

署名方法３：登記供託オンライン申請システムの電子署名
http://www.moj.go.jp/content/001314623.pdf
署名部分が独立している気がするぞ。これ使えるんじゃないか？
→PDFは変更なし、PDFが改変されていないことの証明書がXMLで出力されるだけなのでアウト

署名方法４：JPKI PDF SIGNER（可視署名）
https://jpki.osdn.jp/index.html#%E5%8F%AF%E8%A6%96%E7%BD%B2%E5%90%8D
不可視署名が駄目なら、可視署名をして、署名内容を確認すればいいのでは？
実印の印影をスキャナで取り込んで可視署名の印影にセット
→署名方法１と同じく、住所がX.509データに格納されているものの、公証役場が確認できないのでアウト
https://lapo.it/asn1js/ 使うと楽にX.509の内容は確認できる。

署名方法５：Acrobat＋署名プラグイン
(๑´•.̫ • `๑)氏名とか住所とか、マイナンバーカードに格納されているのじゃなくて、AcrobatでSignedPDFで指定する情報やんけ。しかも証明書はAcrobatでは確認できないので公証役場任せ・・・
氏名や住所が自己申告の内容で、印鑑証明書のように公的に保証された内容でないのはいいのかな？
それともAcrobatでは確認できない箇所に主体性情報が格納されていて、公証役場が利用するソフトではそれが確認できるのだろうか。

<b>結局「公証役場は「指定方式」以外での署名の正当性を認めないので、諦めろ」ということでした。</b>


別記：全然オンライン化出来てない公証役場の手続き
１．WEBで予約する
２．電話かかってきて提出する書類等やTV電話か直接公証役場かの確認
３．予約時に入力したメールアドレスに公証役場からメールが来る
＊紙定款の時は電話で公証役場予約して、定款の内容チェックは法務局でやって貰っていたので直接持ち込み現金払いで終わりでした。実質的支配者となるべき者の申告書もまだ無かったので公証人名を知っておく必要も無かった。（あったとしても現地記載か予約時に聞けばOK）
４．電子定款、実質的支配者となるべき者の申告書をメールで送る
５．実質的支配者となるべき者の申告書に記載が必要な担当公証人の名前を教えてもらう。＜－この段階で担当が決まる
６．書類のOKを貰う

７．TV電話する。
８．定款認証手数料を振り込む。振込確認連絡が来る。
９．登記供託オンライン申請システムで謄本作成日に申請する・
１０．公証役場に電子謄本を受け取りに行く

・・・最後結局公証役場に行かないといかんのかい！
最後の３つは「謄本作成日に公証役場に行けるのであれば」公証役場に行けばオンライン申請不要、現金払いで受け取れます。
多分行政書士さんとかが複数法人をまとめて処理するのであれば効率化されているのでしょう。
かかる時間と手間を考えたら、『電子謄本を受け取るまでは』行政書士さんに任せた方が安いです。
[JQuery]クラスを追加・削除するメソッド

## はじめに
JQueryでクラスを追加・削除するメソッドを使ったので備忘録として残しておきます。

## 本題
ここからはクラスを追加または削除するメソッドを３つ解説していきます。

#### 1. addClass()
要素にクラスを追加するときは`addClass()`を使います

```
$('.hoge').addClass('bg-blue');
```
のようにかきます

#### 2. removeClass()
要素を削除する場合は`removeClass()`を使います

```
$('.hoge').removeClass('bg-blue');
```

#### 3. toggleClass()
要素に対し流動的にクラスを変えたい場合はtoggleClass()を使用します。
指定のクラスを持っている場合に削除、持っていない場合は追加します。

```
$('.hoge').toggleClass('bg-blue');
```

以上でクラスの追加、削除を行うことができます。


## 参考

https://kinocolog.com/jquery_class/
AWS EC2環境でUnicornをSystemctlで起動させる

最終的に、起動できるようになりました、コードを記載します。

```:/etc/usr/systemd/system/unicorn.serve
[Unit]
Description=The unicorn server
After=mysqld.service

[Service]
User=ec2-user
WorkingDirectory=/var/www/アプリディレクトリ
SyslogIdentifier=unicorn
PIDFile=/var/www/Atlier/tmp/pids/unicorn.pid

ExecStart=/home/ec2-user/.rbenv/shims/bundle exec unicorn_rails -E production -c /var/www/Atlier/config/prod_unicorn.rb -D
ExecStop=/usr/bin/kill -QUIT $MAINPID
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target

```

今回はコンテナなどを使用せずに、EC2内に直接アプリをクローンする形で本番環境を動作させています。
Dockerではプロセス管理ツールとして、Supervisorを用いておりましたが、EC2ではプロセス管理ツールとしてsystemctlを導入しております。それぞれのOSに合った管理ツールを選定する運びになりました。
DDL とは？ SQL文を確認したよ

## 勉強前イメージ

基本になるようなもの・・・・？

## 調査

### DDLとは？

Data Definition Language の略で、データ定義言語の意味です。
データベースのデータ構造を定義する言語です。
DDLのコマンドは `CREATE` , `DROP` , `ALTER` , `TRUNCATE` の4つです。

これはSQL文のカテゴリの1つで、他にも色々あります。

### SQL文とは

SQL文とはデータベースを操作する言語のことを指し、
データの取り出しや追加を命令する文のことです。

種類としては以下のカテゴリに分類出来ます。

- DDL
- DML
- DCL
- TCL

#### DDL

↑でやったので飛ばします

#### DML

Data Manipulation Language の略で、データ操作言語の意味です。
データの取得・登録・更新・削除の言語がこちらになります。
コマンドは `SELECT` , `UPDATE` , `DELETE` , `INSERT` になります。

#### DCL

Data Control Language の略で、データ制御言語の意味です。
データベースへのアクセスの制御(権限)をするコマンドがこちらになります。
コマンドは `GRANT` , `REVOKE` になります。

#### TCL

Transaction Control Language の略で、トランザクション制御言語の意味です。
データベース内にでトランザクションを扱うコマンドがこちらになります。
コマンドは `BEGIN` , `COMMIT` , `ROLLBACK` になります。

## 勉強後イメージ

今まで意識してなかったけど、これすらも言語なのか。
しかも結構細かくわかれてるんだね？
DCLとTCLがちょっとややこしい？

## 参考

- [データベース言語（ＳＱＬ）の体系　DDL/DML/DCL](http://www.kogures.com/hitoshi/webtext/db-ddl-dml-dcl/index.html)
- [入門者でもわかるSQLを使って表を作ってみよう！【SQL文の書き方付き】](https://udemy.benesse.co.jp/development/system/intro-sql.html)
- [【DB基礎】4種類のSQL命令(DML, DDL, TCL, DCL)とは](https://kirohi.com/sql_4_languages#DCL)
redash10をubuntu20にインストールする方法

OS：ubuntu 20.04
メモリ：4GB
sudo su
apt-get update
apt-get upgrade -y
cd /
git clone https://github.com/getredash/setup
cd setup
vim setup.sh
ドッカーのバージョン、1.22.0を最新にする　1.29.2
最新はこちら：https://github.com/docker/compose/releases
setupcompose()内のLATEST_VERSIONを、インストールしたいバージョンのTagに変更する
今回はこれ。10.0.0-beta.b49597
最新のタグはこちらからとる：https://hub.docker.com/r/redash/redash/tags?page=1&ordering=last_updated

create_directories()内の二重カッコ（[[hogehoge]]）を1重に修正　2箇所、合計4つ
create_config()内の二重カッコ（[[hogehoge]]）を1重に修正 １箇所、合計２つ

sh ./setup.sh
MAMP  使いたいポート番号が使用中の時の対処法

#はじめに

MAMPで、「そのポート番号はすでに使用中」、みたいなエラーに最初の頃遭遇し、悩まされたので対処法を記事にします。

環境
macOS Big Sur バージョン14

#使いたいポート番号がすでに使用中で使えない時の対処法

ターミナルを開き、ホームディレクトリから次のコマンドを実行します。


```
$ sudo lsof -i -P | grep "LISTEN"
```


パスワードが聞かれるので、パスワードを入力。
すると、ポートの状態の一覧が出てきます。

右端くらいにポート番号が表示されているので、使いたいポート番号が使われていたら、使っているものを次のコードで停止させます。

```
$ kill -9 XXXX （XXXXには、ポートの状態の左端にあるremotedのすぐ右にある番号を入力します）
```

これで、使っているもの全てを停止させれば、使いたいポート番号を使えるようになります。

自分と同じような点で悩んでいる方がおられれば参考になれば幸いです。
[PyVista]カメラのキャプチャ映像をテスクチャにマッピング(調査メモ)

## はじめに

PyVistaで作成した3Dオブジェクトに、ストリーミング配信からキャプチャした映像をテクスチャとしてマッピングするスクリプト（PyVistaを個人開発`ウソ穴`に使えるかもと調査中）

## 概要

同じネットワーク内に、カメラ付きラズパイとWindows10の構成
ラズパイに接続したカメラの映像を3Dオブジェクトのテスクチャにします。

![概要](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/c440f19c-0a70-55b7-78ff-82b258483ac9.png)

- **Windows10**
    - Python 3.9.2
    - opencv-contrib-python 4.5.3.56
    - pyvista 0.31.3
- **Raspberry PI 3 model B+**
    - MJPG-streamer

## 実行結果

後述するスクリプトの実行結果。円柱の3Dオブジェクトを作成し、ラズパイに接続したカメラの映像を3Dオブジェクトのテスクチャにしています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/8934b4f0-e7be-9409-4db8-ae78c8aae6a6.png" width=300>

↓ カメラの前に青い壁をたてている（銀色の箱の中はカメラ付きラズパイ）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/3a31d1e4-06c3-4524-d7b8-97b11f987ecd.jpeg" width=220>


## スクリプト

```python:test.py
import pyvista as pv
from pyvista import examples
import numpy as np
from matplotlib.cm import get_cmap
import cv2

targetImage = "http://{{ラズパイIPアドレス}}:8090/?action=stream"
cap = cv2.VideoCapture( targetImage )

numpy_image = np.array(targetImage)
surf = pv.Cylinder()
ret, frame = cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
tex = pv.numpy_to_texture(frame)
surf.plot(texture=tex)
```


## 補足

[ラズパイ]MJPEG-Streamerでストリーミングを実行するコマンド
(MJPEG-Streamerのインストールは [**ここ**](https://qiita.com/suo-takefumi/items/2ae5527869dc13d038a9) を参照)

```console
$ /usr/local/bin/mjpg_streamer -i "input_raspicam.so -x 400 -y 400 -fps 15 -q 100" -o "output_http.so -p 8090 -w /usr/local/share/mjpg-streamer/www"
```

## 参考

参考にしたサイト

- Applying Textures
    - https://docs.pyvista.org/examples/02-plot/texture.html
RemiでCentOS7にPHP8をインストールする

Remi（=CentOSのサードパーティーリポジトリ）を使って、CentOS7にPHP8をインストールしたので覚え書き。

## 環境
* OS: CentOS7.9（GCP・Compute EngineのCentOS）
* PHP: 8.0.8（インストール後）

## 手順

ほぼ、
[Remi's RPM repository - Configuration wizard | rpms.remirepo.net](https://rpms.remirepo.net/wizard/) 
の通り

上記ページの、Operation system and version selection の欄で、以下の通り選択しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356958/826f5c68-86a7-b3d6-c5e6-053d1aa1cb54.png)


(1) まず、Remiの使用に必要なEPEL（=標準リポジトリでは提供されないパッケージの使用を可能にする）をインストールします。

```
$ sudo yum -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
```

(2) Remiレポジトリの構成パッケージをインストール

```
$ sudo yum install -y https://rpms.remirepo.net/enterprise/remi-release-7.rpm
```

(3) yum-config-managerコマンドの使用に必要なyum-utilsパッケージをインストール

```
$ sudo yum install -y yum-utils
```

(4) PHP8.0のパッケージのみを有効にします

```
$ sudo yum-config-manager --disable 'remi-php*'
$ sudo yum-config-manager --enable remi-php80
```

(5) 使用可能なレポジトリの確認。「remi-php80」が表示されればOKです。

```
$ sudo yum repolist
```

(6) インストールパッケージの最新化を実施

```
$ sudo yum update -y
```
※GCPで、以下の状態でしばらく待ちになった場合は、「Ctrl + C」で中断してしまっても大丈夫です（直後、エラーになりましたが、先に進めました）。

```
Running transaction
  Updating   : google-cloud-sdk-348.0.0-1.x86_64
```

(7) PHPをインストール。

```
$ sudo yum install -y php
```
※サイトの手順では、「php-xxx」となっていますが、「-xxx」は不要でした。

(8) 以下のコマンドを実行し、PHPのバージョンが表示されればOK

```
$ php --version
```

## メモ
地味に、手順(7)のところでハマった。
php-80とか、remi-php80ではなく、単にphpとするのが正解（他のバージョンを無効にしてるからかもですが）

## 参考

* [Remi's RPM repository - Configuration wizard | rpms.remirepo.net](https://rpms.remirepo.net/wizard/) 
* [CentOSなどで使う、EPELってなんだ？ | Qiita](https://qiita.com/charon/items/f5732694515d174851b3)
Paramsとは

#Params
Railsのフォームから送られてきた値を受け取るためのメソッド

箱に入れられるイメージをするとわかりやすいです。
キーとバリューのセットになって箱に入ります。
名前：　太郎
メールアドレス：　taro@taro.com

名前はnameカラムに、メールアドレスはemailというカラムに保存されるとする
するとこれらのパラメーターは次のような形で次のアクションへ送られます。

```ruby:qiita.rb
{name:　"太郎", email: "taro@taro.com"}
``` 

パラメーターが箱に入ってcreateアクションへ渡されました。
データベースへ保存するためには入力された情報を箱から取り出さなくてはなりません。

その時に使うメソッドがparamsです。
paramsメソッドを使うと投稿フォームに入力された情報を箱から取り出すことができます。
M1 Macでできるだけ楽にPython環境を構築する

# はじめに
seabornを使いたくて`pip install seaborn`をしたらド偉い量のエラーに見舞われた。いい機会と考えてなあなあで構築していたPython環境をすべてリセットしてイチから構築し直した。

# 環境

- M1 MacBook Air
- macOS Big Sur 11.3.1

# この記事でやること

1. Homebrewのインストール
1. Pythonのインストール
1. 各種パッケージ(numpy、pandas、matplotlib、scipy、seaborn)のインストール

通常使用であればこのあたりのパッケージがあれば十分？だと思う。TensorflowやPyTorchが必要な人は頑張ってくれ。

なおこの記事ではvenvなどを用いた仮想環境での構築は行わない。仮想環境を使いたい人は[この記事](https://qiita.com/ketaro-m/items/ebae35c49d55aa86dfcf)が参考になる。

# Homebrewのインストール

M1が出てしばらくはhomebrewのインストール方法が迷走していたらしい[^1][^2]が、現在はコマンド一発でインストールできるようになっている[^3]。便利〜〜。

```zsh
% /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

Homebrewのインストールができたらパスを通しておく。ご丁寧にHomebrewくんはパスの通し方をログに書いてくれてる。

```zsh
% echo 'eval $(/opt/homebrew/bin/brew shellenv)' >> ~/.zprofile
% source ~/.zprofile
```

.zprofileにパスとかを書いていく感じだが、なんでもいい。Homebrewくんに逆らうなら.zshrcにでも書いておこう(僕はこっち派)。

# Pythonのインストール

Homebrewで一発。

```zsh
% brew install python
```

python3などとしてバージョンを指定する必要はない。勝手に一番新しいやつが入ってくれる。

# 各種パッケージのインストール

## scipy、numpyのインストール

まずはこの2つから。

先にseabornのインストールでド偉い量のエラーに見舞われたと書いたが、エラーメッセージ的にscipyのインストールで死んでいるようだった。これは、M1 Macでの`pip`ではscipyを入れることができないから。まだ未対応らしい。

しかし、Homebrewからscipyを入れることでこの問題は解決する。

```zsh
% brew install scipy
```

なお、scipyのインストールと同時にnumpyもインストールされる。scipyはnumpyに依存しているので。

## seaborn、matplotlib、pandasのインストール

最後にこの3つ。これらは`brew install`ではなく、`pip install`を使用する。

```zsh
% pip install seaborn
```

seabornはmatplotlib、pandasに依存しているため、これだけで上記3つのインストールが完了する。

## numpyやmatplotlibをすでに導入している人へ

僕は試していないが、numpyが入ったままscipyをHomebrewからインストールすると、同時に入るnumpyとおそらく競合する。それを避けるために一度アンインストールしておく必要がある。また、matplotlibなど、numpyに依存するパッケージもアンインストールの必要がある。

```zsh
% pip uninstall numpy
% pip uninstall matplotlib
% brew install scipy
% pip install matplotlib
```

# まとめ

`pip`一発でPythonの環境構築ができないM1 Macだが、これでも発売当初よりはかなり楽になってきたようだ。
新Apple Silicon搭載MacBook Proの噂もチラホラ聞こえるので、もっと楽に環境構築できるようになってほしい。
~~お前がOSSに貢献しろ~~

[^1]: [Apple SiliconにおけるHomebrewのベストプラクティス](https://qiita.com/yujiod/items/56002a7cef5b5a3be3fb)
[^2]: [Mac M1(Apple Silicon)でhomebrewをインストールできるの？](https://qiita.com/shira-shun/items/0f6213f4923cb5544367)
[^3]: [Homebrew](https://brew.sh/)