OmniAICreator/Qiita-1.07M huggingface dataset:
UITableView HeaderViewを作成 (.tableHeaderView) 復習

#今回の内容
<img width="150" alt="B56AB43E-3A39-4817-A514-815153569B96_1_201_a.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1466343/a691ac6c-b6c6-7f9a-cea4-5cd08a81fcfb.jpeg">

#コードと簡単解説

```Swift:
import UIKit

class ViewController: UIViewController {
 
    @IBOutlet weak var tableView: UITableView!

    let sectionTitleArray = ["Colors","sports","Fruits","GrandTours"]  //セクションのタイトルに表示
    let cellContentsArray = [
    
        ["red","blue","green","white","yellow"],
        ["Athletics","soccer","Cycling","basketball"],
        ["Orange","Pineapple","KiwiFruit"],
        ["Giro d'Italia","Tour de France","Buerta Espana"]
        
    ] //cellに表示する内容
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.dataSource = self
       
        //tableHeaderView HeaderViewを設定
        tableView.tableHeaderView = {() -> UIView in
            
            let headerView = UIView(frame: CGRect(x: view.frame.minX, y: view.frame.minY, width: view.frame.size.width, height: view.frame.size.height / 4))
            
            headerView.backgroundColor = .systemIndigo
            headerView.layer.cornerRadius = 20.0
            
            return headerView
        }()
        
    }

}

extension ViewController:UITableViewDataSource{
    
    //セクション数を設定
    func numberOfSections(in tableView: UITableView) -> Int {
        
        return 4
        
    }
    
    //セクションの高さを設定
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
        
        return 25
        
    }
    
    //セクションのタイトルを設定
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        
        return sectionTitleArray[section]
        
    }
        
    //表示するセルの個数を設定
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        
        return cellContentsArray[section].count
        
    }
    
    //セルに表示する内容を設定
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        
        cell.textLabel?.text = cellContentsArray[indexPath.section][indexPath.row]
        
        return cell
    }
       
}
```
#終わり
ご指摘、ご質問などありましたら、コメントまでお願い致します。
FlutterでAndroidEmulatorに won't run without Google Play services と表示されGoogleMapが表示できない

#■前提
Flutterの開発中、AndroidEmulator上に地図を出したいと思い、
GoogleMapを表示するサンプルを試しました。
https://codelabs.developers.google.com/codelabs/google-maps-in-flutter#0

Android用のキーを取得して、AndroidManifestに設定しと進めていったところ、
無事実機ではGoogleMapが表示されることを確認。

ところがEmulator上で表示しようとしたところ、
googlemap_sample won't run without Google Play services,
which are not supported by your device.
と表示され地図が出ません。

AndroidStudioのログには
W/GooglePlayServicesUtil( 3773): Google Play Store is missing.
W/Gralloc4( 3773): allocator 3.x is not supported
というような怪しいログも出ています。


##・環境
・OS
macOS Big Sur バージョン11.5.2
64bit

・実機
Pixel 55
Android 11

・エミュレータ
Pixel 5 API 30

・IDE
Android Studio Arctic Fox | 2020.3.1

#■解決方法

* 64bit用のSystemImage（Google APIs Intel x86 Atom_64 System Image）をインストールする

![スクリーンショット 2021-09-01 11.15.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18220/60c23cf1-1456-0f78-a03c-276faebd434b.png)


* AVDマネージャーからAVDを編集、SystemImageを64bit向けのものに変更

![スクリーンショット 2021-09-01 11.16.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18220/97f64ab3-37d2-70b5-5623-18d2166134b3.png)

![スクリーンショット 2021-09-01 11.16.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18220/16dff377-b2bc-d01c-1574-b5642e2a0963.png)


この状態で起動すればGoogleMapが起動します。

VirtualDeviceを追加するときに、SystemImageを選択する箇所があるのですが、
ここのRecommended（お勧め）がx86（32bit）なんですよね。
これをそのまま選択するとハマってしまう罠となっています。

大事なのは **AVDのSystemImageを64bit向けのものに変更** することです。
**インストールしても設定しないと意味がない** ので注意してください。
CloudWatch Synthetics について

# はじめに
CloudWatchにはSyntheticsという、アプリケーションの死活監視(外形監視)を行うサービスがあります。

Canaryを作成し、APIのエンドポイントやWebページのリンクを設定することでそのエンドポイントをスクリプトで定期的に叩きに行き、落ちていた場合はSNSで通知や、CloudWatchAlarmを鳴らしたりできます。

以下公式説明です。

> Amazon CloudWatch Synthetics を使用して、スケジュールに沿って実行される設定可能なスクリプトである Canary を作成し、エンドポイントと API をモニタリングできます。Canary は顧客と同じルートをたどり、同じアクションを実行します。これにより、アプリケーションに顧客トラフィックがない場合でも、顧客エクスペリエンスを継続的に検証できます。Canary を使用すると、顧客が問題を検出する前に問題を検出できます。

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries.html

# 料金
料金はCanaryの実行回数に依存します。
1ヶ月あたり100回の実行まで無料枠があり、100回を超過すると1回あたり0.0019USDかかります。(東京リージョン)
その他、Lambdaの実行料金・CloudWatch Logs・S3の料金などがかかるようです。

https://aws.amazon.com/jp/cloudwatch/pricing/

# 試してみる
## サンプルサイトの準備
ECRにある適当に作ったアプリのコンテナをAppRunnerでデプロイします。
(App Runner非常に楽でよいですよね..)

![image_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/f77fd6a6-8dba-e140-a57d-de40c2ee0f5a.png)

動作確認程度なので、トップページにリンクがある簡単なサンプルサイトを用意しました。
![image_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/21689306-150c-9b70-1b13-79f5d5b3e025.png)

## Syntheticsの設定

CloudWatch Syntheticsの設定を行います。

CloudWatchのメニューから「Synthetics Canaries - Canaryを作成」を選択します。
今回は、リンク切れチェッカー機能を試してみます。
![image_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/0a6f5a4d-b04a-2d3d-51e8-7b42f28c7668.png)


以下デフォルトから変更を加えた部分です。

- Canaryビルダー
    - 名前：sample-canary
    - アプリケーションまたはエンドポイントURL：AppRunnerから出力されたURL
    - フォローされているリンクの最大数：10

- スケジュール
    - 一回実行(消し忘れ課金予防の為)

- データストレージ,アクセス許可
    - 任意の適切な設定

設定画面もわかりやすくて良いです。

## 実行
作製したCanaryを選択し、「開始」を押下します。

終了後、「可用性」タブを見てみるとリンク先までチェックしていることがわかります。

![image_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/b1cbf317-4980-18d1-0c5f-cbac2aec70d1.png)

次に、サーバから一つのファイルを削除し、再度起動します。
404を検知して失敗していることがわかります。

![image_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/1dfb68e9-cc92-d651-a659-6e8da5f1413c.png)

# まとめ
CloudWatch Syntheticsのリンク切れチェッカー機能を試してみました。
CloudWatch Syntheticsには、他にもページのスクリーンショットを取って比較し、差異があればアラームを上げる機能など様々な監視が行える機能があります。料金も高すぎるということはなく、設定も簡単なので積極的に使いたいですね。
「.」だけでは改行は無視される

正規表現の有名な何でも屋「.」にも弱点はあります。
それは、仕様として「\n, \r, \u2028 or \u2029 には対応しない」となっていることです。
つまり、改行が来た瞬間「.」は自分の担当ではないと判断してしまうのです。

でも、優秀な仲間である s フラグを使えばこの弱点も補うことができます。

```
str.match(/^.*文字.*$/) -> 改行が入ると必ず null
str.match(/^.*文字.*$/s) -> 改行が入ってもヒット！
```

これで「.」も改行に負けない表現になりました。
めでたしめでたし。

... 実は「.」の代わりに「[^]」や「[\s\S]」でも良いのです


以下、Mozilla の docs より . の解説の引用
>Has one of the following meanings:
>
>- Matches any single character except line terminators: \n, \r, \u2028 or \u2029. For example, /.y/ matches "my" and "ay", but not "yes", in "yes make my day".
>- Inside a character class, the dot loses its special meaning and matches a literal dot.
>Note that the m multiline flag doesn't change the dot behavior. So to match a pattern across multiple lines, the character class [^] can be used — it will match any character including newlines.
>
>ES2018 added the s "dotAll" flag, which allows the dot to also match line terminators.

引用元：https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Character_Classes
【discord.py】VCに入ってるメンバーの一覧を取得する方法【python】

