OmniAICreator/Qiita-1.07M huggingface dataset:
営業の私がGoogle Apps Script【GAS】を使って営業の業務を自動化・効率化していく話。

#はじめに
※この記事は随時更新しています。(最終更新日：2021/3/8 業務フロー大枠を更新)
※この記事は私が仕事している企業の状況等を記載しておりますが、企業内の機密情報は一切記載しません。そのため一部表現を変えたり情報をマスクしていますがご容赦ください。


私は小さなIT企業で営業をしています。
もともとはエンジニアでしたが、会社都合で入社から3年後にして営業をしています。(謎)
営業の業務って本当に無駄な業務が多いんですよね・・・。

特に私の今いる会社では、人数がとても少ないので、一言で営業といっても単に顧客へのセールスだけが仕事というわけではありません。

契約等の事務処理、契約書作成、プロダクト開発、マーケティング、広報・・・
あげればキリがありません。もはや万屋です。

[営業]という切り口から、現在の繰り返しの業務を上げると、たくさん出てきます。
・CRMでの案件作成
・見積書の作成・提出
・契約書の作成・提出・受領
・契約書の締結
・稟議起案・押印
・CRMでの案件情報更新
・顧客への案内(契約書、見積書の送付等)

こんな同じことの繰り返し業務を営業は当たり前のようにやっているわけです。
もう、やめませんかこんな単純な作業と訴えたいのが私の思いであり、誰もやっていないところだったので、
自分が上記の業務を全部効率化・自動化したったろ！というのが志であります。

というわけで、この記事では、私が日頃行う業務をGASをつかってどうやってどのくらい効率化・自動化できるかをまとめて行くことにします。

##会社で使っているツール一覧
* プラットフォーム：Googple Gsuite
* コミュニケーション：Slack
* SCM：Salesforce
* BIツール：TableauOnline(念の為)
* 稟議：承認Time

##自動化・効率化の前にやるべきこと
業務を自動化する上で、実際にコーディング(自動化)する前にやるべきことがあります。

* 現状の業務フローを整理(大枠からPCの操作レベルまで詳細を整理)
* 自動化・効率化するためのフロー設計
* モックアップ作成

とはいえ、上記ができるのは過去に経験者であればできると思いますが、私は一連の流れを経験したことがないので、
できるだけ上記をまず優先的にやりつつも、試験的にコーディングを並行してやって行くことにします。

##業務フローの整理(大枠)
以下に、営業のメンバーが行う大枠の業務フローです。
これを１案件ごとにやっており、1日に一人あたり10件程度裁いています。
(しんどいです・・・・・・)

1. <b>見積書作成</b>
    2. [会社名]・[見積No]・[金額]などの項目を見積書に記入
    3. 完成した見積書をPDF形式でダウンロードして指定のフォルダに保存
4. <b>申込書の作成</b>
    5. 申込書に必要な情報記入
    6. EXCEL形式で新しくファイルを指定のフォルダに保存
7. <b>作成した見積書・申込書・利用規約をメールでクライアントに送る</b>
    8. メール文を作文
    9. 見積書・申込書・利用規約をメールに添付
    10. メール送信
11. <b>クライアントの申込書内容の確認</b>
    12. 記載内容が正しいかを確認
    13. 確認後、押印してpdfと原本を要求
14. <b>CRMの情報更新</b>
    15. CRMの明細情報更新
    15. 案件フェーズを見積・契約対応に変更
16. <b>稟議起案</b>
    17. 稟議を作成(必要情報の記入)
    18. 見積書・申込書の添付
    19. 申請
20. <b>稟議承認後に書類の押印申請用の稟議を申請</b>
    21. 稟議押印申請→こちらは担当が別にいるため、担当者に依頼する。
22. <b>納品対応</b>
    23. 納品対応
24. <b>CRM情報更新</b>
    25. 案件フェーズを[商談成立]に変更
26. <b>納品書・請求書作成対応</b>
    27. 納品書・請求書の作成を依頼する。
28. <b>納品書・請求書等の提出</b>


## 業務フローのグルーピング化
上の業務フローを見てみると、以下のようにフローをグルーピングできると思います。

* 資料作成(見積書、申込書の作成等)
* メール送信(案内、納品書、請求書提出等)
* 稟議起案
* CRM情報更新
* 担当者への依頼
* 納品

上記の機能が一つの画面でボタンぽちぽちすれば実行される・・みたいなことができたら最高です。
また、上記グルーピングした業務の中で、詳細業務をリスト化していきます。

* <b>資料作成</b>
    * 見積書作成
    * 申込書作成
* <b>メール送信</b>
    * 見積書・申込書・利用規約を送る。
    * 納品後に案内する
    * 納品書・請求書を提出する。
* <b>稟議起案</b>
    * 稟議を起案する
* <b>CRMの情報を更新する</b>
    * 明細を作成する
    * 案件フェーズを変更する
* <b>担当者へ依頼する</b>
    * 稟議(押印申請)を依頼する。
    * 納品書、請求書の作成を依頼する。
* <b>納品</b>
    * 納品する

このようにグルーピングすることで、資料作成→見積書→みたいな形で選択できるようにすれば、シンプルな画面設計・フローにすることができます。上記記載のように、大きなフローは全部で６個存在することがわかりました。

## 使えそうなアドアオン・技術
・Data Connector for Salesforce