コード自体はかんたんだったんですけど、ちょっとだけハマったのでメモ。


```python
members = [i.name for i in message.author.voice.channel.members]
```

これで`members`変数にVCにINしているメンバーが入ってきます。

例えば組分けしたい場合は、

```python
members = [i.name for i in message.author.voice.channel.members]
random.shuffle(members)

party_num = 2
team = []
# チーム分け
for i in range(party_num):
    team.append("=====チーム"+str(i+1)+"=====")
    team.extend(members[i:len(members):party_num])

print ('\n'.join(team))
```

こうすることで、

```
=====チーム1=====
homo
チャット
reaper
ana
=====チーム2=====
traser
mac
sol
buri
```

と出力ができるはずです。

## ハマったこと（この記事で伝えたいのはむしろこっち）

botが起動しているときにVCにいるメンバーは取得できたんですけど、VCからメンバーが抜けても、なぜかbot上そのメンバーはまだVCにいるという形で取得してしまっていました。

どの記事でもそれについて書かれていなかったのですが、インテントという言葉を見かけたので「もしかして」ということで、

```python
#intents = discord.Intents(messages=True, guilds=True, members=True, reactions=True)  #before
intents=discord.Intents.all() # after
```

こうしたら行けました。
※具体的にどう設定すればいいかわからなかったので`all()`で。

参考：
https://qiita.com/disneyresidents/items/72741a88265107dd04d3
VSCode のターミナルを Git Bash にする


## はじめに

VSCode のターミナルで Git Bash を使おうとしたら、意外と苦戦したので忘れないようにメモしておく。

## 結論

スペースを入れるな。それと、WSL 使えばいいんじゃね。

## やったこと

まずは、[この記事](https://qiita.com/daikiozawa/items/48a9fe0e2898c7dd78ae) の通りにやろうとしたけど、プルダウン メニューに Git Bash がない。しょうがないので同じ記事の「以前」の通りに しようとしたら、
`terminal.integrated.shell.windows` これが非推奨とか言われた。

むむむと思って、もう少し調べたら、[この記事](https://qiita.com/take_me/items/47f192a126d75a9264a9)があったので、これを参考に、setting.json で

```json
"terminal.integrated.profiles.windows": {
        "PowerShell": {
            "source": "PowerShell",
            "icon": "terminal-powershell"
        },
        "Command Prompt": {
            "path": [
                "${env:windir}\\Sysnative\\cmd.exe",
                "${env:windir}\\System32\\cmd.exe"
            ],
            "args": [
                "/k",
                "chcp",
                "65001"
            ],
            "icon": "terminal-cmd"
        },
        "Git Bash": {
            "source": "Git Bash"
        }
    },
"terminal.integrated.defaultProfile.windows": "Git Bash",
```

とやってみた。ダメでした。

## できたよ

で、試行錯誤の結果、こうなりました。

```json
    "terminal.integrated.profiles.windows": {
        "PowerShell": {
            "source": "PowerShell",
            "icon": "terminal-powershell"
        },
        "GitBash": {
            "path": ["C:\\Git\\bin\\bash.exe"],
            "icon" : "terminal-bash"
        }
    },
    "terminal.integrated.defaultProfile.windows": "GitBash",
```

（コマンドプロンプトは消した。使わないし）

で、何が違うかというと、`Git Bash` じゃなくて、 `GitBash` です。Git と Bash の間にスペースが入るとダメなの。2ヶ所あるのに注意。

それと、

```
            "source": "Git Bash"
```

も使えませんでした。"path" を指定したらOK。なお、"path" と "source" は、同時に使えないらしい。

ここまで長かったっす。

## おわりに

VSCode 便利だねー。でも、スペース入れちゃいけないの、なかなか気づかないよねー。
python kerasダウンロードエラー　解決　（記録）

##pip install kerasで発生したエラー
問題：インストールできない

```
ERROR: Could not install packages due to an OSError: [Errno 2] No such file or directory: 'C:\\Users\\spide\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python39\\site-packages\\tensorflow\\include\\external\\com_github_grpc_grpc\\src\\core\\ext\\filters\\client_channel\\lb_policy\\grpclb\\client_load_reporting_filter.h'
HINT: This error might have occurred since this system does not have Windows Long Path support enabled. You can find information on how to enable this at https://pip.pypa.io/warnings/enable-long-paths
```

##Windowsでのファイルパス長の制限が原因でエラーが発生しました
PythonがAppDataユーザーのホームディレクトリの下のフォルダ構造などのネストされた場所にインストールされている場合、Windowsのデフォルトのパスサイズ制限に達すると、pipがパッケージのインストールに失敗することがあります。

##regeditツールを使用して、Windowsレジストリでその制限を解除することができます。

1.Windowsのスタートメニューに「regedit」と入力して起動しますregedit。

2.Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem キーに移動し ます。

3.LongPathsEnabledそのキーのプロパティの値を編集し、1に設定します。

4.kerasを再インストールします（以前の壊れたインストールは無視します）：

5.pip install keras

https://scikit-learn.org/stable/install.html#troubleshooting
2021/9月版 googlemapをptyhon+seleniumでスクレイピングして気になった点


この記事は、googlemapでスクレイピングをする際に気になった点のメモ書きです。

実験で「python＋selenium」を使ってgooglemapのスクレイピングをしてみました。

その際気になったのがgooglemapの仕様変更部分です。

特に、2021年の9月時点のgooglemap検索結果には、スクロールが導入されていました。

スクロールは、

お店リストの初期表示は５件で、最大２０件まで表示。

下にスクロールするとそれ以降のお店が表示されるというものです。

つまり、６件目以降のお店データを取得するには、pythonで何らかのプログラムを書いて下にスクロールしないといけません。

こちらの方も言及していました。

https://www.next-k.site/blog/archives/2021/07/24/376

PyAutoGuiを使うのも便利でいいですね。PyAutoGuiのほうが直感的にできるのでいいかもしれません。

というわけで、

私の場合は、無理やりsendkey("Keys.PAGE_DOWN")で下にスクロールしてみました。

こんな風に無理やりページダウンを数回程度やったところ２０件目まで表示・取得できました。


```py:pagedown.py
    selector_id = "//*[@id='searchbox-searchbutton']"
    search_button = driver.find_element_by_xpath(selector_id)
    search_button.click()
    selector_class = 'a4gq8e-aVTXAb-haAclf-jRmmHf-hSRGPd'
    WebDriverWait(driver, 120).until(
            EC.presence_of_element_located((By.CLASS_NAME, selector_class))
        )
    search_list= driver.find_elements_by_class_name(selector_class)[0]
    search_list.send_keys(Keys.PAGE_DOWN)
    time.sleep(0.3)
    search_list.send_keys(Keys.PAGE_DOWN)
    time.sleep(0.3)
```


ただし、この方法だと、お店の詳細をクリックして戻ってくるとまた初期表示の５件になってしまうので、そのたびにページダウンをして２０件目まで表示しないといけませんｗ

さらに、chromeドライバーのバージョン「92.0.4515.107」にしたところ謎の不具合が・・・

自分のプログラムが悪いと思うのですが、２ページ目以降で「次ページ」に遷移しようとすると「次ページ」のCSSの要素が消えてエラーとなり取得することができないというものです。※たぶん、ページダウンでスクロールしたことによる不具合だと思うのですが原因不明でした・・・。


で、その後・・・


よく考えたら詳細画面に行ったあと、横スクロールのお店一覧がでてるのでこの配列要素を取得して順番にクリックしていったほうが早いということに気が付きました。

![2021-09-01_17h23_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54074/ce00f489-44b5-c84e-73ec-6e37b440d75f.png)


```py:detail.py

    selector_detail = '//*[@id="bottom-pane"]/div/div[2]/div/div[1]/div/div/div/div[1]/div[2]/div'
    for i in range(お店の件数n):
        WebDriverWait(driver, 120).until(
            EC.presence_of_element_located((By.XPATH, selector_detail))
        )
        #1件目はすでに表示されているので2件目からクリック
        detail_button = driver.find_elements_by_xpath(selector_detail)[i+1]
        detail_button.click()
        #あとは、１件づつ切り替えてスクレイピング
        scraping()
```

これで、各ページの２０件分のデータを取得できるようになります。

取得フローは、下記のようになりました。

1. 検索
2. 一覧表示
3. 1件目クリック
4. 詳細画面へ移動
5. 横リストの2～n件目までを順番にクリックしてデータを取得
6. 前画面に戻る
7. 2ページ目へ遷移
8. 3へ戻って繰り返し 

こうすれば、６件目以降をスクロールしなくても各ページごとに２０件ずつお店データを取得できます。
[filter( ),find( ),findIndex( )]メソッド JavaScript

##filter( )
`filter( )`は配列内にある要素が指定された支持にあっていたらそのあっているものだけを配列から取り出して新しい配列の中にいれてくれます。

```javascript
const characters = ['ab', 'cde', 'fghi', 'jklmn'];
const result = characters.filter(character => character.length > 3); 
console.log(result); => ["fghi", "jklmn"]
```
上記例だと3文字以上の文字を残し、新しい配列を作ってくれます。

```javascript
const foods = [
  { name: "pork", type: "meet" },
  { name: "salmon", type: "fish" },
  { name: "beef", type: "meet" },
  { name: "tuna", type: "fish" },
];
const cookingType = materials => {
  if (materials.type === "meet") {
    return true;
  }
  return false;
};
const foodsType = foods.filter(cookingType);
console.log("result\n", foodsType);
```
.typeがmeetの要素を取り出し、新しい配列を作ってくれます。
##find( )
`find( )`は同じくその支持にあった最初の値を返してくれます。

```javascript
const array = [2, 10, 55, 180, 23];

const result = array.find(element => element > 8);

console.log(result); => 10
```
8より大きい数字の最初の10が返ってきました。
##findIndex( )
`findIndex( )`も同じくその支持にあった最初の要素の位置を返してくれて合わなければ`−１`を返します。

```javascript
const array = [2, 10, 55, 180, 23];

const number = (element) => element > 20;

console.log(array.findIndex(number)); => 2
```
配列は`0`から始まるので20より大きい55の位置の2
が返ってきました。

```javascript
const array = [2, 10, 55, 180, 23];

const number = (element) => element > 181;

console.log(array.findIndex(number)); => -1
```
仮に181を比較対象に入れると`-1`が返ってきました。
EC-CUBE4 の関連カテゴリをシンプルな表示にする

# EC-CUBE4 の関連カテゴリをシンプルな表示にする

EC-CUBE4は、標準で商品詳細ページに「関連カテゴリ」が表示されます。
ただし、この関連カテゴリ、親カテゴリの階層もすべて表示してしまいます。

>価格帯
>価格帯 ＞ 2001円～3000円
>お菓子・スイーツ
>お菓子・スイーツ ＞ 洋菓子
>お菓子・スイーツ ＞ 洋菓子 ＞ クッキー

これを

>価格帯 ＞ 2001円～3000円
>お菓子・スイーツ ＞ 洋菓子 ＞ クッキー

というすっきりした表示にしたいと思います。
当初はDB(dtb_product_category)のデータ側で対処しようかと思いましたが、表示側のみの変更で可能でした。

####\app\template\default\Product\detail.twig
```
                    {# 関連カテゴリ #}
                    {% if Product.ProductCategories is not empty %}
                        <div class="ec-productRole__category">
                            <div class="confilm-table-btn">{{ '▼ 関連カテゴリ'|trans }}</div>
                            <div class="confilm-tables" style="display:none;">
                            {% for ProductCategory in Product.ProductCategories %}
                                <ul>
                                    <li>
                                        {% for Category in ProductCategory.Category.path %}
                                            <a href="{{ url('product_list') }}?category_id={{ Category.id }}">{{ Category.name }}</a> {%- if loop.last == false %}
                                            <span>＞</span>{% endif -%}
                                        {% endfor %}
                                    </li>
                                </ul>
                            {% endfor %}
                            </div>
                        </div>
                    {% endif %}
```

これに、「子供がいるかどうか」の分岐を1つ入れてやればOKです。

```
                    {# 関連カテゴリ #}
                    {% if Product.ProductCategories is not empty %}
                        <div class="ec-productRole__category">
                            <div class="confilm-table-btn">{{ '▼ 関連カテゴリ'|trans }}</div>
                            <div class="confilm-tables" style="display:none;">
                            {% for ProductCategory in Product.ProductCategories %}
                                {% if ProductCategory.Category.countBranches == 1 %}
                                <ul>
                                    <li>
                                        {% for Category in ProductCategory.Category.path %}
                                            <a href="{{ url('product_list') }}?category_id={{ Category.id }}">{{ Category.name }}</a> {%- if loop.last == false %}
                                            <span>＞</span>{% endif -%}
                                        {% endfor %}
                                    </li>
                                </ul>
                                {% endif %}
                            {% endfor %}
                            </div>
                        </div>
                    {% endif %}

```
とします。

ほーらすっきり！
discordの公開botを申請した話

discordの公開botを申請した話です

# 公開botと認証

discordのbotを公開すると、管理人の承認なしでどのサーバにも導入することが出来ます
非公開の状態だと50サーバまでしか導入できず、サーバへの追加を管理人が行う必要があります

しかし、公開botにしても導入できるのは最大100サーバまでで、
それ以上になるとdiscordの運営に連絡して申請の必要があります

申請は75サーバ以上に導入されると申請可能になります

# 申請にかかる時間

公開botの申請自体は早かったと思います
ホストプロバイダを教えてほしいというメールが来ていたので、
使用しているVPSサーバとssh2でアクセスしている、と答えたら2日ほどで承認が降りました

しかし、100サーバ以上に導入しようとするとエラーが出ます
この原因はintentsにあります

# intentsとは

discordではbotの管理画面からONにしないと一部のイベントが送られてきません
![intent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985/380fdc8b-ca2b-a5a9-7125-5f5309922933.png)

上はGUILD_PRESENCESで、on_member_updateのようなメンバーの情報が変更されたイベントを取得するのに必要です
下はGUILD_MEMBERSで、Guild.get_memberのような、メンバーの一覧を取得するのに必要です

これらOnにしなくても、書き込まれたメッセージのユーザーデータは取得できるので、
メッセージに応答するだけのbotは問題なく作成できます

そして、公開botの承認が降りたあとは、これらのオプションを変更することが出来ません
厳密には、ON→OFFは出来ますが、OFF→ONができません
ONのままだと、100サーバ以上の導入は依然としてエラーが出ます

つまり、両方をOFFにしないと承認が降りた意味がありません

# さらなる申請
これらのONにしたbotを作る場合、更に申請が必要になります
これはサポートから専門の問い合わせフォームがあるので必要事項を書いて送りました

最初はテンプレメールが帰ってくるので、必要事項を書いて送信
そこから一ヶ月弱かかり、「本当に必要なのですか？」という英語のメールが着信
それに返信して一ヶ月弱かけて承認が降りました
最初に申請してから約2ヶ月ほどです

最初のテンプレメールは日本語だったので、依然として返事を日本語で書いていました
これを英語で書いていたらもっと早かったかもしれません
しかしながら、この期間の長さを利用して諦めさせるようにも見えます

# 今後の展開

discord は2022年4月からbotに更に厳しい制限を課すようです

https://support-dev.discord.com/hc/en-us/articles/4404772028055

具体的にスラッシュコマンドと呼ばれるメッセージ以外を
botが取得することが出来なくするようです
さらに、この制限を取り払う特権申請は認めない、とも言っています

今のbotはすべてのメッセージをbotが取得することが出来ます
すなわち、botを導入するとbotを通じてすべての書き込みを見ることが出来ます

また、メンバー一覧を直接取得できなくても、発言した人のデータを
全部保存していれば、一度でも発言をした人のリストは作ることが出来ます

そういう意味を込めて、必要以上にbotに情報を渡さない、という方針かと思われます

ただ、この制限は75台以上のサーバに導入されているbotのみらしいので
自分のサーバのみで使うようなbotには影響はないと思われるのですが、
後述する事情もあり、私は懐疑的に感じています

# discord.py への波紋

私は、discordのbotを作るに当たり、discord.pyを利用しています
Pythonで記述するdiscord用のライブラリで、多くの利用者がいます

しかし、そのdiscord.py の開発中止も発表されました
理由はdiscord社とのやり取りに疲れたというふうに読めます

https://gist.github.com/Rapptz/4a2f62751b9600a31a0d3c78100287f1

とりあえず、私のbotは通常のメッセージを拾う仕組みなので、
2022年4月になると連鎖的に開発停止することになりそうです
PADでexcelのマクロを動かしたいっ

お世話になっております。

PADでセルの色判別、色変更は難しそう（というか私が調べる能力足りない）なので
しょうがないのでPAD上でマクロ動作させたいので
そのためのメモを表記させて頂きますっ
今が特定の時間範囲内かどうやって判断する？（システム運休期間とか）（リファクタリング）

## やりたいこと
現在時刻が特定の時間範囲内かどうかをスマートに判断したいです。
例えば、特定の時間帯、ユーザーグループごとシステムへのアクセスや一部機能を制限するのに使えるかと思います。

今日も弊社先輩のリファクタリングに息を呑んでしまったのでメモメモ。

Modelは例えばこんな感じ↓
「利用一時停止」をsuspensionとしてその期間を持たせています。

```ruby
# == Schema Information
#
# Table name: user_groups
#
#  id                    :bigint           not null, primary key
#  name                  :text             not null
#  description           :text
#  created_at            :datetime         not null
#  updated_at            :datetime         not null
#  suspension_started_at :datetime
#  suspension_ended_at   :datetime
```

### 環境
ruby 3.0.0
Rails 6.0.3

## 実装！

### Ruby歴8ヶ月の人の実装例

```ruby
def suspension?
  return if suspension_started_at.nil? || suspension_ended_at.nil?

  current_time = Time.current
  current_time >= suspension_started_at && current_time < suspension_ended_at
end
```

:sweat_smile:


### リファクタリング例

```ruby
def suspension?
  return false if suspension_started_at.nil? || suspension_ended_at.nil?

  current_time = Time.current
  (suspension_started_at...suspension_ended_at).cover?(current_time)
end
```
最終行の可読性がグッと上がった印象です。
ポイントを以下に３つまとめておきます。

####?で終わるメソッド
先輩から「`?`で終わるメソッドは毎回Booleanを返すべき」とレビューが返ってきました。それを読んだ僕は、「何をおっしゃいます。このメソッドの最終行はちゃんとtrue/falseを返しますよ？ふふ」と思ったら...あ、一行目が...！参りました！:bow:というわけで、`return false`にしました。

####範囲演算子`...`
`...`は**範囲演算子**で、x以上y未満という範囲オブジェクトクラスのインスタンスを生成することができます。ちなみに今回はドット２つだけど`..`も見かけるような？:confused:と思ったら、そちらは未満ではなく以下の意味になるそうです。下記、Rubyのリファレンスマニュアルから引用です。

```ruby:Rubyリファレンスマニュアル
Range.new(1, 5) # 1 以上 5 以下
1..5            # 同上
1...5           # 1 以上 5 未満
```

https://docs.ruby-lang.org/ja/latest/class/Range.html

####`cover?`メソッド
`cover?`は、`include?`と似ていますが、違いがあるようで、上のコードをRspecテストの中で単純に置き換えてみるとテストはパスしますが、下記のような注意を受けました。

> DEPRECATION WARNING: Using `Range#include?` to check the inclusion of a value in a date time range is deprecated. It is recommended to use `Range#cover?` instead of `Range#include?` to check the inclusion of a value in a date time range. (called from suspension? at /Users/kakudaisuke/.../app/models/user_group.rb:83)

datetimeの範囲に値が含まれているかをチェックするには`include?`は非推奨だよってことですね。ちなみに`..`にするとこのwarningは出現しませんでした。

`cover?`と`include?`両者の違いは、ドキュメントに書いてありました↓

> Range#include? と異なり <=> メソッドによる演算により範囲内かどうかを判定します。 Range#include? は原則として離散値を扱い、 Range#cover? は連続値を扱います。（数値については、例外として Range#include? も連続的に扱います。）
（Rubyリファレンスマニュアルより　https://docs.ruby-lang.org/ja/latest/method/Range/i/cover=3f.html）


## 最後に
何かご指摘あれば、ぜひよろしくお願いします。
[Ruby on Rails] railsコマンドで`Could not find 'bundler'`エラーが出て苦戦したメモ

Ruby on Railsの勉強がてら手元のMacにrbenv使いながら環境インストールしたら表題のエラーに悩まされたのでメモ

# 参考にさせて頂いた環境導入手順

* [【完全版】MacでRails環境構築する手順の全て](https://qiita.com/kodai_0122/items/56168eaec28eb7b1b93b)
* [Ruby初学者のRuby On Rails 環境構築【Mac】](https://qiita.com/TAByasu/items/47c6cfbeeafad39eda07)

rbenvなどを使って個別環境を作ってみる。

# 発生したエラー

gemインストールしてrailsアプリ作って、さてrailsのインストール確認するかとなった時に以下のエラーが発生。

```
$ rails -v
Traceback (most recent call last):
	15: from bin/rails:3:in `<main>'
　　　￤
   (中略)
　　　￤
	 1: from /(rbenv入れたパス)/.rbenv/versions/2.7.4/lib/ruby/2.7.0/rubygems.rb:254:in `bin_path'
/(rbenv入れたパス)/.rbenv/versions/2.7.4/lib/ruby/2.7.0/rubygems.rb:277:in `find_spec_for_exe': Could not find 'bundler' (2.2.26) required by your /(実行パス)/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
To install the missing version, run `gem install bundler:2.2.26`
```

* `bundle update --bundler` しても、 `gem install bundler:2.2.26` しても状況変わらず
* [こちらのページ](https://qiita.com/kodai_0122/items/c4c13e89dd5c4cba1f32#bundler--v%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B%E3%81%A8could-not-find-bundler-214%E3%81%A8%E3%81%84%E3%81%86%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E7%99%BA%E7%94%9F%E3%81%99%E3%82%8B) の手順を参考にさせてもらったものの状況変わらず。

# 解決手段

`rails` ではなく `bundle exec rails` を使う

```
$ bundle exec rails -v
Rails 5.2.6
```

ちゃんと動いた。

# 補足
Bundlerの動作をちゃんとわかってなかったのが原因。
もっというと、どうやら実行パスに `bundler/setup` が存在してなかったことが直接の原因だったっぽい。
インストールの際にどっかでコケてたのかな…？

(勉強させていただいたページ)
[bundle execって必要なの?](https://qiita.com/d0ne1s/items/fa2dafcee02e963fe997)
[bundler、bundle execについて　※自分用メモ](https://qiita.com/dawn_628/items/1821d4eef22b9f45eea8)
[bundle exec と bin/rails の違い](https://teratail.com/questions/120475)



以上。
JSONとは？

##JSON (JavaScript Object Notation)

* JavaScript Object Notationという意味の縮約語でデータを保存または転送する際によく使われる軽量のDATA交換形式
* Javascriptで客体を作るときに使う表現式を意味する。
* JSON の表現式は人と機械ともに分かりやすく容量が小さいことから、最近ではJSON がXML の代替としてデータ転送などに多く使われている。
* JSON はデータフォーマットに過ぎず、いかなる通信方法でも、プログラミング文法でもなく、単にデータを表示する表現方法に過ぎない。

###JSONの特徴

* サーバとクライアント間の交流で一般的に多く使われる。
* ジャバスクリプト客体の表記法と酷似している。
* ジャバスクリプトを利用してJSON 形式の文書を容易にジャバスクリプトオブジェクトに変換できるメリットがある。
* JSON 文書形式はジャバスクリプトオブジェクトの形式をベースに作られた。
* ジャバスクリプトの文法と非常に似ているが、テキスト形式に過ぎない。
* 他のプログラミング言語を利用しても簡単に作ることができる。
* 特定の言語に従属せず、ほとんどのプログラミング言語でJSONフォーマットのデータをハンドリングできるライブラリを提供する。

###XML vs JSON

1. XML
> データ値の両側にタグがある。
HTML を元にしたのでタグというのがないわけがないが、そのタグを縮めるとしても最低限表現するには両側に何文字ずつあるべきだ。

2. JSON
> タグで表現するよりは中括弧（{}）のような形式にし、値を','と並べるのにその表現が簡単だ。
[SwiftUI]Colorの整理とダークモード対応（超簡単）

## 投稿の経緯
SwiftUI × 個人開発でアプリ内で使う配色を管理する場合に使った方法を投稿します。

## 環境
Swift version 5.4.2
Xcode version 12.5.1

## Colors.xcassetsの作成
`File > New > File...`で`Asset Catalog`を選択して今回は`Colors`という名前でファイルを新規作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643372/eafc58ff-dea3-3145-5b5d-94651a9fb60f.png)

ファイルを作成したら下記画像のように`New Color Set`で色を追加します。

![スクリーンショット 2021-09-01 16.47.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643372/23989720-4b4d-8989-b875-f35df0f4206f.png)

左側がライトモード対応で右側がダークモード対応です。
ここで設定した色が端末のライト or ダークで自動で切り替えられます。超便利ですね。

次は`Colorsファイル`で作成した色をコードで使えるように設定します。

## ColorManagerの作成
`Colors`で作成した色をコードで扱うために、ファイルを新規作成し、構造体`ColorManager`を定義します。
`ColorManager`では以下のように定義します。

```swift
struct ColorManager {
    static let 定数名 = Color("Colorsで設定したColor名")
}
```

私の場合は以下のように設定しています。参考に。。

```swift
struct ColorManager {
    static let baseColor = Color("base_color")
    static let mainColor = Color("main_color")
}
```

後は、コードで呼び出すだけです。

## 反映方法
反映方法は以下の通り。

```swift
ColorManager.baseColor
```

```swift
.foregroundColor(ColorManager.baseColor)
```

```swift
.background(ColorManager.baseColor)
```

UIColorに設定する場合は以下の通り。

```swift
UIColor(ColorManager.baseColor)
```

ポイントはColorとして扱われる点です。参考にしてください。

## お知らせ
現在、iOS開発案件を業務委託で募集中です（副業）。[TwitterDM](https://twitter.com/ken_sasaki2)でご依頼をお待ちしています
PythonマルチスレッドThreadPoolExecutor

## マルチスレッド

`foo`の関数を20回する処理をマルチスレッドで行います。
`foo`の処理は待機時間３秒が含まれているので処理時間は最低でも20回×3秒で60秒以上はかかる。
しかし、マルチスレッドで処理すれば、スレッド数に応じて同時に処理してくれるので60秒もかからず処理が終わる。

```python
from concurrent.futures import ThreadPoolExecutor
from time import sleep

def main():
    with ThreadPoolExecutor() as executor:
        for i in range(20):
            executor.submit(foo, i)

def foo(i: int):
    sleep(3)
    print(f"{i + 1}回目")

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

スレッド数を指定したいとき(PCが重かったりするとき)は引数に数値を入れる。
(max_workers=はなくてもよい。あってもよい。)

```python
with ThreadPoolExecutor(max_workers=3) as executor:
```

引数がない場合はそのPCのCPUのスレッド数に応じて同時に処理されるスレッド数が決まる。
それほど古いパソコンでなければスレッド数10以上にはなるでしょう。
なので上記の処理では6秒くらいで処理が終わります。
Amazon Linux 2 に nmon をインストール

Amazon Linux 2 に「nmon」をインストールする記事がなかったので、記載しておきます。

## インスタンスの前提
・OS（AMI）：Amazon Linux 2 AMI (HVM), SSD Volume Type　64 ビット (x86)
・インスタンス：t2.micro
　※arm版以外のインスタンスであれば問題ないと思います。

## nmonのダウンロード
以下のURLからダウンロードしてください。この記事では、投稿時点で最新の「nmon16m_helpsystems.tar.gz」をダウンロードしています。
http://nmon.sourceforge.net/pmwiki.php?n=Site.Download

##nmonのインストール
・まずはTeraterm等SSHでインスタンスに接続してください。

・インスタンスに接続後、rootでログインしてください。

```
sudo su -
```

・ダウンロードしたnmonをWinSCP等で「/tmp」直下にアップロードしてください。

・「/usr/local/src」直下にファイルをコピーします。

```
cp nmon16m_helpsystems.tar.gz /usr/local/src/nmon16m_helpsystems.tar.gz
```

・ファイルを解凍し、「nmon_x86_64_rhel8」のパーミッションを「755」にします。

```
cd /usr/local/src
tar zxf nmon16m_helpsystems.tar.gz
chmod 755 nmon_x86_64_rhel8
```

・「/usr/local/bin」直下に、「nmon_x86_64_rhel8」をコピーします。

```
cp nmon_x86_64_rhel8 /usr/local/bin/nmon
```

これでインストールは完了です。

## nmonの実行

「nmon」というコマンドを入力すると、nmonの画面が起動します。
アルゴリズム ビット演算で2進数足し算

##ビット演算

2進数の足し算をビット演算について調べてみようと思います。

```java
public static void main(String[] args) { 
	System.out.println(addBinaryPlus("1101","1011"));
	System.out.println(addBinaryNonPlus("1101","1011")); 

	public static String addBinaryPlus(String a, String b) { 
		int aInt = Integer.parseInt(a, 2); 
		int bInt = Integer.parseInt(b, 2); 
		int sum = aInt + bInt; 
		return Integer.toBinaryString(sum); 
 	}//一般的な足し算演算
	
	public static String addBinaryNonPlus(String a, String b) { 
		int aInt = Integer.parseInt(a, 2); 
		int bInt = Integer.parseInt(b, 2); 
		return Integer.toBinaryString(addBin(aInt, bInt)); 
	}// ビット演算で足し算（最初の媒介変数がStringであると仮定したとき）

	public static int addBin(int a, int b) { 
		if(b == 0) return a;
 		int sum = a^b; 
		int carry = (a&b) << 1; 
		return addBin(sum, carry); 
	}//再帰用ビット演算関数
}

```

####1101と1011という2つの進数のバイナリ
1101と1011という二つの2進数のバイナリがあると仮定したとき

普通の方法はこの2数を"+"を利用してaddBinaryPlus関数のように1次的な方法でコーディングすることが一番先に思い浮かぶ方法です。

2進数ということを知らせてくれて(parseInt)単純に + 演算で加えた後に2進数StringでtoBinaryStringしてみると足し算演算になりますが...



ここからもう一歩進み、

ビット演算の原理を利用すれば珍奇な経験をするようになります。

問題の出題意図が二進数足し算アルゴリズムの解法を"ビット演算"で解決したかを判断するのであれば、

再帰関数であるaddBinのようにコーディングできるようです。



足し算のためのビット演算解法の核心は、XOR、AND演算の2つです。
Universal-Sentence-Encoder-QAの確認

[universal-sentence-encoder-multilingual-qa](https://tfhub.dev/google/universal-sentence-encoder-multilingual-qa/3)を試した記事です。

## universal-sentence-encoder-multilingual-qaとは？
universal-sentence-encoder-multilingualでは、日英翻訳であったり、類似語検索であったりに使えるモデルでしたが、こちらはQAに使えるモデルです。
マルチリンガルということで日本語の質問に英語で回答するといったことが可能です。

## サンプル
[モデルの紹介ページにもサンプル](https://tfhub.dev/google/universal-sentence-encoder-multilingual-qa/3)はありますが、[こちらのページ](https://www.tensorflow.org/hub/tutorials/retrieval_with_tf_hub_universal_encoder_qa)の方がより分かりやすいサンプルかと思います。

## 実行
SQuADを使ってSimple Neighborsの処理を流しますが、ロースペックPCであっても然程時間はかからず処理終了します。(3年前のミドルスペックノートで15分ほど)

## 質問
サンプルのコードでは質問をランダムチョイスしているのでそこだけ変えてみましょう。  

```
num_results = 5 # 欲しい回答数を設定
query =["今日の天気は晴れですか？"] # 質問文を設定
display_nearest_neighbors(query[0])
```

## 回答
![result_use_qa.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1509592/80f94efc-1f28-77da-7698-7bd6e2d46df4.jpeg)

日本語の質問に英語で回答されます。
英語回答をDeepLで翻訳すると以下のようになります。     
1.南カリフォルニアは地中海性気候で、雨が少なく、晴れの日が多いのが特徴です。
2. 典型的な夏の午後には、雷雨が発生することもよくあります。
3. 深刻な雨が降ることも珍しくありません。
4. 北ペニン山脈の雨陰にあるため、英国で最も乾燥した都市のひとつです。
5. 夏は暑くて雨が多く、夏の雷雨で豪雨になることもよくありますが、短時間で終わります。

## 結果
そう外れていない回答が返ってきているように見えます。
何より日本語の質問にこのレベルで返せているのが凄いですね。
自社QAのデータをファインチューニングすると中々いいものが出来上がりそうです。
アルゴリズム解釈[カデイン]

##カデイン
[1,-3,-1,2]と同じ数の羅列"数列"があると仮定した時、

各数を加えた時に最も大きな数が出る連続した部分を探すアルゴリズムをカデインアルゴリズムという。

数列アルゴリズムの基礎に該当する問題で

####解釈の核心は、

1. 要素を一つずつ足す

2. 「加えた値を変数に保存」

3. 加えた値がその最後に保存しておいた変数値より大きければ変数を代入


JavaCodeで見ると、

```java
int[] nums = {1,-3, -1, 2}; 
public static int maxSubArray(int[] nums) { 
     //配列の長さが1の場合は加えることがないので0番地のまま返却 
     if(nums.length == 1) { return nums[0]; } 
     //該当osのint範囲での最小値を初期値に設定、各配列要素の合算値
     int maxValInt = Integer.MIN_VALUE, sumVal = 0; 
     //1.配列要素を一つずつ回転しながら価格を足してみる(最大部分和を求めるために) 
     for(int i = 0, j = nums.length; i < j; i++) { 
         //2.合算値代入 
         sumVal += nums[i]; 
         //3.以前の合算保存値と比較して、より大きな値であれば置
         if (maxValInt < sumVal) maxValInt = sumVal; 
         if (sumVal < 0) sumVal = 0; 
      } 
      return maxValInt; 
}
```
Azure Web Appsを複製する方法


- App Service Planを新しく作成したapp-service-plan-hogetest-v3に変更してWebAppsを複製させたい
- App Service PlanはAzure Web Appsとは違うRGに属している。
- 同じRGにapp-hogetest-frontを複製して新たにapp-hogetest-ocr-front-V3（AppServicePlan：app-service-plan-hogetest-v3）を作成する。 

## 既存のアプリの複製

### Azure Powershellからやってみる

```Powershell
$srcapp = Get-AzWebApp -ResourceGroupName [リソースグループ名] -Name app-hogetest-front
```

New-AzWebApp

```Powershell
$destapp = New-AzWebApp -ResourceGroupName [リソースグループ名] -Name app-hogetest-front-V3 -Location "Japan East" -AppServicePlan app-service-plan-hogetest-v3 -SourceWebApp $srcapp
```

複製元のデプロイスロットをすべて含ませて複製する場合には、-IncludeSourceWebAppSlotsを使う。

```Powershell
$destapp = New-AzWebApp -ResourceGroupName [リソースグループ名] -Name app-hogetest-front-V3 -Location "Japan East" -AppServicePlan /subscriptions/[サブスクリプションID]/resourcegroups/[新しいAppServicePlanがあるRG名]/providers/Microsoft.Web/serverfarms/app-service-plan-hogetest-v3 -SourceWebApp $srcapp -IncludeSourceWebAppSlots
```

### WebAppsにある[アプリの複製]

WebAppsにある [アプリの複製] からでも複製できる。


### ARMテンプレートを使う

- 複製元のARMテンプレートをエクスポートして、カスタムデプロイを行えば複製できた。

## まとめ

- どの方法も複製することはできた。
- 複製先はオリジナルと違う名前にする必要があるので、URLが変わってしまう欠点がある。
  - 複製元と同じURLで作成したい場合は、複製元を先に消してから行う必要があるのでARMテンプレートで行うしか方法はなさそう？
- URLを変更せず、安全に複製する方法でもっといい方法があればコメントください。
M5Stack Core2: 温度湿度センサーを使う

M5Stack Core2 で Grove の温度湿度センサーを使う方法です。
Port A に Grove を挿します。
![IMG_20210901_162335.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/a4bd9816-5297-1f7c-8fae-b3a74c71043f.jpeg)


```text:temperature_humidity/temperature_humidity.ino
// ---------------------------------------------------------------
/*
	temperature_humidity.ino

						Sep/01/2021
*/
// ---------------------------------------------------------------
#include <M5Core2.h>
#include "DHT.h"

#define DHTTYPE DHT22
#define DHTPIN 33 
DHT dht(DHTPIN, DHTTYPE);

int count = 0;
// ---------------------------------------------------------------
void setup()
{
	M5.begin(true, true, true, false);
	M5.Lcd.setTextSize(4);
	M5.Lcd.println("Temperature");
	M5.Lcd.println("Humidity");

	while (!Serial);
	M5.lcd.setBrightness(80);

	Serial.println("Sep/01/2021 AM 10:00 ***");
}

// ---------------------------------------------------------------
void display_proc(float temp_hum[])
{
	Serial.print("temperature = ");
	Serial.print(temp_hum[1]);
	Serial.print(" C  ");
	Serial.print("humidity = ");
	Serial.print(temp_hum[0]);
	Serial.println(" %");

	M5.Lcd.setTextSize(4);
	M5.Lcd.setCursor(50,100);
	M5.Lcd.print(temp_hum[1]);
	M5.Lcd.println(" C");
  
	M5.Lcd.setCursor(50,160);
	M5.Lcd.print(temp_hum[0]);
	M5.Lcd.println(" %");
}

// ---------------------------------------------------------------
void loop()
{
	float temp_hum[2] = {0,0};

	Serial.print("count = " + String(count) + "  ");

	if (!dht.readTempAndHumidity(temp_hum))
		{
		display_proc(temp_hum);
		}
	else
		{
		Serial.println("ERROR! *** readTempAndHumidity ***");
		}

	delay(5000);

	count++;
}

// ---------------------------------------------------------------
```
Nginxが再起動できなかった場合の対処法

##Nginxのリスタートでエラー

Nginxを再起動した際にNginxがエラーを出力させた、なぜだ？？

```shell
$ sudo systemctl restart nginx
# エラーの発生
# job for nginx.service failed because the control process exited with error code. See "systemctl status nginx.service" and "journalctl -xe" for details.
```

##とりあえずエラーメッセージを実行する
```shell
$ systemctl status nginx.service
$ journalctl -xe
```
うーん、、、
**なんとなくプロセスが残ってるのかなと思った**

##解決方法
とりあえずNginxのプロセスを確認してみよう。

```shell
$ sudo lsof -i | grep nginx
nginx     15930     ~      
nginx     15930     ~       
nginx     15930     ~        
nginx     15941     ~    
nginx     15941     ~    
nginx     15941     ~    
nginx     15942     ~    
nginx     15942     ~   
nginx     15942     ~ 
```

残ってた笑
それじゃ**killコマンド**で強制的に終了させよう(-9で強制)

```shell 
$ sudo kill -9 15930
$ sudo kill -9 15941
$ sudo kill -9 15942

```

Nginxの
プロセスを再度確認

```shell
$ sudo lsof -i | grep nginx
```


###消えた、これで実行だ！！！！！！！！
```shell
$ sudo systemctl restart nginx
```

***動いた！！！！！！***


##結論
焦らずプロセスの確認をしましょう笑
Browserify を試す(node.jsのrequireメソッドをブラウザで使う)


# 経緯

- node-webrtc のサンプルコード解析して、サーバーサイドのnode.jsのJavaScriptなのか、ブラウザ用のJavaScriptなのか、混乱した
  - node-webrtcについては別途めとめる予定
-  node-webrtcのサンプルでは、Browsersを用いて「node.js用のJavaScript」を「ブラウザ用のJavaScript」に変換していることがわかった
- node-webrtcコードを解析できるように、Browsers の最低限の知識を身に着けるためにまとめる

https://github.com/node-webrtc/node-webrtc



# 環境

- Windows 10 
- node v16.8.0
- npm 7.21.1

## npm

- node-webrtcで使っているnode-fetchのバージョンに合わせている
- 詳細は下部にあるソースコードを参照ください

```
+-- browserify-middleware@8.1.1
+-- browserify@17.0.0
+-- express@4.17.1
+-- node-fetch@2.6.1
`-- serve@12.0.0
```

# Browserify とは

- requireメソッドをブラウザで使えるようにする魔法のようなテクノロジー

> Browsers don't have the require method defined, but Node.js does. With Browserify you can write code that uses require in the same way that you would use it in Node.


![ss.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083912/b8e62da3-22b5-aec8-fa62-0e7caf2dd790.png)

# node-fetch を node.jsで動作させる

## 実装コード

- 以下のHTTPリクエストを行うコードを `node.js` で実行
- 別途、`http://localhost:5000/file.txt` を用意しておく
- HTTPリクエストが成功して、ファイルが取得できることを確認

```javascript:example.js
const fetch = require('node-fetch');

async function Fetch() {
    const response = await fetch('http://localhost:5000/file.txt', {method: 'GET'});

    const body = await response.text();
    console.log(body);
}

Fetch();
```

## 実行結果

- HTTPのリクエストに成功

```
c:\BrowserifyExample>node example.js
The test succeeded.
```

# require('node-fetch') をブラウザで動作させる

- パッケージ側にインストール Browserify を実行して、ブラウザで動作するJavaScriptに変換する

```
node ./node_modules/browserify/bin/cmd.js example.js -o public/bundle.js
```

- 出力したファイルをHTMLで参照する

```html
<!DOCTYPE html>
<html>
<body>
<h1>Open the Console on your browser.</h1>

<script src="bundle.js"></script>

</body>
</html>
```

- node.sjのHTTPサーバーを起動してブラウザでFetchできていることを確認

```
node ./node_modules/serve/bin/serve.js ./public
```

![ss2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083912/4d347657-60d3-5c30-2e64-211e0e52aefd.png)


# expressフレームワークと連携

- `browserify-middleware` を使うと、サーバーサイドで、「require()のあるnode.js用のJavaScript」を、「ブラウザで処理できるJavaScript」に変換して、返却することができる
- 以下のようなコードで、express フレームワークと連携できる

```javascript
const browserify = require('browserify-middleware');
const express = require('express');
const app = express();

app.use(`/`, browserify("./example.js"));

const server = app.listen(3000, () => {
    const address = server.address();
    console.log(`http://localhost:${address.port}\n`);
 });
 ```

## 変換されたJavaScriptのコード

![ss03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083912/1476e020-bb4f-4b39-902b-14769c1ea9a3.png)


# ソースコード

https://github.com/hachicomb/BrowserifyExample
暗号化スイート(AES-GCM)でTLSの安全性を強化する方法


TLS1.2で暗号化スイートを利用して安全性を強化する方法について、紹介させていただきます。

## 1.暗号化スイートとは
SSL/TLS にてデータを暗号化して通信を行うには、自分自身と通信相手の両方が利用可能なアルゴリズムを使う必要があります。
不特定の相手と両者が利用可能なアルゴリズムを確認しあうために使われるのが 暗号スイート(cipher suite) となります。

## 2.Tomcatで暗号化スイートの設定について
### 2.1との暗号化スイートを使います？
JAVAで指定できる暗号化スイートは以下のサイトを参照してください
https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1234182/3c6d16e1-d03a-7099-0703-727d507a3a07.png)

例えば、AES-GCMを利用する場合、
上記サイトから関連する暗号スイートをリストアップしました

|AES-GCM|
|:-:|
|TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 |
|TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 |
|TLS_DH_RSA_WITH_AES_128_GCM_SHA256  |
|TLS_DH_RSA_WITH_AES_256_GCM_SHA384  |
|TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 |
|TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 |
|TLS_DH_DSS_WITH_AES_128_GCM_SHA256  |
|TLS_DH_DSS_WITH_AES_256_GCM_SHA384  |
|TLS_DH_anon_WITH_AES_128_GCM_SHA256 |
|TLS_DH_anon_WITH_AES_256_GCM_SHA384 |
|TLS_PSK_WITH_AES_128_GCM_SHA256     |
|TLS_PSK_WITH_AES_256_GCM_SHA384     |
|TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 |
|TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 |
|TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 |
|TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 |
|TLS_AES_128_GCM_SHA256              |
|TLS_AES_256_GCM_SHA384              |
|TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA|
|TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA|
|TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA2|
|TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA3|
|TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA25|
|TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA38|
|TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256|
|TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384|


### 2.2暗号化スイート(AES-GCM)をTomcatのコンフィグに指定


```xml:server.xml 
    <Connector port="8888" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
               clientAuth="false" sslProtocol="TLS" sslEnabledProtocols="TLSv1.3,TLSv1.2"
               ciphers="TLS_AES_128_GCM_SHA256,TLS_AES_256_GCM_SHA384,
                       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                       TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
                       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                       TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256"
    />

```
### 2.3検証
ウェブサイトをアクセスして、TLS通信に置いて、証明書が適用されている事を確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1234182/2a0a493d-8579-22f6-4385-076fa1cfaea3.png)

wiresharkで暗号スイートが適用されている事を確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1234182/f17e0266-b053-527d-6516-ff0fa7e70e2f.png)
数式のサンプル集

# ケース

## ラウンドロビンケースの割り当て
単純なラウンドロビンアルゴリズムを使用して、ケースをユーザーのプールに均等に分散します。このシナリオでは、MOD関数を[ケース番号]フィールドに適用して、1とラウンドロビンプール内のユーザー数（この例では3）の間を繰り返し循環する番号を生成します。この数式項目を作成した後、それに基づいてケース割り当てルールを作成する必要があります。数式項目の値（この例では1、2、または3）ごとに、ユーザーまたはキューの割り当てを指定します。

**ラウンドロビン**とは、総当たり戦、傘連判状、持ち回り、などの意味を持つ英単語。 ITの分野では、複数の主体や対象を順繰りに（同じ順序で循環的に）指名したり割り当てたりすることを指す。

```
MOD( VALUE(CaseNumber), 3) + 1
```

## ケースエージング（オープンケースの場合）
ケースが開いている日数を計算します。ケースが閉じている場合は、結果を空白に設定します。この数式を並べ替え列として関連リストに追加すると、現在開いているケースのうち、最も長く開いているケースをすばやく確認できます。

```
IF( IsClosed,null, NOW() - CreatedDate )
```

## ケースの分類
ケースの経過時間（カスタムフィールド）に応じて、RED、YELLOW、またはGREENのテキスト値を表示します。この式は、要約レポートでのグループ化に使用できます。

```
IF( Case_Age__c > 20, "RED", IF(Case_Age__c > 10, "YELLOW", "GREEN"))
```

## ケース期日の計算
ケースの優先度に基づいて期日を設定します。それであればが高く、期日は開店後2日です。もしそれが中、期日は開店後5日です。それ以外の場合は、日付は7日です。

```
IF( ISPICKVAL(Priority, "High"), CreatedDate + 2, IF( ISPICKVAL(Priority, "Medium"), CreatedDate + 5, CreatedDate + 7))
```

こっちの書き方の方が修正が楽そうです。式が小さくなると思います。

```
CASE(Priority,"High",CreatedDate + 2 ,
"Medium", CreatedDate + 5 ,
CreatedDate + 7)
```
【Python】S3上の複数JSONファイルを結合する

# awswranglerを使用してS3上の複数JSONファイルを結合し、S3に出力する
## 概要
[AWS Data Wrangler](https://github.com/awslabs/aws-data-wrangler)を使用する。
読み込みには以下のJSONLファイルを圧縮した[sample1.json.gz][sample2.json.gz]を使用する。

```json:sample1.jsonl
{"id":1,"father":"Mark","mother":"Charlotte","children":["Tom"]}
{"id":2,"father":"John","mother":"Ann","children":["Jessika","Antony","Jack"]}
```

```json:sample2.jsonl
{"id":3,"father":"Bob","mother":"Monika","children":["Jerry","Karol"]}
```

## 事前準備
事前にawswranglerをインストールする

```
$ pip install awswrangler
```
## コード

```python
import awswrangler as wr
import pandas as pd
from datetime import datetime,timezone

# 入力
file_list = ["s3://testbucket/prefix/sample1.json.gz",
             "s3://testbucket/prefix/sample2.json.gz"]
dfs = wr.s3.read_json(path=file_list, lines=True)

# 出力
today = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
output_path = 's3://testbucket/output/{}'.format(today)
wr.s3.to_json(
    df=dfs,
    path=output_path,
    orient="records",
    lines=True
)
```
TabViewで選択時のタブを画像毎置き換える方法

## やったことなど
今回はタブボタンを選択した時の変化を「アイコン」に対してやってみようと思いました。
参考ページでは「SF Symbol」のフォントを変化させる方法が掲載されています。
とある案件で選択時のタブボタンを画像毎変える必要があったため、なんとか手を加えて試してみました。
構成やソースはほぼ参考サイトを引用しています。

## コード
引用したソースから「systemName:」を削除して少しImageの名前を変えた程度でなんとかうまくいきました。

```swift
struct ContentView: View {
    
    // タブの種類
    enum Tab {
        case circle
        case square
    }
    
    // 選択中のタブ
    @State private var selection: Tab = .circle
    
    var body: some View {
        TabView (selection: $selection) {
            Text("円の画面")
                .tabItem {
                    Image(selection == .circle ?
                            "circle-shadow" : // 選択時
                            "circle") // 非選択時
                        .imageScale(.large)
                    Text("円")
                }
                .tag(Tab.circle) // タブの種類を指定
            Text("四角の画面")
                .tabItem {
                    Image(selection == .square ?
                            "square-shadow" : // 選択時
                            "square") // 非選択時
                        .imageScale(.large)
                    Text("四角")
                }
                .tag(Tab.square) // タブの種類を指定
        }
    }
}
```

## 結果

### 円のタブ選択時のタブ画像の変化
![Simulator Screen Shot - iPod touch (7th generation) - 2021-09-01 at 15.09.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1229502/0dc00d49-a622-cbd1-abd1-46bb582caa2b.png)

### 四角のタブ選択時のタブ画像の変化
![Simulator Screen Shot - iPod touch (7th generation) - 2021-09-01 at 15.09.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1229502/a5ed2d60-663b-05e6-a8f3-cca2954d5d34.png)


## 思ったこと
「.tag(Tab.circle)」の記載が重要でした。
このtagがないと思い通りにタブボタンが変化してくれませんでした。
あとは、selectionの記法で選択中のタブ情報を保持できるのはお手軽で楽だなと思いました。
参考サイトに感謝を申し上げます。

## 参考
[[SwiftUI]TabViewで選択時のアイコンを変更する](https://note.com/oocoocococo/n/n4e32d1dfe5ab)
ScalaでOptionを使うことを推奨しないケース

# 前置き
Scalaには `Option` 型があります。これは値が存在するか否かをそのサブタイプである `Some` と `None` をつかい明示的に扱うことができます。
また、`Option`型には`Some`であれば処理をし、`None`ならなにもしないというようなメソッドが含まれるので、いちいちif文等を書く必要がありません。

```scala
def double(x: Option[Int]): Option[Int] = x.map(_ * 2)

double(Some(10)) // => Some(20)
double(None) // => None
```

値があったりなかったりする場面で便利に利用することができます。ただし、あらゆるケースで使おうとするのはやめてください。

ここからはタイトルにあるように推奨されない使用方法について述べようと思います。

# 常に値がある、または常に値がない場合

```scala
val foo: Option[Int] = Some(42)
val bar: Option[Int] = None
```

当然ですが、値があったりなかったりするときのものなので、そもそも必ず値がある場面では避けるべきです。
実際にもろにそのようなコードを書いてしまうことはあまりないですが、例えば次のようなコードはありがちです。

```scala
def f(a: Int, b: Option[Int]): Int

val foo: Option[Int] = Some(42)

f(13, foo)
```

関数 `f` の引数型にあわせるために変数`foo`を`Option`型で定義しています。さらに`foo`が実際は`Some`型なのをいいことに、`get`を使い始めたら要注意です。

# `Some`と`None`の組み合わせが限定される場合

```scala
case class Foo private(a: Some[Int], b: Some[Int])
object Foo {
  def makeA(a: Int): Foo = new Foo(Some(a), None)
  def makeB(b: Int): Foo = new Foo(None, Some(b))
}
```

この例では他の箇所では直接コンストラクターは呼ばれないものとします。
`Foo`のフィールド`a`と`b`はどちから一方のみが必ず`Some`であり、他方は`None`となります。しかしながら、型としては両方`Some`、両方`None`がありえます。これはmatch式を書いた場合などに顕著な問題です。

```scala
foo match {
  case Foo(Some(a), None) => // ...
  case Foo(None, Some(b)) => // ...
  case _ => // 実際は到達しないが警告になるので定義する
}
```

コメントの通り、実際には到達しないにもかかわらず警告に対応するためだけにケースを定義する必要があります。

対処としては有効な組み合わせをサブタイプにできるか検討しましょう。

```scala
sealed abstract class Foo
object Foo {
  case class A(value: Int) extends Foo
  case class B(value: Int) extends Foo
}
```

`sealed` で定義することでコンパイラ（とコードを読む人間）に対してサブタイプがこのソースファイル内にしかないことを宣言できます。

サブタイプが限られているという定義は`Option`などと同様です。`Option`はこのsealedを使ったテクニックの１つをジェネリクスとして用意した型といえます。使用方法が完全にマッチしないなら直接`sealed`で定義することを推奨します。

また、この例のように２つの値のどちらか一方が有効な場合を表現する汎用的な型として `Either` があるので、こちらも検討してください。

https://www.scala-lang.org/api/2.13.6/scala/util/Either.html

# `None` では不十分な場合

たとえば `Some` が **成功**、 `None` が **失敗** を表すという用途はよくあります。実際Scala標準ライブラリのコレクションでは`find`は見つかれば`Some`、見つからなければ`None`を返します。`find`であれば見つからないこと以上の情報を求めていないので`None`でよいですが、たとえばユーザー入力を受け取って処理を行う場合はどうでしょうか？ ユーザーに意味のあるエラーメッセージを出したいなら失敗となった理由も返すべきです。

もし失敗した理由を返したいなら `Try` を利用できます。
`Try[A]` は成功した場合は `A` 型の値を保持した `Success` 型に、失敗した場合は `Throwable` 型の値を保持した `Failure` 型になります。

https://www.scala-lang.org/api/2.13.6/scala/util/Try.html

`Trhowable` でなく任意の型の値を返したい場合は `Eihter` を使用します。

# `Option` が多重になった場合

これは絶対に避けろとは言いませんが、避けた方が無難な例です。

```scala
def readContent(path: String): Option[String] = // 省略
def parseString(content: String): Option[Result] = // 省略
def parseFile(path: String): Option[Option[Result]] =
  readContent.map(parseString)
```

このコードではファイルを開き、そのストリームを読み取って結果を返します。
ストリームの読み取り処理では結果がNoneになることがあります。
ファイルが開けなかった時と、読み取り処理でNoneになったときでNoneになる位置が違うので区別できますが、この値を利用する側からすると中途半端な情報です。どちらがNoneだったのかで呼び出し元で対応が必要なのであれば、Exceptionを返して欲しいですし、特に気にする必要がないのであれば多重ではない`Option`であってほしいです。
つまり、`Try[Result]`か単純な`Option[Result]`にして欲しいところです。

# まとめ

いくつか例を挙げましたが、これらに限らずより適切な型が存在することはあります。とても便利な`Option`型で表現できたとしても、もっと正確な表現ができる適切な型がないか一度検討する価値はあります。
【GAS】GmailAppでメール下書きを開くURLを生成する→できないので検索から開かせる

2021/9/9修正：直接開くURLは生成できないので、件名で検索させる

##参考
[【GAS】Ｇメールの下書き（ドラフト）機能まとめ【サンプルソース付】](https://caymezon.com/gas-gmail-draft/)
[Google Apps Script Reference: Class GmailDraft](https://developers.google.com/apps-script/reference/gmail/gmail-draft)



##やりたいこと
GASでメール下書きを取得し、下書きを直接開くURLを生成したい

##やったこと
Gmailを検索するURLから「下書き内で、件名を指定して検索→1件しか出ないはずなのでそれを開く」ようにする
`""https://mail.google.com/mail/u/0/#search/in%3Adraft+"+ 件名`

~~GmailDraftのクラスからmessageIdを取得して、draftを開くときのURLに入れ込む
`"https://mail.google.com/mail/u/0/#drafts?compose="+ messageId`
これでいける。~~
##コード

```javascript:getDraftUrl.gs
function getDraftUrl() {
  const allMailDrafts = GmailApp.getDrafts();
  const targetDraftSubject = allMailDrafts[0].getMessage().getSubject();

  const targetDraftUrl = "https://mail.google.com/mail/u/0/#search/in%3Adraft+" +targetDraftSubject;

  console.log(targetDraftUrl);
}

```

##メモ
受信トレイのメールに関しては直接開くURLが存在するが、下書きにはないようだった。
次善の案として、検索から直接指定する方法をとった。
記事を書いたときは開けていた直接URLだが今日見たら開けなくなっていた、なんでなのか

~~getMessageId()の方で取得したIDじゃないと開けない。
getId()は何に使うのかよくわからん。下書きを送信する(send())ときぐらい？~~

##あとがき
メール下書きを一括取得してチェックして一括送信する、みたいな要件があってチェック用にURLを生成したかった。ただ下書きはURLが違うようで参考になるページがなく~~ちょっと~~かなり苦労したのでメモ。

[stack overflow: Obtain a link to a specific email in GMail](https://stackoverflow.com/questions/20780976/obtain-a-link-to-a-specific-email-in-gmail)
[Qiita: 【GAS】Gmailのメールのリンクの取得](https://qiita.com/golyat/items/6f78d218c0c7f4fc91dc)
このへんはうまくいかなかった。下書きじゃなくて受信メールなら↑でいけると思う。