##役に立つ関数・コード
・[Spreadsheet 複数セルの各文字列を結合する! CONCATENATE関数](https://qiita.com/ryoichi_ben/items/bb70ae1fda5bc95acd8d)

##サービス構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573711/0484262a-2693-5ee9-6672-cc3bd324a3d5.png)

## モックアップ
  ※随時更新します。

## コード
　※随時更新します。

##まとめ
　※随時更新します。
【Java】メソッド情報から引数名を取得する方法

## 概要
Javaのクラス情報（`java.lang.Class`）に含まれるメソッド情報（`java.lang.reflect.Method`）からは、メソッドで定義した引数の名前が取得できます。
今回はその引数名の取得方法を紹介します。

## 条件など
*  [こちら](https://stackoverflow.com/a/20594685)のStackOverflowの記事にある通り、Java8以降が前提となります。Java8以降ではMethodから[Parameter](https://docs.oracle.com/javase/jp/8/docs/api/java/lang/reflect/Parameter.html)クラスが取得可能なので、Parameterクラスの`getName()`を使用することで引数名が取得できます。
*  Javaのコンパイルオプションで、classファイルにパラメータ情報を含める必要があります。この設定を行わないと引数名を取得してもarg0、arg1のような形になります。コンパイルオプションにどういう指定をすれば良いか、[リフレクションで引数名を取得するときはjavacに-parametersオプションをつける](https://blog.shogo-mizuno.me/entry/2017/02/02/140506)にて紹介されています。なお、Intellijでの設定方法は[こちら](https://stackoverflow.com/a/24652186)のStackOverflowの記事を参照ください。

## 実装サンプル
大した内容ではないのですが、MethodクラスからParameterを取得し、それを出力する実装サンプルものせておきます。

```java
public static void outputMethodParamName(Method m) {
    Arrays.stream(m.getParameters()).forEach(p -> {
        System.out.println(p.getName());
    });
}
```
git-svnでSVN→gitする方法

現場がSVNリポジトリを使用していたので、ブランチで分けて開発をすすめて行きたかったのと
その後結局gitリポジトリに移行することになったので、この方法でリポジトリ移行をしました。

git-svnを使って、SVNリポジトリにあるコミットログごとごっそりgitリポジトリに移行しましたので備忘録です。
(コマンドとTortoiseGit使用)

※コミットログやデータ自体が多い場合は１０個ずつなどで分割しながらじゃないとエラーになってしまいますので注意です。

参考サイト：
https://www.karumado.com/2018/09/git-svnsubversiongit.html
https://qiita.com/toshikiw/items/56c57d12566cffa9e3fe

####１．git-svnでSVNリポジトリをclone 

####２．ブランチの確認
`git branch -a`

####３．予め用意しておいたgitのリモートリポジトリをリモート設定に追加(add)
`git remote add origin [リポジトリURL]`

####４．gitのリモートリポジトリのほうにpush
`git push -u origin --all`

####５．SVNで管理できてた空のディレクトリをgitでも管理する
→https://improve.backlog.jp/wiki/DEVELOPE/git%E3%81%A7%E7%A9%BA%E3%81%AE%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%82%92%E7%AE%A1%E7%90%86%E3%81%97%E3%81%9F%E3%81%84%E3%81%A8%E3%81%8D


＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝
ここからさきは、間違えてSVN側にコミットしちゃわないようにSVNの設定を削除しておく方法


####６．SVNの設定ファイルやgit-svnの設定ファイルを消す

・今のブランチがmasterであることを確認

・必要なSVNのブランチはgitのブランチとして作り直しておく

・ローカルのgit-svnブランチの削除
`git branch -D [ブランチ名]`

・リモートのsit-svnブランチの削除
設定ファイルからこの部分をまるごと削除

```
[svn-remote "svn"]
        url = <svnレポジトリurl>
        fetch = trunk:refs/remotes/trunk
        branches = branches/*:refs/remotes/*
        tags = tags/*:refs/remotes/tags/*
```

・リモートリポジトリ側からも削除
バックログの場合はブランチタブから削除可能
コマンドの場合は
`git branch -r -d remotes/[trunk, tagsとか]`



・ガベージコレクトする
`git gc`

・metadata削除
`rm -rf .git/svn`
`rm -rf .svn`

#####おまけ　gitkeepのはなし

参考：
https://blog.fukata.org/archives/6577
http://note.mokuzine.net/git-gitkeep/

gitは空のディレクトリはバージョン管理できない仕組みになっているので、
空のディレクトリには「.gitkeep」というファイルをおいて、それを管理することでバージョン管理対象にします。
ex)logディレクトリ

空のディレクトリの場合には.gitkeepを作成するシェル
`for d in $(find [対象のディレクトリ] -type d -empty); do touch "$d/.gitkeep"; done`


■ディレクトリは管理したいけど、その中のファイルまでは管理したくない場合
ex) logディレクトリは管理しておきたいけど、中のログファイルは管理したくない

.gitignoreファイル（バージョン管理を無視するファイルの設定ファイル）を下記の様に編集（なければ作成）

```
path/to/*
!.gitkeep
```
※無視ファイルの追加はGUIでも可能
privileged なしで GCS を GKE 上にマウントする

# はじめに
コンテナに特権(privileged)を与えずに、 GCS を GKE 上にマウントする手順が思ったより複雑だったため紹介する。
が、 Kubernetes リポジトリにある関連 issue[^1]が解決されるまで、 GKE 上で GCS マウントすることはあまりお勧めしない。

もしマウントする場合、下記いずれかのリスクを受け入れることになることに注意してほしい。
1. GCS をマウントするコンテナに privileged を付与する
2. GCS をマウントするコンテナの AppArmor 設定を解除する
3. AppArmor カスタム設定を読み込む privileged を付与された DaemonSet を作成する

# GCS を GKE 上にマウントする上での課題
Linux システムにおける GCS のマウントは、 [Cloud Storage FUSE](https://cloud.google.com/storage/docs/gcs-fuse?hl=ja)ページにあるように gcsfuse ライブラリを利用すれば簡単に実現できる。
しかし、このライブラリは /dev/fuse volume を利用してマウントを行っているが、 Kubernetes 上のコンテナにおいて /dev/fuse にアクセスするための標準的な手段は提供されていない。

仮にコンテナに privileged を付与すれば node の /dev/fuse にアクセスできるようにはなるものの、何でもできるコンテナが存在するセキュリティリスクを孕んだシステムになってしまう。これを回避するために、 /dev/fuse にアクセスできる device plugins を実装する必要がある。

## smarter-device-manager
[smarter-device-manager](https://gitlab.com/arm-research/smarter/smarter-device-manager) は IoT デバイスの測定結果をアプリに取り込むために作成された device plugins である。 簡単な設定を行うだけで Kubernetes 上のデバイスドライバーへアクセスできるようになる。

これをデプロイし、 GCS をマウントする Pod における拡張リソース名を「smarter-devices/\<volume名\>」のように指定すれば、node の /dev 配下の volume にアクセスできる。

# privileged なしで GCS マウントするための構成
GKE 上で smarter-device-manager を使って MyApp コンテナ上で GCS アクセスするための構成は下記である。

![architecture2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269590/a13fce4a-772d-783b-9cb9-7df08eac1382.png)

詳細な構築手順・設定は github 上[^2]で公開されている。
ただし上記の構成では AppArmor の設定を解除するため、My App Pod に下記記述を追加している。

```
annotations:
        container.apparmor.security.beta.kubernetes.io/<MyApp container名>: unconfined
```

AppArmor は GKE 上でデフォルトで有効になっているコンテナセキュリティ設定であり、これを解除しないと fuse volume にアクセスする際に permission denied エラーになってしまう。

# 残課題
## apparmor 設定が解除されている
リポジトリ[^2]にあるようにカスタム apparmor 設定を作成することは可能だが、設定をロードするための DaemonSet に privileged を付与する必要がある。これはこの記事のテーマに反するため適用していない。

## gcsfuse ライブラリ自体が beta 品質
リポジトリのトップページに記載されているように、 gcsfuse は beta 品質なライブラリである。[kubernetes 上でgcsfuse が動かなくなるトラブル](https://github.com/GoogleCloudPlatform/gcsfuse/issues/366)のような issue が存在していることを理解した上でこのライブラリを活用する必要がある。

# さいごに
「はじめに」で言ったように、 GKE 上で GCS をマウントするのは現時点ではお勧めしない。gsutil や各言語の GCS API を利用した方が、簡単に&品質的に問題のない GCS へのアクセスを実現できる。


[^1]: https://github.com/kubernetes/kubernetes/issues/7890
[^2]: https://github.com/samos123/gke-gcs-fuse-unprivileged
サブネットマスク、ネットワークアドレス、ブロードキャストアドレスの計算

# 問題
192.168.1.68/28が属するサブネットマスク、ネットワークアドレス、ブロードキャストアドレスの値を答えなさい
# 解答
## サブネットマスク
IPv4の場合は32ビットで、/28は左から28ビットが１。よって↓
11111111 11111111 11111111 11110000
第４オクテットを10進数で表すと240
A. 255.255.255.240
## ネットワークアドレス
IPアドレス(2進数)とサブネットマスクをAND演算

IPアドレス(192.168.1.68)を2進数に変換
11000000 10101000 00000001 01000100

AND演算
11000000 10101000 00000001 01000100
11111111 11111111 11111111 11110000
↓
11000000 10101000 00000001 01000000
これを2進数に変換
A. 192.168.1.64

## ブロードキャストアドレス
IPアドレス(2進数)とサブネットマスクを反転したものをOR演算

IPアドレス(192.168.1.68)を2進数に変換
11000000 10101000 00000001 01000100

OR演算
11000000 10101000 00000001 01000100
00000000 00000000 00000000 00001111
↓
11000000 10101000 00000001 01001111
これを2進数に変換
A. 192.168.1.79
[Ruby] あるインスタンスのブロック内で、そのインスタンスのメソッドやインスタンス変数を使う方法

# instance_eval を使って実現
Ruby 3.0.0

## Code 
```ruby 
class Klass 
  def initialize &block 
    @instance_variable 
    instance_eval(&block) unless block.nil? 
  end 
  def instance_method 
    p @instance_variable 
  end 
end 
``` 
## Using 
```ruby 
klass_instance = Klass.new do 
  @instance_variable = :hoge 
  instance_method 
end 
``` 
## Result 
``` 
:hoge 
``` 
# Reference 
- [BasicObject#instance_eval (Ruby リファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/method/BasicObject/i/instance_eval.html)
Python、SeleniumでWebブラウザ操作

#概要

ブラウザを立ち上げ→Qiitaのトップページに移動→検索ボックスにキーワードを入力→検索までの一連の作業をPython, Seleniumを用いて実行するコードを書きました。

このコードを発展させれば、ブラウザ立ち上げ→ログイン自動化→フィルター条件設定→データダウンロードといった一連の作業を自動化できます。

＜環境＞
OS：Windows
Code Editor：Visual Studio Code
python：3.9.2

#準備

##pythonのインストール

以下からOSにあったpythonをインストールする。
https://www.python.org/downloads/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239166/3b5787d1-a4f5-063f-e8b5-360d26c96ff6.png)

VS codeのTerminalに以下を入力し、インストールの確認を行う。

``` bash
py -m pip install selenium
py -V #バージョン確認
```

##seleniumのインストール

VS codeのTerminalに以下を入力し、インストールする。

``` bash
py -m pip install selenium #pipでインストール
```

##ブラウザドライバーのダウンロード

###Google Chromeの場合

google chromeのバージョンを確認する。（右上のアイコン→ヘルプ→Google Chromeについて）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239166/9fffc784-5703-168f-19a0-6339578f35e4.png)
以下から、確認したChromeのバージョンにあったChrome Driverを選択。
OSにあったzipファイルをダウンロードし、解凍する。
https://chromedriver.chromium.org/downloads
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239166/717f52c9-0db9-cd6b-6868-a104ead26d66.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239166/ef426a0f-68b1-2588-6de0-53dfa8b69278.png)

###Microsoft Edge (New Edge)の場合

Google Chromeと同じため、画像は省略。
Microsoft Edgeのバージョンを確認する。（右上のアイコン→ヘルプとフィードバック→Microsoft Edgeについて）
以下から、確認したEdgeのバージョンにあったWebDriverを選択。
https://developer.microsoft.com/ja-jp/microsoft-edge/tools/webdriver/
OSにあったzipファイルをダウンロードし、解凍する。


#コード

F12でQiitaの検索ボックスのHTMLを確認すると、class名がst-RenewalHeader_searchInputであることがわかる。qiita.pyというファイルを作成し、以下のコードを記入。

```python:qiita.py
# coding:utf-8
from selenium import webdriver

# Google Chromeを立ち上げ、Qiitaのトップページを開く。
driver = webdriver.Chrome(executable_path='chromedriver.exeのパス')
driver.get('https://qiita.com/')

# Microsoft Edgeを立ち上げ、Qiitaのトップページを開く。
driver = webdriver.Edge(executable_path='msedgedriver.exeのパス')
driver.get('https://qiita.com/')

# 検索ボックスを取得し、キーワードを入力後、検索
keyword_txb = driver.find_element_by_class_name('st-RenewalHeader_searchInput')
keyword_txb.send_keys('python selenium')
keyword_txb.submit()
```

コードを実行すると、ブラウザが立ち上がり、検索ボックスにキーワード（python selenium）が入力され、結果が表示される。

#つまづいた点

- pythonのバージョン確認 

```bash
python -V #バージョンが表示されない。Python→pyで正しく表示。
Python
```

- seleniumのインストール

```bash
pip install selenium #エラーになる。py -mが必要
```

- ブラウザのバージョンとドライバのバージョンが異なる

ブラウザのバージョンと異なるバージョンのドライバをダウンロードした結果、起動した瞬間、ブラウザが閉じ、VS codeのターミナルにバージョンが異なる旨のエラーメッセージが表示される。

#最後に

クローリング、スクレイピングをする場合、著作権、サーバー側の負荷、利用規約に注意が必要。スクレイピングでダウンロードしたデータを第3者に提供した場合は、著作権法違法になる可能性がある。また、以下のサービスは、利用規約でスクレイピングが禁止されている。
- Twitter
- Instagram
- Amazon
- J-PlatPat 特許情報プラットフォーム
【Swift】Podsをプロジェクトから削除する方法 (unable to load contents of file listのエラーが出た時の対処法)

##1. プロジェクトのディレクトリから、該当のファイルとフォルダを全て削除します。

・Podfile
・Podfile.lock
・/Pods
・xcworkspace

##2.下記項目を削除します。右側の「×」ボタンをクリックすることで削除できます。(自分はこれで治りました)

・[CP] Check Pods Manifest.lock
・[CP] Embed Pods Frameworks
・[CP] Copy Pods Resources

![wp_ios_dev_firebase_build_configuration_pods_remove.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1150411/4d9bdb9d-a35c-7874-516f-100150ce04e6.jpeg)

この方のサイトを参考にさせていただきました。(https://blog.guttyo.jp/apple/ios-app/ios-app-dev/3990/#Pod)
音を鳴らす　Swift

■音や音楽を流す方法


使うもの

import AVFoundation


■使うクラス
このクラスのメソッドで音を鳴らしたり止めたりする
インスタンス化する

```
var player = AVAudioPlayer()
```


♪Bundleクラスを使って音源を指定する。

```
let Path = Bundle.main.bundleURL.appendingPathComponent("音源の名前")
```


♪指定した(Path)の音をAVAudioPlayerに入れ込む。

```
player = AVAudioPlayer(contentsOf:Path)
```

♪play()メソッドで鳴らす

```
player.play()

//ちなみに止めるはこれ
player.stop()
```


■しかしこのままではエラーになる。
do-catch文でエラーの場合を記述するとなおる。

```
do{

 player = try AVAudioPlayer(contentsOf:Path)
 player.play()

 }catch{

 print("エラー")
 }

```


これで大丈夫です簡単にまとめました。
[はじめてのLinux]Linuxにログインする方法

#サーバに接続する方法
sshというコマンドを使う

#sshとは
Secure shellの略
自分のパソコンからサーバに安全に接続する為のプロトコル
ssh以外にサーバに接続する方法として
```telnet```
と言うものを使う方法があるが、telnetだと通信内容が暗号化されないので入力したパスワードをそのまま送ってしまう。
sshは通信内容を暗号化するので、telnetより安全にサーバに接続ができる。
何故ssh接続を使うのかというと通信内容を暗号化しないで接続すると、入力したパスワードが外に漏れる危険性があり万が一漏れた場合にそのパスワードを使って悪意のある人間がサーバに接続しサーバを勝手に編集したりといったことが出来る為

#接続する為のコマンド

```php:ターミナル
ssh testuser@ipアドレス
```
ssh: ```sshコマンド```
test: ```ID（ユーザ名）```
ipアドレス: ```通信の相手先を識別するために割り振られた番号```

#補足
実務上は公開鍵・秘密鍵という物を使った認証をするのでパスワードは使わない
理由
セキュリティ上の問題。パスワードを使った認証にすると、パスワードの管理の仕方が難しい為
(例えばメモ帳に書いたり、付箋にパスワードを書いてパソコンに貼ったりするといった方法だと他人にパスワードが知られてしまう、サーバに接続する際にパスワードを入力している所を第三者に見られたりするとそのパスワードを使って接続出来てしまう為)
nohupを使う (サーバーからログアウトしても、処理が止まらないようにしたい)

### 目的

自分は、サーバーにログインして作業することが多いのですが、

例えば、処理終了まで長い時間がかかるバッチのコマンドを手動で実行している時に

不意に意図せずサーバーからログアウトされてしまう時に困ることがありました。

困ることとは、サーバーからログアウトした場合、手動で実行したコマンドも止まってしまうことです。

今回は、サーバーからログアウトした場合に、手動で実行したコマンドは動き続けるようにする方法を調べたので、紹介します。

### 解決策

解決策は、ズバリ、```nohup```コマンドです。

使用例）

```
$ nohup php hogehoge.php > abc.log &
```

- php hogehoge.php は処理終了まで長時間かかるコマンドの例です
- 出力先を指定しなかったら、`nohup.out` に出力します（使用例では、出力先を```abc.log```としています）
- ```&```はコマンドをバックグラウンドで実行します

### 参考情報

- みんな大好きmanコマンド

```
$ man nohup
```

- [UNIXの部屋 コマンド検索:nohup (*BSD/Linux)](http://x68000.q-e-d.net/~68user/unix/pickup?nohup)
- [Linuxコマンド集 - 【 & 】 コマンドをバックグラウンドで実行する：ITpro](http://itpro.nikkeibp.co.jp/article/COLUMN/20060224/230589/)
【個人開発】ファラオが情報共有し現代社会を生き抜くアプリ作りました

## はじめに
こんにちはファラオ
世は情報社会、多くの人々がスマートフォンを持ち歩きTwitterなどで常に情報交換していますね
しかし、世で使われているSNSは現代の言語で書かれており、ファラオが使うのには少し不便なのではないでしょうか
今回はそんな情報収集に苦労しているファラオ達に使っていただけるSNSを開設しました

## サービス概要
[hieroglitter](https://hieroglitter.herokuapp.com/)
ファラオ達がヒエログリフで情報を共有出来るアプリです
普段お使いの言語を入力すると、ヒエログリフに変換されて投稿されます
これで世界中のファラオと情報を共有出来ますね
※現在は日本語とアルファベットのみに対応しています

## 使い方
こちらがトップページです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/8e317dec-88df-5645-ff51-33d3e8b89c49.png)
ファラオのプライバシーを守るためファラオログイン機能によって平民はフィルタリングしています
安心してお使いください
ファラオの皆さんならどこからファラオログイン出来るか一目瞭然ですね？
こちらがアプリのメインページです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555267/4c103324-a1f4-41cc-c525-163374c9bd71.png)
ここから好きなように投稿してください
[![Image from Gyazo](https://i.gyazo.com/7760dd6c853f63bc3c32bcadbceb3a67.gif)](https://gyazo.com/7760dd6c853f63bc3c32bcadbceb3a67)
お気に入りの投稿を見つけたら猫ボタンを押すと好きなだけあなたの好意を伝えられます
[![Image from Gyazo](https://i.gyazo.com/136e5cfe58c659513612021b3bfe3b8a.gif)](https://gyazo.com/136e5cfe58c659513612021b3bfe3b8a)

## 使用技術について
- Ruby on Rails 6.0.3.5

- Yahoo ルビ振り API
https://developer.yahoo.co.jp/webapi/jlp/furigana/v1/furigana.html
漢字で投稿された際に内部でひらがなに変換しています

- gem 'miyabi'
https://rubygems.org/gems/miyabi/versions/0.1.1
ひらがな、カタカナをアルファベットに変換するのに使わせていただいています

- gem 'pharaoh_lang'
https://rubygems.org/gems/pharaoh_lang
アルファベットをヒエログリフに変換するメソッドto_hieroglyphを提供するgemを自作しました
変換表はこちらを参考にさせていただきました
https://gist.github.com/souri-t/3bb9475c2710a55a10e71337d84ce2c3

## 自作gemについて
アルファベットを対応するヒエログリフに変換して出力するメソッドを提供する単純なgemです
Unicodeはヒエログリフに対応しているので、A-Zまでそれぞれの音が対応するヒエログリフの文字コードをもとに文字列を変換しています
アルファベット以外はそのままの文字を出力します
※デプロイ後にノリで作成したのでせっかくなのでノリで使いました

## おわりに
クソアプリ作ってみたかったので心が満たされました
自分の好きな感じのアプリが作れたかなと思います
就活用に作ってるポートフォリオそっちのけで作ったので、そっちが全然進んでませんが僕は元気です
よかったら遊んでみてくださいね
[hieroglitter](https://hieroglitter.herokuapp.com/)
ちなみにファラオファラオ言ってたらアプリの名前誰も覚えてくれなくてファラオSNSって呼ばれてます
このアプリは[web1week-202102](https://crieit.net/boards/web1week-202102)に参加するために作りました。[だら🎄FlutterとReactとLaravelと風来のシレン](https://twitter.com/dala00)様、良い機会を下さりありがとうございました
Railsチュートリアル6.0に沿って、AWSのCloud9上でRailsの開発環境作ってみた

Railsチュートリアル6.0を実施するにあたって、記載があった手順でAWS Cloud9でクラウド統合開発環境を作ってみました。
Cloud上とPC上で説明が混ざっていたので、こちらにまとめてすっきりさせたい意図で本記事を作成しました。(あと、記事の画面が若干古かったため)

なお、アカウントは既に作成済みで、使用機材はwindowsです。(クラウドなので、差異はない気がしています)

以下が参照した手順
https://railstutorial.jp/chapters/beginning?version=6.0#sec-development_environment

<h2>手順1:AWSのCloud9にアクセスする</h2>
ルートユーザーでログインし、AWSマネジメントコンソールにアクセス。

上の検索窓から「Cloud9」を入力し、Cloud9に遷移します。
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/ec603f9c-7999-f413-abc1-4de1d1093a85.jpeg)

<h2>手順2:「Create environment」を押下</h2>
Cloud9の画面で「Create environment」を押下する。



<h2>手順3:Name environmentを設定</h2>
Name environmentの「Name」と「Description」に適当な値を設定します。
自分の場合は
Name:rails-tutorial
Description:rails-tutorial
と値を設定しました。

値設定後「Next step」を押下します。
![キャプチャ1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/678b7980-fdc7-1f28-5afc-33dbcc089ebd.jpeg)

<h2>手順4:各種設定</h2>
ここでの設定ですが、
Platform：Ubuntu Server 18.04 LTS
を設定して、他はデフォルトのままで「Next step」を押下します。
![キャプチャ2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/720a508c-da37-d73f-fbbd-3c657a4bc864.jpeg)

![キャプチャ3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/334d6256-7143-c044-d924-dfb769bd5209.jpeg)

<h2>手順5:Review</h2>
設定した内容を確認し、「Create environment」を押下。

![キャプチャ4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/267d9a62-5f72-c9ac-8e3a-0dd984224c0c.jpeg)

これでCloud9で開発環境の構築は完了です。
![キャプチャ6.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/92c6125a-f08c-4771-0ad8-5476428e02ea.jpeg)


<h2>補足：インデントのデフォルトを変えておく</h2>
Rubyはインデントに2つのスペースを使うの通例らしいので、エディタのインデント設定をデフォルトから2に変更します。

手順としては、Cloud 9の右上歯車マークを押下し、Project Settingを開きます。
その後、CodeEditer(Ace)のsofttabを2に設定すれば完了です。

![キャプチャ7.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927880/3562196a-4d99-f22a-1cbd-f8213bab4a3c.jpeg)


<h2>Railsインストール</h2>

下記コマンドをCloud 9のターミナルで走らせます。

まず、Rubyドキュメントをインストールしないよう.gemrcファイルを設定します。

```
$ echo "gem: --no-document" >> ~/.gemrc
```

バージョンを指定してRailsをインストールします。(Railsチュートリアルではv 6.0.3をインストールしているのでそれに倣います)

```
$ gem install rails -v 6.0.3
```

下記コマンドを入力して、Railsのバージョンが返ってくれば、Railsのインストールは完了です。

```
$ rails -v
```

<h2>Yarnインストール</h2>
もう一つだけ、javascriptのパッケージマネージャーの「Yarn」をインストールしていきます。

```
$ source <(curl -sL https://cdn.learnenough.com/yarn_install)
```

もし警告メッセージが出た場合は、メッセージ通り下記を入力すればOKです。

```
$ yarn install --check-files
```

こちらもバージョンを確認して、表示されたらOKです。

```
$ yarn -v
```

これでRailsチュートリアルで実施されていたクラウド統合開発環境構築はいったん完了です。

Gitやherokuなどセットアップはまた別の機会に。
Mac の Automator で「親フォルダ名_連番」の一括リネームを行う

# はじめに
iMac 2011 mid がトラブルに見舞われ M1 mini を購入し、とうとう Sandy おじさんを卒業する事となったが、前準備なく 32bit アプリケーション難民となったため代替アプリ探しに四苦八苦していた。

高機能リネームソフト [Shupapan](http://sunsky3s.s41.xrea.com/shupapan/) もそのひとつで、ある程度は [Path Finder](https://cocoatech.com/) 付属のリネーム機能で代用できたが、親フォルダ名を使ったリネームは Path Finder には無かった。（サイト製作時、パーツ毎に連番画像を作る際などに便利）
Shupapan 代替として紹介されているいくつかのアプリはその機能を備えていたが、残念ながら動作など今ひとつしっくり来るものが無かった。

そこで macOS 標準の自動化機能作成アプリ Automator である。

# こんな仕上がり

+ リネームしたいファイルの入ったフォルダを選択してコンテクストメニューから実行
+ 複数のフォルダに対する同時実行が可能
+ フォルダの入れ子には非対応（入れ子フォルダの中は無視される）
+ プレビューやアンドゥなど気の利いた機能は無いので、慎重に扱うファイルは複製後の実行を推奨
+ 数十程度なら問題無いが、フォルダ・ファイル数が増えると重いのであくまで簡易的なもの

# Automator で製作する
Automator の基本的な解説は図解入りの分かりやすいページが多く存在しているので省略。
複数のフォルダを選択した際の動作のために2段階で処理を行う形となる。
設定やスクリーンショットは ver.2.10 (509) のもの。

## コア部分：リネーム処理本体
新規書類で「ワークフロー」を下記の通り作成して保存する。
ここで作成したものを次で呼び出す。

+ シェルスクリプトの内容は `basename "$1"` 
+ 各変数はテキスト形式
+ 「Finder項目を並べ替える」や「Finder項目の名前を変更」のオプション「ワークフローの実行時にこのアクションを表示」をオンにするとリネーム時にダイアログが表示されるので、作成の古いものから連番にする、今回は数値を4桁揃えにするなど、その都度調整可能な汎用型を作る事もできる
+ リネーム対象を限定したい場合は「Finder項目を並べ替える」の前に「Finder項目にフィルタを適用」を追加してフィルタリングを行う [^1]

![am_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73925/bcd1a3b5-e0eb-7271-4243-5e1ba93a8134.png)

## 実行部分：先程のワークフローをバッチで実行
こちらがコンテクストメニューの表示名になるので、分かりやすい名前をつける。
新規書類で「クイックアクション」を下記の通り作成して保存する。
なおクイックアクションは `/Users/ユーザ名/Library(隠しフォルダ)/Services/` に保存される。

+ Path Finder など標準以外のファイルブラウザを利用している場合は、検索対象を「すべてのアプリケーション」または自分が利用しているファイルブラウザにしないとコンテクストメニューに表示されないので注意。

![am_002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73925/2ac0f249-d257-4be4-03a5-a634c17d6697.png)

### 「使用するワークフロー数」について
この数値は複数のフォルダに対して実行した際の「リネーム処理の『初回』同時実行数」となる。
内部処理を追った訳ではなく挙動を観察した上での推測となるが、複数実行の場合「親フォルダ名の収集や並び替え等の前処理 → 各フォルダ内のファイルを実際にリネーム」の順番で処理が行われていると思われる。
そして後者のリネーム処理は「まずワークフロー数と同じ数のフォルダ内のファイルを一気にリネーム、それ以降は1フォルダづつリネーム」という流れになるようだ。
超過分は1つづつの処理となるため16にしているが、同時実行の予定があまり無いなら小さくても構わないと思う。

# Automator メモ
製作中に気づいたことのメモ。
Automator を触るのは今回がほぼ初めてで、触った感じこれっぽい的な内容なので勘違いなどあるかも。ver. 2.10 (509) における話で、バージョンによって挙動が違う可能性もあるので注意。

+ Finder でファイルを選択した状態でサービスメニューを起動した場合、Automator はそのファイルのパスを取得した状態から処理を開始する。
+ そのため、最初に「選択された Finder項目を取得」を置く必要はない。
+ 同様にパス取得のために何か処理を行う必要はなく「変数の値を設定」「クリップボードにコピー」などで、そのままパスを取り出す事ができる。[^2]
+ Automator は最初に取得したパスを対象に処理を行うため、それを変更した場合は対象項目を再指定する必要がある。[^3]
+ 対象項目の再指定は「選択されたFinder項目を取得」で可能 [^4] で、ひとつのワークフローで処理が完結するならその方法で問題ないが、今回のように別のフローから呼び出してバッチ処理させた場合は挙動がおかしくなるため「パスを変数へ格納 → その変数を呼び出す」形式で対象項目を再指定する必要がある。

# おまけ：同階層のファイルを「定型_連番」にリネーム
同じフォルダ内のファイルを定型連番にリネームしたい場合に。
選択したファイルではなく、同階層のファイル・フォルダを全てリネームするので注意。
新規書類で「クイックアクション」を下記のように作成して保存する。

+ シェルスクリプトの内容は `dirname "$1"` 
+ リネーム対象を限定したい場合は「Finder項目を並べ替える」の前に「Finder項目にフィルタを適用」を追加してフィルタリングを行う [^1]

![am_003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73925/abb1fadb-447d-ce20-8d63-5fa56564d6c0.png)

[^1]: フィルタリングには Spotlight 情報を利用するため、リネームしたいファイルが「システム環境設定 ＞ Sporlight ＞ プライバシー」に登録されている場所にある場合はリネームを実行できないので注意。
[^2]: 例えば「クリップボードにコピー」を1つ置くだけで選択項目のパスをコピーするサービスメニューを作る事ができる。
[^3]: 今回の場合シェルスクリプトでパスをファイル名に変換した時点で Automator は現在位置を見失う形となる。これに気づかずしばらくハマった。
[^4]: ただし Path Finder など macOS 標準以外のファイルブラウザの場合、この方法では再指定する事はできない。
（要注意）CoroutineのasyncはConcurrencyが必ずParallelismではないこと

COROUTINEの並列実行の速度測定する為、
本来FLUX(reactor-core)の部分をCOROUTINEに変える時の発見なんです。

タスクはあるウェブサイトから画像264枚をダウンロードし、JPGファイルとして保存する。
私のノートパソコンはWINDOWS 10、AMD Ryzen 5 3500Uだから
以下の処理は8 Threadの並列実行になります。

###FLUX(reactor-core)

        Flux.fromIterable(taskStream.map {
            Mono.fromCallable { it.invoke() }.onErrorResume { Mono.empty() }
        })
            .parallel()
            .runOn(Schedulers.parallel())
            .flatMap { x: Mono<Boolean> -> x }
            .subscribe(onProcess, whenError, whenFinish)

####結果
．．．
/galleries/1809504/262.jpg
/galleries/1809504/263.jpg
/galleries/1809504/264.jpg
Calculation of Flux time :53075 milliseconds

###Coroutine Async
 [Kotlin Coroutine 入門2: 並列実行と Structured Concurrency と例外](https://qiita.com/wm3/items/72dedaa85ef1ed5eabeb)　を参考して以下を作成。

            taskStream.map { activeScope.async { it.invoke() } }.awaitAll()

####結果
．．．
/galleries/1809504/262.jpg
/galleries/1809504/263.jpg
/galleries/1809504/264.jpg
Calculation of Coroutine time :340325 milliseconds

###原因
え。。Fluxより6.41倍の時間も掛かった。これは間違いなく1 Threadの実行結果。
でも上の記事ではasyncを使って2秒のタスク二つを２秒で完成したはず。これは何のことでしょう！？

答えはここに見つけた：　
[CONCURRENT COROUTINES – CONCURRENCY IS NOT PARALLELISM](https://kotlinexpertise.com/kotlin-coroutines-concurrency/)
簡単に言うとKotlin Coroutine 入門2の例は実際はdelay(2000)を使って何にもしてないからだ。
Coroutineでは1ThreadでもdelayとかawaitとかSuspend関係のコードがあったら、
その暇を利用して他のタスクを実行する！だからdelay(2000)の二つは2.165秒で完成出来た。
しかし、実際CPUの計算を2秒掛かったタスクの場合は4秒以上になってしまう。
入門2の例は並行処理だ、並列処理ではない。

###並列処理
CONCURRENCY IS NOT PARALLELISMからは実際の並列実行（Parallel Coroutines）を必要の条件も書いてる
####1 Run in GlobalScope　（GlobalScopeを使う）
    val one = GlobalScope.async { doSomethingUsefulOne() }
    val two = GlobalScope.async { doSomethingUsefulTwo() }

####2 Specify a coroutine dispatcher （dispatcherを指定する）
    val one = async(Dispatchers.Default) { doSomethingUsefulOne() }
    val two = async(Dispatchers.Default) { doSomethingUsefulTwo() }


###改正
早速改正して

            taskStream.map { activeScope.async(Dispatchers.Default) { it.invoke() } }.awaitAll()

####結果
．．．
/galleries/1809504/262.jpg
/galleries/1809504/263.jpg
/galleries/1809504/264.jpg
Calculation of Coroutine time :56176 milliseconds

これはFluxと完全一致の並列実行速度。そしてこの簡潔な表現、Kotlinやっぱり素晴らしい！
pygameでゲームを作ってみる2＜Spriteクラス編＞

#はじめに
限界を感じた私はSpriteクラスに手を出すことにした...

#進捗
Spriteクラスを使った処理になった。
###できること
・テキストを表示する
・残り時間をカウントし、表示する
・得点を計算し、表示する
・画像を表示する
・画像上をクリックしたときに「+1」を表示する
・画像上をクリックしたときに1点加える

###できなくなったこと
・画像を一方向に動かす

###コード全文

```python
import pygame
from pygame.locals import *
import sys

Scr_rect = Rect(0,0,500,500)

class MySprite(pygame.sprite.Sprite):
    def __init__(self, filename, x, y, vx, vy):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load(filename).convert_alpha()
        width = self.image.get_width()
        height = self.image.get_height()
        self.rect = Rect(x,y,width,height) 
        self.vx = vx
        self.vy = vy
    
    def draw(self, screen):
        screen.blit(self.image, self.rect)

def main():
    pygame.init()
    cl = pygame.time.Clock()
    screen = pygame.display.set_mode(Scr_rect.size)
    pygame.display.set_caption("Game")

    font = pygame.font.SysFont(None, 78)
    font2 = pygame.font.SysFont(None, 45)
    font3 = pygame.font.SysFont(None, 30)
    text1 = font.render("Click!", True, (204, 102, 112))
    text2 = font2.render("+1", True, (0, 0, 0))

    #Spriteを作成
    ob1 = MySprite("Game\dg.jpg",50,100,0,0)
    
    click = 0
    score = 0
    n_frames = 0
    TIME_END = 15
    
    while (1):
        screen.fill((102, 204, 194)) 
        screen.blit(text1, [170, 30])
        
        rest = TIME_END - (n_frames / 30)
        text4 = font3.render('time: {}'.format(round(rest,1)), True, (0, 0, 0))            
        screen.blit(text4,[350,60])

        #Spriteを更新
        ob1.update()

        #Spriteを描画
        ob1.draw(screen)

        for event in pygame.event.get():
            if event.type == QUIT:
               pygame.quit()
               sys.exit()
            if n_frames < 30 * TIME_END and event.type == MOUSEBUTTONDOWN and event.button == 1:
                if ob1.rect.collidepoint(event.pos):
                    click = 9
                    score += 1
        text3 = font3.render('score : {}'.format(score), True, (0, 0, 0))
        screen.blit(text3, [350, 40] )

        if click > 0:
            pos = pygame.mouse.get_pos()
            screen.blit(text2, pos)
            click -= 1

        pygame.display.update()
       
        if n_frames < 30 * TIME_END:
             n_frames += 1

        cl.tick(30)

if __name__ == "__main__":
    main()
```

#おわりに
Spriteクラスを使って、できることを増やしていく。
ElectronのexecuteJavascriptでError: Script failed to executeが出る件

# はじめに
初投稿です...
ElectronのBrowserViewでexecuteJavascriptを使った際、一見正しそうなスクリプトがエラーを吐く問題で少々悩んだので備忘録として。

<a id="#Chapter1"></a>
# 問題のソースコード
要素の取得に失敗したらfalse、成功したら真を返したい

```
      const result = await browserView.webContents.executeJavaScript(`
        const anko = document.getElementById("anko"); 
        return anko == null ? false : true;
      `)
```

<a id="#Chapter2"></a>
# 正しいソースコード
どうやらreturnで返しちゃだめらしいです。（そんなん知らんよ…）
最後に評価された値が返却されるらしい。

```
      const result = await browserView.webContents.executeJavaScript(`
　　　　 let val = true;
        const anko = document.getElementById("anko"); 
        anko == null ? false : true;
      `)
```
<a id="#reference"></a>
# 現在作っているアプリ
Youtube Live用のコメントビュワーです（Electron製）（ベータ版）
配信する方はぜひどうぞ
- [https://tubug.netlify.app](https://tubug.netlify.app)
UserDefaultsの使い方

■主にデータを保存する機能
アプリを落としてもデータは保存されている。
軽量のデータを保存する機能




使い方
■データを保存する方法

``` swift
UserDefaults.standard.set(保存したい値,forKey:"キーの名前")
```



■データを取り出す方法(読み込み)

``` swift
//int型
UserDefaults.standard.integer(forKey:"キーの名前")

// Float型
userDefaults.standard.float(forKey: "キーの名前") 

// Double型               
userDefaults.standard.double(forKey: "キーの名前") 

// Bool型             
userDefaults.standard.bool(forKey: "キーの名前") 

 // URL型                 
userDefaults.standard.url(forKey: "キー")

 // 文字列型                   
userDefaults.standard.string(forKey: "キー")   

 // 文字型配列          
userDefaults.standard.stringArray(forKey: "キー")

//汎用メソッド上記に当たらない型の場合
 UserDefaults.standard.object(forKey: "キー") as? 型

//汎用メソッド上記に当たらない配列の型の場合
userDefaults.array(forKey: "todoList") as? [型]

```




■消去する方法

特定のデータの消去

``` swift
UserDefaults.standard.removeObject(forKey:"キーの名前")

//todoリストのように配列の場合
//消去メソッドに新たに保存機能を渡す ある中身を消去した配列を新たに保存する。 やないと配列ごと消える。
UserDefault.standard.set(配列名, forkey:"キーの名前")
```


全部のキーを消す

``` swift
let Domain = Bundle.main.bundoleIdentifier
UserDefaults.standard.removePersistentDomain(forName:Domain!)
```
Python Redmineでチケットを抽出する

Python Redmineでチケットを抽出したいとき、使用できるパラメータについてです。

[公式](https://python-redmine.com/resources/issue.html#filter)

|項目|説明|
| :- |  :- | 
|issue_id|チケットのID。 (複数の場合,で区切る)|
|parent_id|親チケットのID|
|project_id|プロジェクトのID|
|subproject_id|サブプロジェクトのID。project_id=X, subproject_id=!*と指定するとプロジェクトのissueのみを取得してサブプロジェクトの方は取得しない。|
|tracker_id|トラッカーID|
|query_id|クエリID|
|status_id|ステータスID ※指定しないと未完了のチケットのみ取得する。open ：未完了のチケットclosed ：終了したチケット* ：すべてのチケットid ：IDで指定|
|assigned_to_id|チケットをアサインされているユーザのID。"me"を指定すると自分にアサインされているチケットが取得される|
|cf_x|xの箇所にカスタムフィールドのIDを入れ、値を指定してチケットを取得する。値の前に〜を入れると、カスタムフィールドにその文字列を含むチケットを取得できる。|
|sort|ソートするカラムを指定する。:desc をつけると逆順になる。|
|limit|取得するチケット数の上限を指定する|
|offset|取得開始位置を指定する。|
|include|関連チケットや添付ファイルを取得する|

下記の例ではプロジェクトIDがproject_A、カスタムフィールド(IDが5のもの)の値が「稼働中」のものと「構築中」のもの、トラッカーIDが1のものが抽出されます。

```python
issues = redmine.issue.filter(
    project_id='project_A',
    cf_5='稼働中|構築中',
    tracker_id='1'
)
```

参考：

https://qiita.com/mima_ita/items/1a939db423d8ee295c85
AWS APIGatewayでパスパラメータの設定を行う

# はじめに

AWS APIGatewayでAPIを作成する際、URLのパスをパラメータにするやり方を紹介します。

具体的にはこういう感じにしたい場合ですね。

```
https://xxx.execute-api.ap-northeast-1.amazonaws.com/test/10001
```
こうではありません。

```
https://xxx.execute-api.ap-northeast-1.amazonaws.com/test?tokenId=10001
```

詳細な手順は公式の以下に書いてありますが、ちょっとわかりづらかったので、やさしくまとめました。
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/integrating-api-with-aws-services-lambda.html#api-as-lambda-proxy-expose-get-method-with-path-parameters-to-call-lambda-function

# AWS APIGatewayでパスパラメータの設定を行う

## GETメソッドを作る

とりあえず普通にGETを作ります。
「アクション」→「メソッドの作成」→GET

## リソースを作る

次に、リソースを作ります。
「アクション」→「リソースの作成」
ここでポイントは、<b>リソース名、リソースパスを{tokenId}</b>のように設定することです。
※tokenIdはパラメータ名をセット

![スクリーンショット 2021-03-07 23.46.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117960/636c183f-cf6a-087b-6030-ce0f1c651357.png)

## リソース配下にGETメソッドを作る

さらにリソース配下にGETメソッドを作ります。
「アクション」→「メソッドの作成」→GET

統合リクエスト
↓
マッピングテンプレートの追加
↓
application/json
↓

```
{
   "tokenId": #if($input.params('tokenId')=='%2F')"/"#{else}"$input.params('tokenId')"#end
}
```

%2FはURLのエンコーディングを考慮しているようです。

あとはAPIをデプロイしてできあがり。
Gentoo Linux構築の手順　パート2

# 目的
Gentoo Linuxの[ハンドブック](https://wiki.gentoo.org/wiki/Handbook:AMD64/Full/Installation/ja#Portage.E3.82.92.E8.A8.AD.E5.AE.9A.E3.81.99.E3.82.8B)をもとにgentoo linuxを開発しました
ハンドブックでは分かりづらいため、他の方が作る際に少しでも助けになればいいとおもい記事にしました。
・[Gentoo Linux構築の手順　パート1](https://qiita.com/Goto_Kazuhide/items/9119186f9fb1bede1009)
・[Gentoo Linux構築の手順　パート2](https://qiita.com/Goto_Kazuhide/items/07eb5304c806a2dad793)
・[Gentoo Linux構築の手順　パート3](https://qiita.com/Goto_Kazuhide/items/cf671bf65f5f984abf21)
・[Gentoo Linux構築の手順　パート4](https://qiita.com/Goto_Kazuhide/items/fafe5ba5188f44be0433)
・[Gentoo Linuxを構築する上での用語集](https://qiita.com/Goto_Kazuhide/items/1ea550fe910255b1d24e)


# GPTとMBRのどちらにすべきか
```
fdisk -t dos /dev/sda
```
# parted で現在のパーティションレイアウトを表示する
```
parted -a optimal /dev/sda
```
# GPTラベルを適用する
下記コマンドをたたくとディスクすべてのデータが失われます。
僕はここで勘違いし、データだけが失わるとおもいましたが、WindowsというOS自体使えなくなりました。
WindowsというOSについては復元可能ですが、OS自体抹消されることをわかった上で下記コマンドをたたく必要があります。

```
parted -a optimal /dev/sda
```

# パーティションを作成する
（parted -a optimal /dev/sdaをたたいたあとでなければ、下記についてはたたけません）

```
unit mib
```
GRUB2ブートローダが使用する2MBのパーティションを作成します

```
mkpart primary 1 3
name 1 grub
set 1 bios_grub on
print
```

同じやり方でブートパーティション（128MB）、スワップパーティション（8192MB＝8GB）、残りのディスクすべてを占めるルートパーティションを作成します

```
mkpart primary 3 131
name 2 boot
mkpart primary 131 8323
name 3 swap
mkpart primary 8323 -1
name 4 rootfs
```

ハンドブックではスワップパーティションは512MBであるが、通常、スワップ領域と言うのは、メモリサイズ以上のサイズが必要です。
私のPCは4GBあるから余裕を持って、8GBにしています

```
set 2 boot on
print
```
printでパーテション構成に問題ないこと確認できたら下記コマンドでpartedを終了します。

```
quit
```

# パーティションにファイルシステムを適用する
```
mkfs.vfat /dev/sda1
mkfs.vfat /dev/sda2
mkfs.ext4 /dev/sda3
mkfs.ext4 /dev/sda4
```
sda1をvfatにするのは
ハンドブックでは、一例としてsda1を「BIOS ブートパーティション」として使用する形が提示されていますが、
そのファイルシステムを何にするか、については明記されていないため
そこで、最も汎用的なvfat（FAT32）にしました
sda3がext4であるのはメインで使用する sda4 に合わせるためです
他のフォーマットを使用しても大丈夫らしいです

# スワップパーティションを有効にする
mkswapはスワップパーティションを初期化するために使われるコマンドです：

```
mkswap /dev/sda3
```
スワップパーティションを有効化するには、swaponを使います

```
swapon /dev/sda3
```
上述のコマンドで、スワップを作成して有効化しましょう

# ルートパーティションのマウント
```
mount /dev/sda4 /mnt/gentoo
```
ハンドブックではsda3をマウントしておりましたが、sda3をマウントしようとするとswapのためマウントできないと言った
エラーメッセージが英語で表示されるためここではsda4をマウントします。


# stage tarballをインストールする
現在の日付と時刻が正しいか確認

```
date
```

# stage tarball をダウンロードする

```
cd /mnt/gentoo
links https://www.gentoo.org/downloads/
```

下の方に行くと（下キー25回くらい）stage3というリンクがあるのでEnterで入る

バージョンの指示は無いので、
画面に表示されているstage3-amd64-20210224T214503Z.tar.xzを選択

# stage tarball を展開する
```
tar xpvf stage3-*.tar.xz --xattrs-include=‘*.*’ --numeric-owner
```
tar.xzの部分についてはstage tarballをダウンロードした拡張子によって変更してください

# コンパイルオプションを設定する

## はじめに
```
nano -w gentoo/mnt/etc/portage/make.conf
```


## CFLAGS と CXXFLAGS とMAKEOPTS
```
COMMON_FLAGS=“-march=native -O2 -pipe”
CFLAGS=“${COMMON_FLAGS}”
CXXFLAGS=“${COMMON_FLAGS}”
MAKEOPTS=“-j2"
```

※MAKEOPTS=“-j2”の2の値は自分のPCのCPU数（コア数）に応じて変えてください
C++の開発環境を用意から"Hello World"をコンパイルして実行する【Mac編】

### C++の開発環境を用意から"Hello World"をコンパイルして実行する【Mac編】


#### 1.[Visual Studio Code](https://code.visualstudio.com/download)をダウンロード

![Qiita_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/904294/834b5d3e-470f-1255-3d71-64657f39fc73.png)

#### 2.Visual Studio Codeの拡張機能をクリック

![スクリーンショット 2021-03-07 23.32.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/904294/bbe46ac5-2b58-623b-6eaf-9015f9958dc3.png)

#### 3.C/C++をインストール

![スクリーンショット 2021-03-07 23.35.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/904294/b7c1c2ff-8029-cce8-484b-954e31954017.png)

#### 4.RunCoderをインストール

![スクリーンショット 2021-03-07 23.35.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/904294/9eb7b9f2-7a61-0643-23b8-cdc226232011.png)

#### 5.ファィル作成→今回はindex.cppと記載（ソースコードは下記を参照)

```
#include <iostream>

int main(void){
    printf("hello world");
    return 0;
}
```


#### 6.右上の▶️ボタンをクリック（このボタンをコンパイラと言います。)

![スクリーンショット 2021-03-07 23.47.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/904294/c31290cb-eb5b-a22f-b598-8925c7cc82bf.png)

#### コンパイル完了です！

![スクリーンショット 2021-03-07 23.40.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/904294/6d8ea4bb-9040-3505-ecb3-ea7624d6c2a5.png)
データベースの正規化(情報処理試験によく出るやつ)

## DBの正規化とは
DBで扱うデータの冗長性を排除し、独立性を高めることです。これにより、DBのメンテナンスが容易になります。
しかし、きちんとしたルールに基づいて正規化を行わなければ整合性や一貫性を確保できなくなります。

## 正規形のざっくりとした説明
・非正規形
1レコード内に繰り返し項目を持っている。データベースに格納して、SQLで操作して...という処理ができない。

・第1正規形
キーを設定し、テーブルの繰り返し項目を取り除く。

・第2正規形
部分関数従属性を別テーブルに分離。

・第3正規形
推移関数従属性を別テーブルに分離。

## 非正規形
以下のような伝票テーブルがあったとします。(マークダウンで書けなかったので、Excelで作ったやつのスクショです。)
1レコードの中に商品コード、商品名、単価、数量が複数入力されています。これが繰り返し項目です。
1レコードの中にデータが混在しているため、MysqlなどのRDBMSではこのテーブルを管理することができません。
![スクリーンショット 2021-03-07 23.15.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429077/9f5f32e8-da77-706a-1704-86d8efd85251.png)


## 第1正規形
繰り返し項目を排除し、伝票番号と商品コードごとに1レコードとします。これが第1正規形です。
レコード数は多くなりますが、伝票番号と商品コードを主キーとして伝票を一意に表すことができます。

| 伝票番号 | 担当者ID | 担当者 | 商品コード | 商品名 | 単価 | 数量 |
|:--|:--|:--|:--|:--|:--|:--|
| 1 | 001 | 田中 | A1 | 机 | 10000 | 2 |
| 1 | 001 | 田中 | B1 | 椅子 | 20000 | 3 |
| 1 | 001 | 田中 | C1 | 本棚 | 5000 | 4 |
| 2 | 002 | 鈴木 | A1 | 机 | 10000 | 2 |
| 2 | 002 | 鈴木 | D1 | テレビ | 50000 | 1 |

## 第２正規形
部分従属性を無くすことで、テーブルを第2正規形にすることができます。
部分従属性とは、主キー(今回の場合は伝票番号と商品ID)の一部からレコードを特定できる事です。
第１正規形の伝票テーブルを見ると、商品IDから商品は特定できることがわかります。
そのため、伝票テーブルは以下のように伝票テーブルと商品テーブルに分けることができます。


・伝票テーブル(商品名カラムと単価カラムは商品テーブルに分離)

| 伝票番号 | 担当者ID |担当者 | 商品コード | 数量 |
|:--|:--|:--|:--|:--|
| 1 | 001 | 田中 | A1 | 2 |
| 1 | 001 | 田中 | B1 | 3 |
| 1 | 001 | 田中 | C1 | 4 |
| 2 | 002 | 鈴木 | A1 | 2 |
| 2 | 002 | 鈴木 | D1 | 1 |

・商品テーブル(New!)

| 商品コード | 商品名 | 単価 |
|:--|:--|:--|
| A1 | 机 | 10000 |
| B1 | 椅子 | 20000 |
| C1 | 本棚 | 5000 |
| A1 | 机 | 10000 |
| D1 | テレビ | 50000 |

## 第3正規形
推移的従属性を無くすことで、テーブルを第3正規形にすることができます。
推移的従属性とは、主キー以外からレコードを特定できる事です。
第2正規形の伝票テーブルを見ると、担当者IDから担当者は特定できることがわかります。
そのため、第３正規化を行うことで、以下のように伝票テーブル、商品テーブル、社員テーブルに分けることができます。

・伝票テーブル(担当者カラムは社員テーブルに分離)

| 伝票番号 | 担当者ID | 商品コード | 数量 |
|:--|:--|:--|:--|
| 1 | 001 | A1 | 2 |
| 1 | 001 | B1 | 3 |
| 1 | 001 | C1 | 4 |
| 2 | 002 | A1 | 2 |
| 2 | 002 | D1 | 1 |

・社員テーブル(New!)

| 担当者ID | 担当者 |
|:--|:--|
| 001 | 田中 |
| 001 | 田中 |
| 001 | 田中 |
| 002 | 鈴木 |
| 002 | 鈴木 |

・商品テーブル

| 商品コード | 商品名 | 単価 |
|:--|:--|:--|
| A1 | 机 | 10000 |
| B1 | 椅子 | 20000 |
| C1 | 本棚 | 5000 |
| A1 | 机 | 10000 |
| D1 | テレビ | 50000 |
おとをだすVue

備忘録
取り急ぎ（エレメントの指定してる部分とか省略）

```js
var watcher_vm = new Vue({
    el: '#wrapper',
    data: {
        // データ系他で使うとこあればここ
    },
    methods: {
        playSound: function() {
            let se = $('#btnsound');
            console.log(se);
            se[0].currentTime = 0;
            se[0].play();
    },
```


```html
    <a id="link" v-on:click="playSound()">音声鳴らす</a>
    <audio id="linksound" preload="auto">
        <source src="{{asset('/ongaku.mp3')}}" type="audio/mp3">
    </audio>
```
【PHP学習メモ】PHPUnitが実行できない時

　PHPUnitを実行しようとするとエラーが発生する、ということがありました。
　忘れないように自分用のメモとして残しておきます。

　※ 今回はこちらの記事を参考にさせて頂きました:relaxed::tulip:
　https://blog-s.xchange.jp/?p=1152

　__・PHPUnitを実行する__
　phpunit コマンドを実行してユニットテストを行います。
　phpunitはPHPUnitをインストールしたディレクトリのvendor\binフォルダ配下にシンボリックリンクがあります。

　全てのテストケースの実行、特定のテストケースだけ実行どちらもできます。

　__例 )__ 　以下のようなディレクトリ構成で全てのテストケースを実行する場合


```
src                              tests                        vendor
`-- CoordinatePoint.php          `-- CoordinateTest.php       `-- bin
`-- PeopleCoordinatePoint.php    `-- TyphoonTest.php              `-- phpunit
`-- RadiusPoint.php                       
`-- Typhoon.php

※ 一部省略しております。
```
　次のコマンドで実行することができます。

```
vendor/bin/phpunit tests/
```

 __■ 注意 ■__
　コマンドを実行するディレクトリを確かめましょう。


```
vendor/bin/phpunit tests/
zsh: no such file or directory: vendor/bin/phpunit
```
React Hooks

##useState()

・ステートフックと呼ばれる
・クラスコンポーネントでいう
　this.stateとthis.setState()を代替
・複数のstateを扱う時はstate毎に宣言

useState関数をインポート

```
import React, { useState } form 'react';
```
宣言する

```
const [stateの変数名, stateを変更させる関数名] = useState(stateの初期値);
```
JSX内で使う

```
const App = () => {
 stateを変更させる関数名(変更したstateの値)
};
```

useStateの実用例

```
import React,{useState} from 'react';

const Article = (props) => {
    const [isPublished, togglePublished] = useState(false);
    return(
        <div>
            <input type="checkbox" checked={isPublished} id="check" onClick={() => togglePublished(!isPublished)}/>
        </div>
    )
};

export default Article
```

## useEffect
・ライフサイクルメソッドを代替できる
・Functional　Componentでライフサイクルを使える
・コードをまとめられる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/249640/897ae326-0626-fc00-05e1-843292102dbd.png)
###①レンダー毎
・基本の形
・useEffect（）内にCallback関数を書く
・Callbackはレンダー毎に呼ばれる
・returnするCallback関数はアンマウント時に呼ばれる。（クリーンアップ関数）

```
useEffect(() => {
    console.log('render!')  
    // ここはレンダーごとに呼ばれる 
    return () => {
        console.log('unmounting!')
    }
})
```
###②マウント時のみ
・第二引数の配列内の値を
　前回レンダーと今回レンダーで比較
　→変更があればCallback関数を実行
・第二引数に空の配列を渡すと
　最初の１回（マウント時）のみ実行される

```
useEffect(() => {
    console.log('render!')
}, []) 
```
###③マウント＆アンマウント
・①と②の複合形
・通常のCallbackはマウント時のみ
・アンマウント時はreturn内のクリーンアップ関数が実行される

```
useEffect(() => {
    console.log('render!')
    return () => {
        console.log('unmounting!')
    }
}, [])
```
###特定のレンダー時
・マウント時に実行される
・limitの値が変わった時に実行される

```
const [limmit, release] = useState(true);
useEffect(() => {
    console.log('render!')
    return () => {
        console.log('unmounting!')
    }
}, [limit])
```
eXtrme Gammon を M1 Mac で動かす

# eXtrme Gammon を M1 Mac で動かす

## 環境

- MacBook Air (M1, 2020)
- macOS Big Sur 11.2.2
- homebrew (x86_64 版)

## はじめに

基本的に以下は全て Rosetta2 を使って行ったので、ターミナルは Rosetta2 で起動する。

x86_64 となっていれば Rosetta2 で起動できている。

```zsh
$ uname -m
x86_64
```

## wine-crossover のインストール

wine-crossover を使うと 32bit のアプリ（eXtreme Gammon）を動かすことができる。

crossover 版以外の wine では 64 bit のものしか動かせないので注意。

https://github.com/Gcenx/homebrew-wine

```zsh
$ brew tap gcenx/wine
```

```zsh
$ brew install --cask --no-quarantine wine-crossover
```

### 日本語フォント

`wget` を使って `winetricks` をダウンロード

https://wiki.winehq.org/Winetricks

```zsh
$ ./winetricks fonts fakejapanese
```

## eXtreme Gammon をダウンロード

http://www.extremegammon.com/download.aspx

`eXtreme Gammon is a Windows only program, continue?` と聞かれるが気にせずダウンロード

```zsh
$ wine32on64 ./xg2install.exe
```

インストーラにしたがってインストールする。

ホームディレクトリ内に `.wine` というディレクトリが作成されておりその中にある `drive_c` が Windows の C ドライブに相当する。

```zsh
$ wine32on64 ~/.wine/drive_c/Program\ Files\ \(x86\)/eXtreme\ Gammon\ 2/eXtremeGammon2.exe
```

とすると eXtreme Gammon が動く。

毎回これを実行するのは大変なので `.zshrc` などに適当なエイリアスを作成する。

```zsh
alias xg2='wine32on64 ~/.wine/drive_c/Program\ Files\ \(x86\)/eXtreme\ Gammon\ 2/eXtremeGammon2.exe'
```

これで次回以降はターミナル上で `xg2` と打つだけで起動するようになる。

## 問題点

Windows を介していないため起動が早い、仮想化ソフトを使う必要がないなどのメリットはあるが、スペックの割に遅い。

私の知る限りでは現在（2020/3/7）M1 Mac で eXtreme Gammon を動かす唯一の方法です。

他に方法がある、または、Wine を使ってももっと早く動かせるなどあれば教えてください。

![スクリーンショット 2021-03-07 22.41.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/778510/7a3046cc-f873-1f28-869c-6bcceec36c0a.png)

## 参考サイト

- https://github.com/Gcenx/homebrew-wine
- https://qiita.com/asfdrwe/items/24cf3759edeb3fe56bc0
- https://www.sparxsystems.jp/help/15.0/enterprise_architect_macos.html
- https://qiita.com/TomokiYamashit3/items/e6752fd7ec1e4c3f4ea7
Apple developer documentにおけるバージョン違いのエラー


初めまして、R_tattsu2060です。
会社員（事務職）と一児の父をしながら隙間時間でswiftを独学しています。

今回初めてのQiita投稿になります。

Apple developer documentで「FoodTracker」というサンプルアプリのチュートリアルを実行しているとバージョンの違いからエラー表示が出ました。

[Start Developing iOS Apps (Swift)](https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/WorkWithViewControllers.html#//apple_ref/doc/uid/TP40015214-CH6-SW1)(2016)

「UIビューを操作する」imagePickerControllerのコードより抜粋。

2016年版

```Swift
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

    guard let selectedImage = info[UIImagePickerControllerOriginalImage] as? 
    UIImage else {
        fatalError("Expected a dictionary containing an image, but was provided the following: \(info)")
    }
}
```
これを以下のように修正するとビルドできるようになります。

2021年版

```swift
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    
        guard let selectedImage = info[UIImagePickerController.InfoKey.originalImage] as?
        UIImage else {
            fatalError("Expected a dictionary containing an image, but was provided the following: \(info)")
}
```

シュミレーターでImageView画面をタップしても何も反応がない場合は、
inspector - [interaction]Usre Interaction Enabledにチェックを入れると解決します。 


ちなみに同じことで悩んでいた方が相談していたサイトがあったので追記しておきます。
[Apple Tutorial Bug](https://forums.swift.org/t/apple-tutorial-bug/27235)
※英語サイトです

以上です。
jQueryでCSSやテキストを変更させたい

#はじめに
jQueryって、HTMLやCSSに対して、視覚的な変更を行うことができるから、触っててわかりやすいと感じました。

##CSSを操作する

```javascript
$('セレクタ').css('プロパティ','値')
```
`.css`の引数に、どのプロパティに対して、どんな値に変えるのかを記述します。

##textを操作する

```javascript
$('セレクタ').text('何に変更するか')
```
HTML内のテキストを変更することができます。
`.text`の引数にどう変更させるのかを記述します。
CentOS8にオフラインで最新のpython3.X系をインストールする

#0.はじめに
RHEL8では /usr/bin/{python,python2,python3} が存在しないことがある
今回はインターネットに接続しない環境でpython3.x系をインストールする。
pythonとpipをインストールすることでAnsibleの勉強も進めていきたい。(←こちらはまた別の記事で)

```
$ python --version
-bash: python: command not found
```

#1. 依存関係

#６．必要なパッケージのインストール
コンパイルに必要

```
# yum -y install make gcc
```

Python、pipインストールに必要。

```
# yum -y install zlib-devel openssl-devel tk-devel libffi-devel
```


#2.ソースコードのダウンロード
以下のサイトからpythonのソースコードをインストールする。
https://www.python.org/downloads/source/
※今回はPython-3.9.0.tar.xzをインストール

#3.サーバに配置
ダウンロードしたファイルをサーバに配置

```
#ls -ltr
-rw-r--r--. 1 root root 18866140 11月 23 19:32 Python-3.9.0.tar.xz
```

#4.解凍
ダウンロードしたファイルを解凍。

```
# tar Jxvf Python-3.9.0.tar.xz
```

#5.解凍したフォルダに移動
以下コマンドで回答したフォルダに移動。

```
cd Python-3.9.0
```

#6.makefileの作成

```
./configure --prefix=/usr/local/python390 --with-ensurepip
```

**注意点1**
実行前に、gccが入っていることを確認すること。

```
# gcc --version
gcc (GCC) 8.3.1 20191121 (Red Hat 8.3.1-5)
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```

gcc等のCコンパイラが含まれていない場合は以下のエラーが発生するので注意。

```
# ./configure --prefix=/usr/local/python390 --with-ensurepip
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for python3.9... no
checking for python3... python3
checking for --enable-universalsdk... no
checking for --with-universal-archs... no
checking MACHDEP... "linux"
checking for gcc... no
checking for cc... no
checking for cl.exe... no
configure: error: in `/work/python/Python-3.9.0':
configure: error: no acceptable C compiler found in $PATH
See `config.log' for more details
```

**注意点2**
OpenSSLのバージョンについて、pip3.9を利用する際には、OpenSSL1.0.2以上のインストールが必要

#７．make

```
# make
```

**注意点**
事前にmakeコマンドが利用できることを確認しておくこと

```
# make -v
GNU Make 4.2.1
このプログラムは x86_64-redhat-linux-gnu 用にビルドされました
Copyright (C) 1988-2016 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 以降 <http://gnu.org/licenses/gpl.html>
これはフリーソフトウェアです: 自由に変更および配布できます.
法律の許す限り、　無保証　です.
```

#８．インストール

```
# make install
Successfully installed pip-20.2.3 setuptools-49.2.1
```

※make install の代わりに、make altinstallを利用することが推奨されている。
既存のpython、python3ｗ置き換えるシンボリックリンクを作成しないため。
altinstallを利用することで、既存の環境を壊さず異なるバージョンのpythonをインストールすることができる。

#９．シンボリックリンクの作成
python3.9で実行できるようにシンボリックリンクを作成

```
ln -s /usr/local/python390/bin/python3.9 /usr/bin/python3.9
```

pipも同様にpip3.9にシンボリックを作成しておく

```
# ln -s /usr/local/python390/bin/pip3.9 /usr/bin/pip3.9
```

#１０．確認

Python 3.9.0と表示されれば完了です。

```
# python3.9 --version
Python 3.9.0
```

pipについても確認します。

```
# pip3.9 --version
pip 20.2.3 from /usr/local/python390/lib/python3.9/site-packages/pip (python 3.9)
```