OmniAICreator/Qiita-1.07M huggingface dataset:
docker-composeで作成されたiptablesにホスト側のルールを適用する

# † 前回のあらすじというか動機
家庭内LANのラズパイ上で三年弱稼働したとある実行環境を、docker noob が docker-compose を用いてリプレイスした。内容的には python から node.js、apache から nginx なカンジ。
一通り完了したので docker のログを見ながらいろいろ粗探ししてたら家庭内LANのWindows機からウザい接触が頻発していることに気づいた。そういえば旧環境には iptables を施していたなぁ新環境にも適用しようかぁとか思ったら rules.v4 ファイルが存在していて中を見てみたら、設定した覚えのないルールがぎっしり書き込まれていてビックリ！
ホスト側の設定どうすればいいの？ これ消しちゃまずいよね！？

# † 環境
```
$ uname -a
Linux raspberrypi02 4.14.98-v7+ #1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l GNU/Linux

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Raspbian
Description:	Raspbian GNU/Linux 9.8 (stretch)
Release:	9.8
Codename:	stretch

$ docker -v
Docker version 18.09.5, build e8ff056

$ docker-compose -v
docker-compose version 1.24.0, build 0aa5906

$ iptables --version
iptables v1.6.0
```

# † やったこと
## rules.v4 の書き換え
/etc/iptables/rules.v4 に docker が自作したっぽいルールを全消去して、ホスト側のルールだけ書く。
## 更新したルールの適用
```
$ sudo /usr/sbin/iptables-apply /etc/iptables/rules.v4
```
## docker サービスの再起動

```
$ sudo systemctl restart docker
```

## docker-compose で再ビルド
```
$ docker-compose up --build
```


以上。
ホスト側には自分で書いたルールが適用されて、docker network は自動作成されたルールで動く。

# † この記事を記した理由
iptables が理由で docker 環境に問題が発生したというような記事は日本語・非日本語で散見されるが、docker 環境と干渉しないようにホスト側に設定を施す具体的な方法について明記された記事に出くわせなかった。Linuxに親しんだ層にはきっと当たり前すぎて記すほどではないのだろう。
いろいろ調べて深い沼に沈んでは容易ではなさそうだと感じて――DOCKER-USERに書けとか記されているのだが具体例がなかったり――放棄すること数度、dockerの公式ドキュメントを見たらそんなに難しくないんじゃね？と思えたので、やってみたら簡単だった。
ニッチっぽいので記録してみた。

# † 留意点
- iptables を再起動すると docker が作成した iptables が効かなくなる。docker サービスの再起動が必要。
- 動作していた期間にもよると思うが、docker-compose up --build 時に既存の docker ログがターミナルを埋め尽くすイキオイで出力されるかもしれない。ctrl + c で停止、再度 docker-compose up --build で。当環境において二度目は発生していない。

# † 参考
[Dockerコンテナがiptablesのエラーで立ち上がらなくなった](https://sioyaki.com/entry/2016/06/15/084317)
[ラズパイ2TypeBのファイアーウォール設定メモ](http://k-mawa.hateblo.jp/entry/2018/01/08/175258)
[Raspberry Piを攻撃から守るための手順](https://makezine.jp/blog/2017/09/secure-your-raspberry-pi-against-attackers.html)

# † 追記
raspbian buster にしてみたらまた違う問題が発生したので、タグを変更。
2019/10/12時点で buster での完璧な対処はこちら。
[Debian 10(buster) + ufw + Docker: Dockerコンテナが外部と通信できない問題の対処](https://qiita.com/no_clock/items/5145ff0aeb377fda3934)
Barabasi Albert model (BAモデル) を Python で 10行で書いてみた。

## Motive
約10年くらい前にscilabで簡単な複雑ネットワークモデルを作成したのですが、packageが豊富で学習コストが比較的低いpythonで実装してみました。

## Algoryism
1. 初期値は2つのノード（点 or dot）を用意してエッジ(線)でつなぐ
2. ノードが一つ増える。元からあるノードを一つ選び、新しくできたノードとつなぐ。
    - この時の選び方は各ノードが持つエッジの数(次数)に依存する。 <br>
    例)<br> 

    |ノード|所有しているエッジ数|次の新しいノードにつながる確率|
    |:--|:--|:--|
    |0|1|1/10　　　　　10%|
    |1|2|2/10　　　　　20%|
    |2|3|3/10　　　　　30%|
    |3|2|2/10　　　　　20%|
    |4|1|1/10　　　　　10%|
    |5|1|1/10　　　　　10%|

    ![Figure_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/19653c4e-142b-bbee-74de-a3af2216d324.png)

3. 2をひたすら繰り返す

## Development

パッケージを含めて10行です。

```Python
import random
__TRAINING_COUNT__ = 100
pile, edges= [1,1], [(0,1)]
for i in range(__TRAINING_COUNT__ ):
	board = sum([[i] * p for i,p in enumerate(pile)], [])
	node = random.choice(board)
	pile[node] = pile[node] + 1
	pile.append(1)
	edges.append((node, len(pile) - 1))
print("pile:{}, edge:{}".format(pile, edges))
```

新しくノードにつなぐ部分ですが単純に次数分のノードNoを配列に格納してランダムで取得しています。

``` Python
sum([[i] * p for i,p in enumerate(pile)], [])
```

ものすごいnodeがあった場合は配列が崩壊しそうです。他にいい方法はないかと考え中です。

## Conclusion
```
pile:[7, 12, 1, 2, 15, 9, 5, 9, 1, 4, 3, 2, 3, 3, 2, 1, 3, 2, 2, 2, 1, 1, 1, 5, 2, 5, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 5, 1, 1, 1, 1, 1, 2, 2, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1], 
edge:[(0, 1), (1, 2), (1, 3), (1, 4), (1, 5), (0, 6), (1, 7), (1, 8), (4, 9), (5, 10), (1, 11), (10, 12), (4, 13), (5, 14), (9, 15), (5, 16), (5, 17), (12, 18), (14, 19), (4, 20), (0, 21), (4, 22), (6, 23), (19, 24), (6, 25), (13, 26), (11, 27), (0, 28), (4, 29), (7, 30), (23, 31), (16, 32), (30, 33), (6, 34), (16, 35), (4, 36), (25, 37), (23, 38), (23, 39), (0, 40), (1, 41), (7, 42), (27, 43), (24, 44), (0, 45), (4, 46), (4, 47), (7, 48), (25, 49), (13, 50), (37, 51), (4, 52), (44, 53), (1, 54), (4, 55), (4, 56), (27, 57), (7, 58), (23, 59), (18, 60), (40, 61), (25, 62), (10, 63), (7, 64), (44, 65), (62, 66), (54, 67), (44, 68), (5, 69), (1, 70), (44, 71), (12, 72), (50, 73), (7, 74), (60, 75), (3, 76), (7, 77), (6, 78), (17, 79), (9, 80), (68, 81), (57, 82), (5, 83), (5, 84), (7, 85), (84, 86), (67, 87), (4, 88), (9, 89), (51, 90), (1, 91), (43, 92), (25, 93), (66, 94), (4, 95), (92, 96), (4, 97), (86, 98), (0, 99), (5, 100), (77, 101)]
```

## Future
上記をもとにnetworkXで描画、分析でもしてみよう。:thought_balloon:
あとはこのモデルの亜流をいろいろと考えてみます。:construction_worker:
非同期処理のサンプルモデルにも使えそうです。

SNSの普及とともに複雑ネットワークが流行し始めたのは2000年くらいですが、ネットワークの性質を求める方法としてノードの次数グラフや平均最短経路ノードの密度などがありますが、他に一般解・評価方法がまだありそうな気がします。さまざまなジャンルの実データを使って分析する必要がありそうです。

## Reference
[複雑ネットワーク](https://ja.wikipedia.org/wiki/%E8%A4%87%E9%9B%91%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF)
[Barabási–Albert model](https://en.wikipedia.org/wiki/Barab%C3%A1si%E2%80%93Albert_model)
Flask/Pythonの仮想環境を作成して、簡単なWebアプリを作成する

# この記事を読んで出来る事
「[多分これが一番簡単だと思います。 WindowsでPython仮想環境構築 2019年](https://qiita.com/glarpycat/items/e4d5e4199095af0e0a90)」の続き。具体的にライブラリをインストールして簡単なWebアプリを作成、実行する。

# 必要な環境
- Windows10
- Python3.7
- Visual Studio Code
- Python Extension Pack(VS Code拡張機能)
- pipenv

# 新規にインストールされるパッケージ
- Flask (Pythonライブラリ) と関連ライブラリ
 
# 仮想環境の作成
Powershell を開いて以下のコマンドを実行

```
PS> mkdir flask-py3
PS> cd flask-py3
PS> pipenv install --python 3.7
PS> pipenv install flask
```

# 仮想環境の切り替え
VS Codeの左下のステータスバーから上で作成した仮想環境を選択する
![vscode-switch-flask-python3-screenshot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494172/ca1a40eb-0866-d58b-9f0a-d52c6586e595.jpeg)



# 「Hello World!」プログラム作成

```python:hello.py
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'こんにちは！'


if __name__ == '__main__':
    app.run()
```

# Webアプリの実行と動作確認
VS Code のタブの右にあるアイコンをクリックして実行
![vscode-py-exec-on-terminal-screenshot.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/494172/0c498194-53bc-c22b-5843-94bd81e7f449.jpeg)


ターミナルに出力されたURLにアクセスして動作確認

# クエリパラメータを受け取る
クライアントから名前のデータを受け取り表示する

```python:hello_name.py
from flask import Flask, request, redirect, url_for

app = Flask(__name__)

@app.route('/')
def index():
    return 'こんにちは！'

# /post にアクセスしたときの処理
@app.route('/post', methods=['POST'])
def post():
    name = request.form.get('name')
    if name is None:
        return redirect(url_for('index'))
    return '{name}さん、こんにちは'.format(name=name)

# /get にアクセスしたときの処理
@app.route('/get', methods=['GET'])
def get():
    name = request.args.get('name', 'NO NAME')
    return '{name}さん、こんにちは'.format(name=name)

if __name__ == '__main__':
    app.run()
```

スクリプトを実行後、ブラウザに以下のURLを入力
http://127.0.0.1:5000/get?name=Mike

POSTの動作確認はPowershelから確認するのがお手軽

```powershell:PowershellからPOST
PS> Invoke-WebRequest -Method POST http://localhost:5000/post
PS> Invoke-WebRequest -Method POST -Body "name=Mike" http://localhost:5000/post

```

# テンプレートを使う

```python:hello_with_template.py
from flask import Flask, render_template, request, redirect, url_for

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

# /post にアクセスしたときの処理
@app.route('/post', methods=['POST'])
def post():
    name = request.form.get('name')
    if name is None:
        return redirect(url_for('index'))
    return render_template('index.html', name=name)

# /get にアクセスしたときの処理
@app.route('/get', methods=['GET'])
def get():
    name = request.args.get('name', 'NO NAME')
    return render_template('index.html', name=name)

if __name__ == '__main__':
    app.run()
```

# Python2.7環境で文字コード関係のエラーが出る場合

> SyntaxError: Non-ASCII character ..., but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

プログラムの先頭行に以下の行を追加

```python
# -*- coding: utf-8 -*-
```

> UnicodeDecodeError: 'ascii' codec can't decode byte 0xe3 in position 0: ordinal not in range(128

`app = Flask(__name__)`の前に以下の行を追加

```python
import sys

reload(sys)
sys.setdefaultencoding('utf-8')
```

毎回、行を追加するのが面倒なのとPython3環境にとっては無関係なコードを書くのもアレなので、上記プログラムを書いたファイルを作成して`.venv\Lib\site-packages\sitecustomize.py`として保存するのがオススメ
【Linux】Jet Brains製エディタを日本語化するスクリプトを組んでみた

# はじめに
　私はJetbrainsのエディタをいつも愛用しています。ただ、OSを再インストールする際などで頻繁に日本語化するため、たまにパスミスで環境構築に詰まることがあります。そこで、Jetbrains全てのエディタに対応した日本語化スクリプトを組み、作業ミスを減らそうというのが今回のお話です。

# 日本語化スクリプト
``` bash
#!/bin/bash
if [[ -z $1 ]]; then
	echo "第一引数にエディタのパスを指定してください。"
	exit 0
fi

if [[ ! -e "$1" ]]; then
	echo "存在するパスを指定してください。"
	exit 0
elif [[ ! -e "$1/bin/" ]]; then
	echo "pluginsやbinディレクトリがあるパスを指定してください。"
	echo "例: $ bash jetbrains_japanese.sh /opt/webStorm/WebStorm-192.6262.59/"
	exit 0
fi

JETBRAINS_PATH=$(cd "$1" && pwd)
echo $JETBRAINS_PATH

wget http://ftp.jaist.ac.jp/pub/mergedoc/pleiades/build/stable/pleiades.zip
unzip pleiades.zip -d $JETBRAINS_PATH

for v in $(find $JETBRAINS_PATH/bin/ -name *.vmoptions); do
	echo -Xverify:none >> $v
	echo -javaagent:$JETBRAINS_PATH/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar >> $v
done

rm pleiades.zip
```

なお、[ここ](https://raw.githubusercontent.com/HiroRittsu/setup/master/jetbrains_japanese.sh)から同じスクリプトをダウンロードできます。

# 使い方
1. まず上のコードをコピペorダウンロード
2. `bash jetbrains_japanese.sh /opt/webStorm/WebStorm-192.6262.59/`のように、jetbrainsのエディタがあるディレクトリを第一引数に指定
3. そのまま実行

※日本語化自体はエディタを実行する前に行ったほうが良いです。もし、スクリプトを実行しても日本語化できなかった場合は、ホームディレクトリに隠しディレクトリとして存在する設定ファイルを削除してください。
【はじめてのLTは、とても楽しい】 - 残暑に負けるな！自由研究LT大会 参加レポート - #engineers_lt

初めてLTをやりました。緊張しました。
だけど、とても楽しかったです。やってよかった。そんなことを書きます。

イベントURL:
https://engineers.connpass.com/event/141228

今回はイベントの1週間前にLTのリハーサルを主催の方々が開いてくれました。
**リハーサルがとてもタメになったので、リハーサルの体験談を中心に書いていこうと思います**。

## リハーサルの状況

まず、事前に主催の方から登壇者へ日程調整の連絡があり、[調整さん](https://chouseisan.com/)を使ってリハーサル日を決めました。

リハーサルは本番とは別の会場を主催の方に準備していただきました。主催の方々と登壇者が同じ部屋（プロジェクターがある広めの会議室）に集まって、登壇者が作ってきたスライドをプロジェクタでスクリーンに映しながらやりました。リハーサルの参加者は、主催側と登壇側を合わせて１１名ほどでした。本番は立ってLTするのですが、リハの段階では座ってやる人が大半でした。また、本番とは発表の順番も異なりました。それ以外は本番と変わらない形でやりました。

タイムキープは主催の方にやっていただき、本番と同じく5分測ってもらいました。**本番と違ってタイムリミットを告げるのは銅鑼ではなかったです**。

登壇者とタイムキーパーがリハーサルをしている間、他の主催者や登壇者はLT内容へのフィードバックを書きました。今回は[HACKMD](https://hackmd.io/)というサービスを主催の方に準備してもらい、そこにMarkdown形式でフィードバックを書いていきました。リアルタイム反映機能があるので、みんなが書いた内容がどんどん反映されていく様は賑やかで面白かったです。

さて、実際に自分が受けたフィードバックの一部を抜粋してみました。

---

- P1 スライドタイトルに名前があるといい（xxx）
- P4 「"」に閉じがない（xxx）
- P4 初対面の～ 文字の改行位置を変えると読みやすい（yyy

---

上記のように、フィードバックは**「スライドのページ番号、指摘内容、指摘者の名前（xxxやyyyで伏せました）」**の形式で書かれています。もちろん、フィードバックは改善点を指摘するだけではなく、「ここが良かった」と指摘するのもアリです。良いフィードバックを貰えるのは励みになるし、自分の意図がそのまま相手に伝わったかどうかの判断材料になります。

リハーサル時のHACKMDを確認したところ、登壇者１人あたり平均２０個のフィードバックがありました。５分間で２０個以上のフィードバックを貰えたのはとても有難いことだと思います。

主催の方から促され、自分も、ほかの登壇者へフィードバックをしました。自分もフィードバックを受ける側だったので最初は躊躇しましたが、指摘をしたことで自分のLTの改善点に気付けてからは積極的にフィードバックしてゆきました。ほかの登壇者のLTを聴き、参考にできそうな点を勉強できました。

自分が最も印象に残ったフィードバックは、「**５分の制約があると早口になりがちなので、意識してゆっくり喋るようにする**」というものでした。じっさい、本番でも大事だったので今後も意識して実施していこうと思いました。**緊張すればするほど早口になることも分かったので、緊張の度合いが大きいほどゆっくり喋ることを心掛けようと思いました**。

こうして、沢山のフィードバックを貰ってリハーサルは終わりました。これから本番まで、LTの内容をブラッシュアップしてゆきました。

## ブラッシュアップ

さて、フィードバックを貰ったのは良いけれど、数が多くて大変だと思いました。でも、１つ１つを見ていくと、同じ番号のスライドに同じような指摘が幾つかあることに気づきました。そこで、受けたフィードバックの内容を大きく以下の３つに分類することにしました。

---

- 文字が多くて見づらい
- 主語がなく、内容が具体的でない
- 論理的な構成になっておらず、理由が書かれるべきところに事実が書かれている

---

上記の点を意識してブラッシュアップをすることにしました。

さて、上記の指摘はさらに、１つの根本原因にまとめられるように思いました。すなわち、「**自分が何を主張したいのか明確でない**」ということです。言いたいことをもっと明確に言語化できたら、より良いスライドになるのではないだろうか。そう思いました。

そこで、まずは**スライドを直すのではなく、言いたいことを言語化することから始めました**。具体的には、朝の通勤中にスマホにトークスクリプトを書き、それを仕事の空き時間に人気のない場所で喋りました。この「スクリプト作成⇒トーク練習」のサイクルを１日１サイクル、計３サイクルくらい実施しました。

これでだいぶ頭が整理されたというか、だんだん自分の言いたいことが纏まっていくのを感じました。また、喋っていてどうしても詰まってしまう部分が出てきたことも分かりました。というわけで、休日に纏まった時間が取れたタイミングで、出てきた課題も含めてスライドを直しました。

スライドを直し、主催の方がLT会用に作ってくれたSlackチャンネルにアップして、参加者全員に再度フィードバックを依頼しました。１～２日ほどでフィードバックが頂けたので、指摘内容を再度分析してスライドに反映しました。２回目のスライド修正は、１回目よりもスムーズに出来ました。おそらく、自分の言いたいことがそれなりに頭の中に纏まっていたからだと思いました。

最終的にスライドが完成したのは本番前の夜23時くらいで、以下の形になりました：
https://speakerdeck.com/yasuakiomokawa/hello-remote-world

そして比較用に、こちらがリハーサル前に作ったスライドです：
https://speakerdeck.com/yasuakiomokawa/on-rehearsal-chu-dui-mian-falseren-torimotowakusurukotu

自分で見ても、だいぶ変わったと思います。

## まとめ

登壇のリハーサルはやったほうがいいです。フィードバックをもらい、ブラッシュアップする過程で自分の頭が整理され、より良い資料になることが分かったからです。

今回はLT会の参加登録からリハーサル前にスライドを作成するまで3週間くらいかかりました。今後はこのスピードをもっと短くするように工夫したいです。具体的には、WBSを最初に作って、当日までの流れを意識して作業していきたいです。

言いたいことを言語化するのは根気のいる作業だということも分かりました。一気にゴールに辿り着こうとせずに、繰り返し作業するということが大事だと学びました。

本番後には、主催の方がリハーサルと同じようにHACKMDを用意してくれたので、登壇者全員でふりかえりを書きました。この内容をもとに、後日懇親会をやる予定になっています。予定が合わないかもしれませんが、なるべく参加したいと思います。

**至れり尽くせりの環境で、本番も盛り上げていただき、主催の方には感謝しかありません。最後になりますが、改めて御礼を申し上げます!**
JVMのinvoke系命令の違い

### 関連リソース
* [Chapter 6. The Java Virtual Machine Instruction Set](https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html)
* [Javaにおけるメソッド
呼出しの仕組み](https://www.oracle.com/webfolder/technetwork/jp/javamagazine/Java-SO17-MethodInvoc.pdf)
* [Java SE 8のラムダ式はどう実現されたのか？──実装の経緯、内部的な仕組みを理解する
](https://builder.japan.zdnet.com/sp_oracle/35077771/)

### 共通していること
オペランドのオフセットでconstant poolからmethod infoを引いてきてその情報を元にディスパッチしている。

## invokevirtual
インスタンスメソッドの呼び出し。

### 使用例
```java 
class Main {
    public static void main(String[] args) {
        System.out.println("hello");
    }
}
```
```
  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String hello
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
```

## invokestatic
スタティックメソッドの呼び出し。

### 使用例
```java
class Main {
    public static void main(String[] args) {
        System.currentTimeMillis();
    }
}
```
```
  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #2                  // Method java/lang/System.currentTimeMillis:()J
       3: pop2
       4: return
```

## invokeinterface
インターフェースを経由した呼び出しに使われる。

### 使用例
```java
import java.util.*;

class Main {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.get("hello");
    }
}
```

```
  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/util/HashMap
       3: dup
       4: invokespecial #3                  // Method java/util/HashMap."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #4                  // String hello
      11: invokeinterface #5,  2            // InterfaceMethod java/util/Map.get:(Ljava/lang/Object;)Ljava/lang/Object;
      16: pop
      17: return
```

## invokespecial
その他のメソッド (コンストラクタ、superclass、privateメソッド) の呼び出しに使われる。

### 使用例
```java
class Main {
    public static void main(String[] args) {
        new Main();
    }
}
```

```
  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class Main
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: pop
       8: return
```

## invokedynamic
呼び出すメソッドを動的に検索して呼び出す。JVM上で実装された動的言語や、Lambdaなどで使われる。

### 使用例
```java
class Main {
    public static void main(String[] args) {
        Runnable runner = () -> { System.out.println("hello"); };
    }
}
```

```
  public static void main(java.lang.String[]);
    Code:
       0: invokedynamic #2,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
       5: astore_1
       6: return
```
IE(internet explorer)でのWeb画面自動テストアプリを作成しました

#概要
Web画面のテストを自動で実行するアプリを作成しました。
画面項目のテスト値をEXCELファイルに事前に設定し、アプリに読み込ませ
テスト画面への値設定、ボタン押下、実行結果の値、画面エビデンスの取得を自動で実行します。
※対象のブラウザはIEのみです

以下でアプリを公開しています
https://github.com/ZumWalt22/WebBrowserTestApp
以下のディレクトリをダウンロードしてください
WebBrowserTest1.0.0.1

詳細な操作手順はWikiを参照ください
https://github.com/ZumWalt22/WebBrowserTestApp/wiki

ソースコードは以下で公開しています
※基本的な処理のみの実装のため、必要に応じ、処理を追加、修正し使用してください
https://github.com/ZumWalt22/WebBrowserTest

#作成経緯など
かれこれ１０年ほど前、まだ画面の自動テストツールがあまり出回っていないころ
某案件で画面テストを永遠と強いられるのに嫌気が差し、何とか自動でできないかと自作したものです
そこそこ時間をかけて作成したにもかかわらず、以降使う機会がなく
長い間、PCの片隅に放置状態だったため、すこし勿体ないかなとおもい公開しました。

自動テストツール全盛の今となっては有用性は薄まりますが
小規模な開発で画面の自動テストをしたいが商用のテストツール導入するまでもないとかで活用いただけると幸いです。
.NETのWebBrowserTestコンポーネントを利用し作成しています。
開発言語はC#を使用しています。

#簡単な使い方の説明

1．テストケース設定
EXCELテストケース設定シートにテストケースを設定します
設定情報　テスト対象画面のURL、HTMLタグのID、項目設定値、ボタンのID、結果画面期待値を設定後
CSVファイルに変換しアプリ配下「TestCase」フォルダに配置しておきます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/0e7d1cfe-a5d0-cdfc-4188-28c6af7ecbbc.png)

2．テスト実行
WebTestアプリの「テストケース一覧」から、実行したいテストケースにチェックを入れます
「テスト自動実行」ボタンを押下するとチェックされたテストケースが自動実行されます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/90e3f20c-a09b-11ef-6892-cc8efdde83f6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/32fbbb8d-e083-6b45-8eb4-8a7e27c5f50c.png)


3．テスト結果確認
テストが完了するとダイアログボックスが表示され、テスト結果（テスト成功件数／総テスト件数）が通知されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/fe2d774a-e730-fda6-72fd-108d2505142c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/a8cd4cd6-ec7a-c650-36f1-fd23baefeb76.png)


テスト結果の詳細は、アプリ配下「Ebidence」フォルダに生成されます
結果画面のHTMLと画面エビデンスが出力されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/0101d060-0c40-af1c-9cb1-10d704f26e28.png)

「TestResoultReport.csv」に事前に設定した期待値との正誤結果の一覧が出力されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172093/8c6cd19f-2e5b-3c87-0fd7-adf05f9b1f70.png)


#今度の機能追加予定
テスト前後のDB情報の取得機能を追加予定です。
現在、Config設定とするべき箇所を直打ちで記述してしまっているため、Configから設定変更可能にする予定です。
gitlab.com の board でドラッグ＆ドロップできない問題の対処法

他のタブをすべて閉じる！

ずっと対処法わからなかったけど、偶然解決法の投稿を見つけ、なんと実際に効果があったのでメモ。

参考: https://gitlab.com/gitlab-org/gitlab-ce/issues/21581#note_52213646
UnityからiOS&Androidの画面輝度をMAXにする方法

現在QRコードを使用したアプリの開発をしており、QR表示画面で画面輝度をMAXにする機能を実装するための備忘録。。

UnityからOSの画面輝度をiOS、Androidともに変更することができなそうだったので各OSごとにネイティブプラグインを実装した。

##環境
・HIAWAY VTR-L29 Android8.0.0, API26
・iphoneX
・Unity(version 2018.3.0f2)


#参考
画面輝度のネイティブプラグインについて
https://qiita.com/GeneralD/items/5e77121cf0b26a103d38

ネイティブプラグインってなに？っていう状態の人向け→この動画を参考にして勉強しました。
・iOS
https://youtu.be/zP5mjHzJR-o?list=LL_wGxSuSiBEf1EWiFFdo4pA

・Android
https://www.youtube.com/watch?v=bmNMugkOQBI&list=PLj-rpMEVqe_iaQHssCAtpEgrO3M_tIS3y


##iOS
まずUnityのAssets/Plugins/iOSにBrightnessPlugin.mmを配置

```C++:brightness.mm
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h

#ifdef __cplusplus
extern "C" {
#endif
    
void setBrightness(float val) { [UIScreen mainScreen].brightness = val; }

#ifdef __cplusplus
}
#endif

```

一度ビルドするとXcodeのLibraries/Plugins/iOS以下にBrightnessPlugin.mmが作られます。

##Android
※※※Androidネイティブプラグインも実装はしてみたものの正常に動作せず、色々試してみたものの、全てうまく行かず、、、（どなたかAndroidネイティブプラグインの実装にお詳しい方がいたらご教授いただけると、、）
なので、Unity側から直接Androidの画面輝度を実装します。


## Unity側の実装
次にUnity側からiOSにはBrightnessPlugin.mmに定義したメソッドを呼び出すようなc#インターフェースを実装します。AndroidにはAndroidJavaClassから直接javaを実行します。

```c#:SetBrightness.cs
using System;
using UnityEngine;

#if !UNITY_EDITOR && UNITY_IOS
using System.Runtime.InteropServices;
#endif

public static class SetBrightness 
{
    #if !UNITY_EDITOR && UNITY_IOS
    [DllImport("__Internal")]
    private static extern void setBrightness(float brightness);
    #endif
   
    public static void DoAction(float maxbright) {
        #if UNITY_EDITOR
        #elif UNITY_IOS
        setBrightness(maxbright);
        #elif UNITY_ANDROID
        var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        var activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
        activity.Call("runOnUiThread", new AndroidJavaRunnable(() => {
            var window = activity.Call<AndroidJavaObject>("getWindow");
            var lp = window.Call<AndroidJavaObject>("getAttributes");
            lp.Set("screenBrightness", maxbright);
            window.Call("setAttributes", lp);
        }));
        #endif
    }
}
```

使用したいスクリプト上で

``` c#
SetBrightness.DoAction(1.0f);
```
とかすると画面輝度が最大になります。
ちなみにDoActionの引数は0~1.0fでないと多分エラー吐きます。（実証してません）

※※※iOS上で試しましたがアプリの実行中にコントロールパネルで画面輝度を変更したり、iPhoneの電源ボタンをタップし、再度開いてアプリに戻っても画面輝度が最大になりません。（お詳しい方がいましたらよろしくおねがいします、、！）
JavaScriptとElectronで日付による有効期限判定

## 日付での有効期限管理が許されるのは1990年代までだよねー！
たしかに、今更感が半端ない。
でも、開発段階のテストツールをユーザーテストするとかで一時配布したいけど削除を徹底できない場合とか、限られた場面で使えそう。
Webアプリなのに会社の規約上レンタルサーバーを借りて公開とかができない場合は、Electronでスタンドアローン化して配布するけどその場合の処理も紹介する。
## まずご用意いただくのは、一家に一台「Dateオブジェクト」
Dateオブジェクトを使えば現在時刻や指定時刻における、1970/01/01 00:00:00からのミリ秒が取得できる。
現在時刻と有効期限期日で比較して、現在時刻のほうが小さければ有効期間、大きければ有効期間が過ぎている、と判定する。

```.js
	var todayObj = new Date();  // new Date('yyyy-mm-ddThh:mm:ss');
	var today   = todayObj.getTime();  // 1970/1/1午前0時からの現在までのミリ秒

	// new Date('2019-09-05T15:57:00');のように0埋めが必要
	var endObj   = new Date('2019-09-30T23:59:59');  // 終了日の指定
	var end   = endObj.getTime();  // 1970/1/1午前0時からの終了日までのミリ秒

	if(today <= end){
		// 有効期限の範囲内
		alert("今日は有効期限より前です。");
	}
	else{
		// 有効期限切れ
		alert("今日は有効期限より後です。”);
        }

```


## 期限切れなら、バルスる。
### ウィンドウよ、さらば。
Webサービスならブラウザを閉じさせてしまえば良い。
なので、アラートでメッセージを出してから閉じる。


```timeLimit.js
 function isOverTimeLimit(){
	// アプリ起動時間と有効期限の判定をする
	var todayObj = new Date();  // new Date('yyyy-mm-ddThh:mm:ss');
	var today   = todayObj.getTime();  // 1970/1/1午前0時からの現在までのミリ秒

	// new Date('2019-09-05T15:57:00');のように0埋めが必要
	var endObj   = new Date('2019-09-30T23:59:59');  // 終了日の指定
	var end   = endObj.getTime();  // 1970/1/1午前0時からの終了日までのミリ秒

	if(today <= end){
		// 有効期限の範囲内
		alert("今日は有効期限より前です。");
	}
	else{
		// 有効期限切れ
		alert("今日は有効期限より後です。\n有効期限は "+endObj.getFullYear()+"/"+(endObj.getMonth()+1)+"/"+endObj.getDate()+" です。");
		// ウィンドウを閉じる
		window.open('','_self').close();
		window.open('about:blank','_self').close();
	}

}
```

この処理をページを読み出した時に行えばよい。

```index.html
<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<title>Time limit script</title>
		<script type="text/javascript" src="js/timeLimit.js"></script>
	</head>

	<!-- HTML部分 -->
	<body onload="isOverTimeLimit();">
		sample
	</body>
</html>
```



### Electronよ、さらば。
Electronの場合もMainWindowを閉じてやればよい。
レンダリングプロセスから閉じる方法もあるが、有効期間判定はツールが起動した直後に行うので、メインプロセスでやるのが楽。

```main.js
function createWindow () {
	// Create the browser window.
	mainWindow = new BrowserWindow({width: 800, height: 600})

	// and load the index.html of the app.

	// アプリ起動時間と有効期限の判定をする
	var todayObj = new Date();	// new Date('yyyy-mm-ddThh:mm:ss');
	var today	 = todayObj.getTime();	// 1970/1/1午前0時からの現在までのミリ秒

	// new Date('2019-09-05T15:57:00');のように0埋めが必要
	var endObj	 = new Date('2019-09-30T23:59:59');	// 終了日の指定
	var end	 = endObj.getTime();	// 1970/1/1午前0時からの終了日までのミリ秒

	if(today <= end){
		// 有効期限の範囲内
		console.log("今日は終了日より前です");
    mainWindow.loadURL(`file://${__dirname}/index.html`)
  }
	else{
		// 有効期限切れ
		console.log("今日は終了日より後です");
		dialog.showErrorBox("有効期限が切れています。", "有効期限は"+endObj.getFullYear()+"/"+(endObj.getMonth()+1)+"/"+endObj.getDate()+" です。");
		app.quit();		
	}

```

https://github.com/Kahiro-M/TimeLimitScript
[メモ] React Native (iOS) 環境構築のメモ


[chxcode](https://github.com/klaaspieter/chxcode) / anyenv / rbenv / nodenv などを使ってリポジトリごとにdependenciesを管理しているiOS開発環境で[React NativeのGetting Started](https://facebook.github.io/react-native/docs/getting-started.html) をやったときのメモです。 

# インストール

[React NativeのGetting Started](https://facebook.github.io/react-native/docs/getting-started.html) の通りに入れると既存の管理方針が壊れるため、概ね以下のようにしました。

### Watchman のインストール
npmではとても古い(deprecatedな)バージョンしか入れられないのでHomebrewで入れる必要があります。

Brewfileに追記してインストール

```ruby:Brewfile
brew 'watchman'
```

```zsh
$ brew bundle
```

### java のインストール
Homebrew Caskで適当な最新版を入れます。 

```ruby:Brewfile
cask 'java'
```

```zsh
$ brew bundle
```

### node のインストール
  
```zsh
$ nodenv install 12.9.1
```

### Xcode のインストール
xcode-installでインストール

```zsh
$ xcversion install 10.3
```

### Yarn のインストール
速度などの問題でnpmよりYarnを入れるべきらしいです。

```zsh
$ npm install -g yarn
```

### React Native CLI のインストール
`react-native-cli` は npm で 2.0.1 をグローバルに入れました

```zsh
$ npm install -g react-native-cli
```

react-native-cli はグローバルにインストールするように作られているのので、グローバルに入れた方が良さそう。

# プロジェクト作成
```zsh
$ react-native init ReactNativeGettingStarted
```

### プロジェクトのnode のバージョンを固定
12.9.1 に固定します。
  
```zsh
$ nodenv local 12.9.1
```

### プロジェクトのXcode のバージョン固定
chxcode (のauto) で 10.3 にバージョン指定しました ( `.xcode-version` ファイル)  

```zsh
$ echo "10.3" > .xcode-version
```
[windows][bat]バッチを使った簡易ソースバックアップ

バッチを使ったバックアップの例（メモ書き）

日付と時刻を意味するフォルダに、ソースなどの変更が多いファイルをバックアップします。

設定ファイルの変更を行う前後や
少し実施すると壊れるJSPファイル、JavaScript ファイル等、デリケートな場所や、少しデバッグして
何か理由の実験を行う際など。
実施時刻なら覚えていると思いますので、そのこと自体にバックアップを実施します。

```bat:backup.bat
@ECHO OFF
set FOLDER_NAME = %BACKUPDIR%%DATE:~-10,4%%DATE:~-5,2%%DATE:~-2%%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%

mkdir %FOLDER_NAME %
cd %FOLDER_NAME %

mkdir backup
copy "バックアップ元" backup\
cd .. 
```
Power BI の レポート テーマ のお話 

以前に作成したレポートについてはテーマを変更とかしない限り特段の問題はないはずだけれども、いろいろ確認する機会があったので整理しておこうかと。

- **テーマ の定義は pbix ファイルに少なくともひとつ埋め込まれている**
- **テーマを差し替えることでビジュアルの設定を変更することができる**
- **ただし、設定できるのは "既定値"**
- **ビジュアルごとの設定値を変更していないとき、テーマによる値(既定値)が利用される**

# テーマ の仕組み
**テーマで定義されるものは既定値**の一式であり、カラーパレットやレポートの背景など様々な要素属性を含めることが可能。

たとえば、ビジュアルをレポート上に配置したとき、フォントサイズを変えたりすることはあるでしょう。この時の変更とは "テーマによる既定値を継承しないための変更" と考えておけばよいかと思うのです。実際そんな感じに動作しますし、ビジュアルごとの設定とテーマは別の管理がされているので、ささっとテーマの差し替えができるわけで。

## 順を追って

- 優先されるのは ビジュアルごとの設定
- 設定されていない、つまり既定値であるときはテーマ

**ページにビジュアルを配置したとき、**
(フィールドなど設定が必要だけれども)プロパティペインのカードに表示されている値は テーマで定義された既定値。他にあるとすればアプリケーションによる既定値といったところでしょう。ビジュアルの配置位置や設定などは Layout に json で保存されているので確認するのは簡単。

.pbix\Report\Layout

ここではプロパティの値を変更していないから、Layout には配置位置など必要な値以外を保存していないことがわかる。Layout 内の該当ビジュアルのプロパティ、例えば フォントサイズが見当たらない。では、テーマによる既定値を採用って感じ。

**たとえば、フォントサイズを変更したとき、**
変更したフォントサイズは、Layout に該当するビジュアルの情報として保存される。フォントサイズがLayout で確認できるのでこれが採用される。

**このあとテーマを差し替えたら、**
差し替えるテーマにフォントサイズを指定されていたとしても、ビジュアルごとの設定、Layout にあるフォントサイズが採用される。

**テーマを適用させたいとき、**
ビジュアルのプロパティで適用させたい分は既定値に戻せばよい。

## インポートしたカスタム テーマ
Power BI Desktop が用意し利用されるテーマは ベーステーマっていう扱い。
.pbix\Report\StaticResources\SharedResources\BaseThemes\CY******.json

これ以外にカスタム テーマをインポートできる。このとき、

- 優先されるのは ビジュアルごとの設定
- 設定されていないつまり既定値であるときはカスタムテーマ
- カスタム テーマに定義されていない要素はベーステーマ

# Power BI Service で レポートを新規作成したとき
Power BI Desktop で作成した pbix ファイルをアップロードしたあと、Power BI Service 上でも新規のレポートを作成することはできる。ただし、**テーマは継承されない**。なぜか。Power BI Service 上では データセット(データ本体)とレポート(View)は分離して管理運用されるから。Power BI Desktop 同様にテーマのインポートができるようになればよいのだけど。

# 思ったこと🙄
ビジュアルの適用したすべての設定を既定値に戻すには、プロパティカードごとで ポチポチを"既定値に戻す" か、"書式のコピー/貼り付け"してしまえばよい。新たにビジュアル配置してこれを書式をコピーして貼り付ける感じに知れば、すべての書式は 既定値 になる。

## おまけ
デモとかする機会が多いのだけど、フォントサイズとかしゅっと大きくしたいことが多いのでこんな感じのカスタムテーマを使っている。

```json:フォントサイズだけ大きく
{
    "name": "My Theme",
    "visualStyles": {
        "*": {
            "*": {
                "*": [
                    {
                        "fontSize": 16
                    }
                ],
                "items":[
                    {
                        "textSize": 16
                    }
                ],
                "labels": [
                    {
                        "fontSize": 40
                    }
                ]
            }
        }
    }
}
```
スキーマについては以下くらいかな。
[Use Report Themes in Power BI Desktop - Power BI | Microsoft Docs](https://docs.microsoft.com/en-us/power-bi/desktop-report-themes)

# その他
- [Power BI 勉強会 - connpass](https://powerbi.connpass.com/)
- [Power BI Desktop を使い始めるには](http://qiita.com/PowerBIxyz/items/6c4735cb5b5712e70981)
- [Excel で Power Query を使い始めるには](http://qiita.com/PowerBIxyz/items/52e563037b855a099ca9)
【Larabel5.5】DB接続について※備忘録

今回はLaravelでテーブル作成する際に、DB接続しておく必要があるので、接続設定について色々迷走したのでここに記載しておく。

毎回のことながら、自分自身の備忘録ですw

DB接続されていないと `php artisan migrate` しても以下エラーが出ます。

```
In Connector.php line 67:
  SQLSTATE[HY000] [2006] MySQL server has gone away
```

```
In Connector.php line 67:
  Packets out of order. Expected 0 received 45. Packet size=4739923
```

DB接続設定でどうやってすればいいのか、色々調べてみると以下2ファイルが関係していることがわかった。

```blog/.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret
```

と

```config/database.php
<?php

return [

    'default' => env('DB_CONNECTION', 'mysql'),

    'connections' => [

        'sqlite' => [
            'driver' => 'sqlite',
            'database' => env('DB_DATABASE', database_path('database.sqlite')),
            'prefix' => '',
        ],

        'mysql' => [
            'driver' => 'mysql',
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
            'unix_socket' => env('DB_SOCKET', ''),
            'charset' => 'utf8mb4',
            'collation' => 'utf8mb4_unicode_ci',
            'prefix' => '',
            'strict' => true,
            'engine' => null,
        ],
~省略~
```

「なんで2つもあるねん！」と思ったが、どうやらそれぞれ役割が違うみたい。

```
config/database.php
```

の方は `.envファイル` から読み込んでいるらしいことがわかった。
なので、DB設定する場合は、 `.env`   ファイルの方を修正すれば良い。
修正箇所は

```blog/.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1 //ここ
DB_PORT=3306
DB_DATABASE=homestead　//ここ
DB_USERNAME=homestead　//ここ
DB_PASSWORD=secret　　　//ここ
```

 `//ここ` と記載した箇所を修正すればDB接続はOK。

これでDB接続設定は完了です。
システム再構築時のサーバサイドをKotlinで作るかJavaで作るか

限られたシーンでのお話なんだけど、割と世の中でありそうなシーンであることと、違っているところや他の観点があればアップデートしたいのでQiitaにpublishする。
あと、細かい構文レベルでの差異を取り扱う記事は多く見られるものの、様々な観点で比較している記事が少なく見えたことも書いた理由。

## 比較の前提
- 既存のJavaシステムをSpring Bootで再構築する
- Spring Bootについては、特定のコンテキストなため、ここでは取り扱わない
- 細かい言語レベルの差異は比較対象外
- 数十人規模のチーム開発

## Java
### メリット
- 実績
    - ミッションクリティカルなシステムを含め、多くの開発実績がある
- 人
    - 学習コストを支払い済みの人が多いため、実質学習コストが低い
- 技術
    - Optionalだと実行時null safe可能、@Nullableとか使えばコンパイラレベルでnull safe可能だが…
- 将来性
    - Oracle次第な面が少し薄れてきているし、色んなベンダーが関わっているので危うさは低い
- 情報流通
    - 新しいもの古いものあるが、総じて得られる情報は圧倒的に多い
- 再構築文脈
    - 再構築時、同じ言語であれば再利用が可能

### デメリット
- 人
    - エンジニアの母数としてJava人材は非常に多いが、玉石混交になりがち
- 生産性
    - 慣れたKotlinエンジニアに敵わない？
- 再構築文脈
再構築時、同じ言語であるが故に単純なコピペも可能、せっかくの再構築なのに台無しなコードが混じる可能性あり

## Kotlin
### メリット
- 人
	- 採用時、Kotlinができる = Java人材と比較して高レベルの人材である確率が上がる
- 技術
	- 構文レベル＋コンパイラレベルでnull safe、他にもJavaで陥りがちな落とし穴を構文レベルで防止している感
- 生産性
	- 慣れるまでは上がりづらいが、慣れればJavaと比較して高い？
- 再構築文脈
    - 再構築時、異なる言語であるため、単純な流用ができず、変換やリファクタリングをするため、台無しなコードが混じる可能性が低い

### デメリット
- 実績
    - Androidはさておき、サーバサイドの言語として大規模だったり複雑性が高かったりする実績が少ない
- 人
	- 学習コストを支払い済みなエンジニアが少ないため、学習コスト高め
- 将来性
    - JetBrains主体でOSSとして開発し、GoogleがAndroid標準としているが、JVMありきなため、Oracleの去就がリスクになりうる？
- 情報流通
    - Javaと比較すると、圧倒的に得られる情報が少ない
- 再構築文脈
    - 再構築時、異なる言語であるが故に変換やリファクタリングが必要

## 参考
- [サーバサイドJava開発者のためのKotlin入門](https://www.infoq.com/jp/articles/intro-kotlin-java-developers/)
firewall-cmd のよく使うオプションまとめ

CentOS 7 で確認。firewall-cmd のバージョンは 0.5.3。

ポートを開けるときは、サービスにポートを割り当ててから、サービスをゾーンに追加すると、管理しやすい。`--add-port` でポートを開けるのはやめたほうがいいと思う。

### 定義済みサービスのリストを表示
```
firewall-cmd --permanent --get-services
```

### 新規サービスを作成
```
firewall-cmd --permanent --new-service=<サービス名>
```

### サービスに登録されているポートのリストを表示
```
firewall-cmd --permanent --service=<サービス名> --get-ports
```

### サービスにポートを追加
```
firewall-cmd --permanent --service=<サービス名> --add-port=<ポート番号>/<プロトコル>
```

ポート番号は `50001-50009` のように範囲指定もできる。

### サービスからポートを削除
```
firewall-cmd --permanent --service=<サービス名> --remove-port=<ポート番号>/<プロトコル>
```

ポート番号は `50001-50009` のように範囲指定もできる。

### デフォルトゾーンに登録されているサービスのリストを表示
```
firewall-cmd --permanent --list-services
```

### デフォルトゾーンにサービスを追加
```
firewall-cmd --permanent --add-service=<サービス名>
```

### デフォルトゾーンからサービスを削除
```
firewall-cmd --permanent --remove-service=<サービス名>
```

### 変更した設定を反映
```
firewall-cmd --reload
```
Project Euler 009を解いてみる。「特別なピタゴラス数」

# Project Euler 009
Project Eulerを知る切っ掛けになった問題です。
[009](http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%209)
>ピタゴラス数(ピタゴラスの定理を満たす自然数)とは a < b < c で以下の式を満たす数の組である.

```math
a^2 + b^2 = c^2
```
>例えば, 

```math
3^2 + 4^2 = 9 + 16 = 25 = 5^2
```
>である.
a + b + c = 1000 となるピタゴラスの三つ組が一つだけ存在する.
これらの積 abc を計算しなさい.


->[次の問題](https://qiita.com/radiol/items/fc36541d744751de4ee5)

## 考え方
aとbは入れ替え可能なのでa<bとする。

```math
a^2 + b^2 = c^2\\
```
より `a<b<c`となる。
よって

```math
a + b + c = 1000 \\
\rightarrow 3a < 1000 \\
\rightarrow a < \frac{1000}{3}
```
b<c, c = 1000-a-bより

```math
b < 1000 - a - b\\
\rightarrow 2b < 1000 - a \\
\rightarrow b < \frac{1000-a}{2}
```
となり、a,bが決まればcは定まるので、
a < 1000/3, b < (1000-a)/2の範囲を探索すれば良さそうです。

## コード
```Python:euler009.py
def main():
    n = 1000

    for a in range(1, (n // 3)):  # 切り捨て
        for b in range((a + 1), -(-(n - a) // 2)):  # 切り上げ
            c = n - (a + b)
            if c ** 2 == a ** 2 + b ** 2:
                print(f"a={a}, b={b}, c={c}, abc={a * b * c}")
                break


if __name__ == '__main__':
    main()
```
`結果
a=200, b=375, c=425, abc=31875000
main処理時間: 0.20030498504638672 sec.
`

余談ですが、nによっては解が複数あるようです。
10000以下で検索するとn=9240の時組み合わせが最大で、20通りのa,b,cの組み合わせがあります。

###追記
コメントでご指摘いただきました。上記のコードだと内部のループをbreakで抜けたあと、外側のforループが再開してしまい無駄が生じます。
以下のように修正しました。

```Python
def main():
    n = 1000

    for a in range(1, (n // 3)):  # 切り捨て
        for b in range((a + 1), -(-(n - a) // 2)):  # 切り上げ
            c = n - (a + b)
            if c ** 2 == a ** 2 + b ** 2:
                print(f"a={a}, b={b}, c={c}, abc={a * b * c}")
                break
        else:
            continue
        break
```
for~elseの組み合わせではforループが全て終了した場合のみelse内の処理が実行されます。
これを利用して、forループが完了する=breakしなかった場合、else内のcontinueで外側のループを継続します。
forループが完了しない=breakした場合、else内の処理をスキップしその下のbreakが実行され外側のループも抜けます。
VSTOブックをClickOnceで配布した時の知見をまとめる

# はじめに
VSTOブックは止めておいた方がいい。
どうしてもVSTOブックを作らなけらばならない人に本記事が参考になれば幸いです。
※VSTO全てを否定しているわけではありません。Excelブックと紐づくVSTOアドオンに対する知見になります。

# 経緯
仕事でExcelマクロを組む必要があり、それを社内で公開する必要があった。
どうせならバージョン管理もできるし、.NET Frameworkで開発できるVSTOにしようと思い、VSTOブックでの開発を行ったが、思った以上に苦戦（むずがゆい思いを）したので、その理由などを残しておく。

# レイアウトについて
ブックに直接レイアウトするのはオススメしない。
面倒でもアドオンで書式などの設定をした方がいい。
ただし今後レイアウトを変更する予定がある場合の話です。
改修の予定がないVTSOブックであれば、ブックに直接レイアウトした方が早いです。

## レイアウトをアドオンで設定した方がいい理由
Excelブックは__アドオンのバージョンまで保持していない__ようだから。
と言うもの仕様変更でExcelシートに入力欄を1列追加し、ClickOnceで公開した。
公開後にExcelブックを開くと、ClickOnceによってアドオンはバージョンアップされたのだが、__Excelブックは古いまま（入力欄が足されていない状態）で利用できてしまった__。
幸いにも今回のケースでは、追加した入力欄は最新バージョンでは必須だった為に、古いExcelブックは利用できなくなったので、問題なかったが、場合によっては入力チェックなどもスルーされて異常なデータが登録されてしまう事も考えられる。

なのでExcelブックを差し替えてもらうよう、ユーザにアナウンスする必要があった。
また古いExcelブックを使われないようにする対策も考慮する必要があった。

# ClickOnceによる配布について
最新の__Excelファイルを配布先からダウンロードしないといけない__ので、少し微妙。
[ここ](https://docs.microsoft.com/ja-jp/visualstudio/vsto/deploying-an-office-solution-by-using-clickonce?view=vs-2019#Put)には、VSTOをインストールした後にExcelブックを所定のパスにダウンロードする仕組みが解説されているが、__manifestファイルを直接編集したり、それを再署名したりする必要がある__ので、やろうと思えば出来ますよな感じがする。

# 開発環境と本番環境の共存について
プロジェクトファイルの下記項目を切り替えて、公開すれば共存可能。（項目に関する説明は後述）

* SolutionID
* ManifestKeyFile
* ManifestCertificateThumbprint

## 共存とはどういう事か
私は開発者兼利用者だったので、ClickOnceで公開したVSTOブックをインストールした。
しばらくして仕様変更する必要があったので、Visual Studioでデバッグ実行しようとしたところ、「インストール済みです。」と言ったエラーが出て、デバッグ実行できなかった。
インストールしていたVSTOをアンインストールしたところ、デバッグ実行が出来るようになったが、また最新版が公開された場合には、再度インストールする必要がある。

## べき論で言うと
開発環境で本番、検証環境に繋げるべきではない。
仮想環境なり、アカウント切り替えるなりして本番環境に接続するのが安全。（今回の場合は開発環境を仮想化するのが無難）

## 項目に対する補足
### SolutionID
Excelブックに埋め込まれているIDでどのVSTOアドオンを利用するか保持しているらしい。
この値さえ異なれば、アセンブリ情報は同じでも問題なかった。

### ManifestKeyFile
コード証明書。
これを変更しておかないと、インストール時に「既にインストール済みです。」とエラーになった。

### ManifestCertificateThumbprint
コード証明書のサムプリント。
ManifestKeyFileで指定したコード証明書のサムプリントと一致していないと、エラーになる。

## 設定切り替えが面倒なら
本番用のプロジェクトを用意した方がいいかも。
イメージとしてはこんな感じ

┣　公開用プロジェクト　...　開発用プロジェクトを参照。ClickOnceでの配布はこのプロジェクトで。
┗　開発用プロジェクト　...　デバッグ実行する時はこのプロジェクトで。

リボンが必要ならベースプロジェクトから参照して利用すればいい。
Excelブックが継承できないので、そこで冒頭で説明したレイアウトはアドオンで設定する事が生きてくると思う。

# 最後に
当初の目的は

* バージョン管理がしやすい
* .NET Frameworkが使える

だったので、目的は達成されたのたが、それ以外が少し残念と言うのが素直な感想だった。
ただ振り返ってみると、Excelブックに拘ったのがそもそもの間違いだったのかと思う。
今後、仕様変更も入る予定だったので、バージョン管理がしやすいVSTOブックを採用したが、それならWindows FormやWeb アプリなどで開発した方が公開とかはしやすかったと思う。
また作ったきりで改修される予定のないものであれば、おとなしくVBAで開発した方がインストールの必要もないので、使いやすいと思う。
Ubuntu18.04で突然解像度がおかしくなったときの対処法

# はじめに
私のPCはubuntu18.04とWindows10でデュアルブートしており、もともとのPCの解像度は1920×1080です。  

```
sudo apt-get update
sudo apt-get upgrade
```

↑のコマンドのようにパッケージの更新をかけ、再起動したら解像度が640×480で以下のように固定になっていました。  
他のカーネルをすべて試しましたが、同様の現象が起きていました。  
その問題に対する対象法について説明していきます。  

![DSC_0800.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289151/245c0754-5523-ebbb-7901-582c9fb2a76c.jpeg)

ご意見・ご要望などある方はぜひコメントよろしくお願いします。

# 現状の確認

まずは、現状の解像度の状態を確認しましょう。以下のコマンドを実行します。  

```
sudo apt-get install inxi
inxi -G
```

すると、以下の画像のResolutionの部分に解像度が書いてあると思います。  


![範囲を選択_029.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289151/c80de830-585e-70e4-9872-77dcd70714da.png)  

また、解像度をあやつるコマンドである"xrandr"も用いてみます。  

```
xrandr
```

以下の画像のように、Screen 0が800×600の解像度としか認識されていないことがわかります。

![範囲を選択_030.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289151/d75dafa5-0789-07b2-d958-3b208983ae87.png)  

この現象に対する対処法として、以下のようなものが挙げられます。  
（他にあれば教えてください）  

* xrandrに手動で新しい解像度を追加する
* grubのファイルを直接いじって解像度を変更する

結論から言うと、xrandrで解像度を追加する方はうまく行かず、grubのファイルを直接いじることで解決しました。  

# xrandrに手動で新しい解像度を追加する（失敗）

まずは、xrandrに手動で新しい解像度を追加してみます。  
始めに行っておくと、これは失敗しました。  
てっとりばやく問題を解決したい人は次の項を見てみたほうがいいかもしれません。  

実行コマンドは、以下のようになります。  
（戻したい解像度に応じて数字は変更してください）  
最初のコマンドで出てきた文字列の一部を次のコマンドの"1920×1080_60.00"の部分に適宜変更してください。  
（Screen 0の部分についてもxrandrで出てきたスクリーン名に変更する。）  

```
cvt 1920 1080
sudo xrandr --addmode Screen 0 "1920x1080_60.00"
```

サイズが取得できないらしい。  
原因を調べると、grubをいじれば解決するらしい（次の項参照）  


![範囲を選択_032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289151/8216b429-1c9c-3828-8516-8595036166a7.png)

# grubのファイルを直接いじって解像度を変更する

最終手段（？）として、grubのファイルをいじって解像度の変更を試みました。  
vimコマンドを実行し、grubのファイルを編集します。


```
sudo vim /etc/default/grub
```

![範囲を選択_034.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289151/f41092c9-23c2-c474-2be8-ca4b0357a19d.png)

そして、ファイル内に以下の文を追加します。  
（追加の仕方は、vimコマンドでググればでてきます。）  

```
GRUB_GFXMODE=1920x1080
```

![範囲を選択_033.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289151/3ba38a90-e9c0-07af-1fc6-5988f8bc4a96.png)

最後に、以下のコマンドでgrubファイルを更新し、再起動をかけることで解像度がもとの解像度に戻るはずです！！！

```
sudo update-grub
```

![範囲を選択_035.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/289151/f16d8699-cf6f-f064-7f04-8f4ce3fcec56.png)

# まとめ
この記事では、突然Ubuntuの解像度がおかしくなったときの対処法について書いてきました！
他にも疑問があれば、是非コメント欄や[twitter](https://twitter.com/nsd244)などで話しかけてくれると嬉しいです！  

# 参考サイト
* [Failed to get size of gamma for output default when trying to add new screen resolution](https://askubuntu.com/questions/441040/failed-to-get-size-of-gamma-for-output-default-when-trying-to-add-new-screen-res)  
* [Ubuntu日本語フォーラム / モニタの解像度を変更する](https://forums.ubuntulinux.jp/viewtopic.php?id=11178)  
* [ディスプレイの設定をコマンドライン・シェルで行う](https://qiita.com/hiro_mayo/items/db8ddcea600e8bfbdd5f)  
* [Ubuntu 18.04 で解像度が見つからなくなったときの対処](https://blog.capilano-fw.com/?p=1881)
MYSQLの初期パスワードの調べ方と変更方法の忘備録

AWSの勉強中、
DBサーバーにMYSQLをインストールしたまではよかったもののログインができず困ったため、忘備録。

MYSQLのバージョン:5.7.27

# MySQLにアクセスできない
インストールが完了し、DBサーバーを実行したまではよかったもののパスワードを作成しようとするとエラー。

```
$ mysqladmin -u root password
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
```

## パスワードを確認する
初期起動時にパスワードを自動生成される。その起動時のlog（mysqld.log）にパスワードが残っている。

```
$ sudo cat /var/log/mysqld.log | grep 'password is generate'
2019-09-03T03:49:01.756755Z 1 [Note] A temporary password is generated for △△△@localhost: 〇〇〇
# ユーザー名		：△△△
# パスワード		：〇〇〇
```

## パスワードを変更する
初期設定だと何かと不便なので、変更する。
変更のためには、
　1. 仮のパスワードを作成
　2. パスワード設定を変更
　3. パスワードを再度設定する
という流れとなる。
初期設定のパスワードだと、パスワード設定の確認の際に、変更を求めらるため仮パスワードを設定する。

確認したパスワードでSQLにログインする。
次に適当な長さと強度を持ったパスワードを作る。（8文字以上、小文字、大文字、特殊文字、数字）

```
# 適当に付けたパスワード：MYSQL@password1
mysql> set password for root@localhost=password('MYSQL@password1');
Query OK, 0 rows affected, 1 warning (0.00 sec)
```
**パスワードを変更したいだけなら、ここまででOK。**
パスワード設定を確認する。

```
mysql> SHOW VARIABLES LIKE 'validate_password%';
+--------------------------------------+-------+
| Variable_name                        | Value |
+--------------------------------------+-------+
| validate_password_check_user_name    | OFF   |
| validate_password_dictionary_file    |       |
| validate_password_length             | 8     |
| validate_password_mixed_case_count   | 1     |
| validate_password_number_count       | 1     |
| validate_password_policy             | MEDIUM|
| validate_password_special_char_count | 1     |
+--------------------------------------+-------+
```
長さとポリシーを変更して、再度変更を確認。

```
mysql> SET GLOBAL validate_password_length=4;
mysql> SET GLOBAL validate_password_policy=LOW;
mysql> SHOW VARIABLES LIKE 'validate_password%';
+--------------------------------------+-------+
| Variable_name                        | Value |
+--------------------------------------+-------+
| validate_password_dictionary_file    |       |
| validate_password_length             | 4     |　←
| validate_password_mixed_case_count   | 1     |
| validate_password_number_count       | 1     |
| validate_password_policy             | LOW   |　←
| validate_password_special_char_count | 1     |
+--------------------------------------+-------+
mysql> set password for root@localhost=password('root');
```
4文字以下のパスワードが登録できるようになった。
ちなみにポリシーは
>LOW 	：長さ
>MEDIUM	：長さ。数値、小文字、大文字、および特殊文字
>STRONG	：長さ。数値、小文字、大文字、および特殊文字。辞書ファイル

## 参考ページ
>https://dev.mysql.com/doc/refman/5.6/ja/validate-password.html
iosシミュレータの動作が遅いときの対処法(ReactNative)

# 対処法

`Debug -> Slow Animations`をoffにする。
`command + T`ショートカットを無意識に押してしまったのが原因。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/291139/804d15e9-9cbb-a6f4-1ef3-c1e37d444eb6.png)

# 参考
[iOSシミュレータの動作が遅いとき](https://hacknote.jp/archives/27343/)
firebase functionsでlocal packageを使う

firebase functionsで、local packageを使っているときに、deployしてもそのパッケージが無いよって言われちゃってどうしたもんだろう。と小1時間ググッた。
dependenciesに`"mypackage": "../mypackage"`とかしても見に行ってくれないのです。

現状、このWorkaroundが使える。
https://github.com/firebase/firebase-tools/issues/968#issuecomment-460323113

`preinstall`で該当packageをpackして、dependenciesにその生成物を指定する。

```package.json
  "scripts": {
    "preinstall": "if [ -d ../mypackage ]; then npm pack ../mypackage; fi",
    ...
  },
  ...
  "dependencies": {
    "mypackage": "file:./mypackage-1.0.0.tgz",
    ...
  }
```


#### memo
* https://stackoverflow.com/questions/54458795/firebase-functions-local-file-dependencies
* https://github.com/firebase/firebase-tools/issues/968#issuecomment-460323113
【Django】TypeError: Object of type QuerySet is not JSON serializable

## はじめに
以下のようにBlogモデルへの検索結果をそのままJsonResponseで返却しようとしたところ、表題のようなエラーが出た。エラー内容は「検索結果がQuerySetだとJSON形式に変換できないよ」というもの。
このエラーへの対処方法を備忘録として残しておく。

``` python:views.py
blog_list = Blog.objects.filter(user=request.user)
ret = { "blog_list": blog_list }
return JsonResponse(ret)
```

## 対処方法
serializersを用いてjson型に変換する。

``` python:views.py
from django.core import serializers
...
blog_list = serializers.serialize("json", Blog.objects.filter(user=request.user))
ret = { "blog_list": blog_list }
return JsonResponse(ret)
```

## おわりに
以下を参考。
https://stackoverflow.com/questions/41992889/queryset-is-not-json-serializable-django/41992944
AWSの基礎用語（たぶん）をひたすらメモしていくページ

タイトルのとおり．勉強したことをひたすら書きなぐるページ．間違っていても知りません．

## 基礎用語一覧
### リージョン
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-regions-availability-zones.html

EC2の物理的なロケーションを指す．リージョン同士は分離設計されているため，あるリージョンの障害が別のリージョンに伝播することは基本ない．一方，リージョン間の通信には，料金が発生する．東京リージョンのコードは「ap-northeast-1」である．

### AZ
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-regions-availability-zones.html

availability-zone（アベイラビリティゾーン）の略．1つのリージョンは複数のAZから構成される．ap-northeast-1のAZは現時点で3種類（1a, 1c, 1d）である．

### エンドポイント
AWSの各種リソースに外部からアクセスするときに用いる名称．

### Elastic IP
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html

AWSアカウントに紐づけられたpublicな静的IPv4アドレス．Amazonがプールしているアドレス，もしくは独自に持ち込んだIPアドレスを適用することができる．

### Amazon CLI
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-welcome.html
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-configure.html

コマンドライン上でAWS操作を可能とするツール．pipがインストールされている前提で，コマンドラインで`pip3 install awscli --upgrade --user`を叩けばインストールできる．`aws --version`でバージョン情報が出たらOK．期待どおりの挙動を示さない場合は，Pathが通っていない（もしくはpip3がインストールされていない）ことを疑う．初期設定は`aws configure`が楽．

### IAM
https://aws.amazon.com/jp/iam/
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/getting-started_create-admin-group.html

Identify and Access Managementの略．その名のとおり，認証や権限を管理する機能．大きく「グループ」「ユーザー」「ロール」「ポリシー」の4要素から成り，「グループ」は「ユーザー」を束ねる概念である．

### ポリシー
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies.html

IAMにおけるルールセットのようなもの．1つ以上のStatementを持ち，各Statementには「リソースに対するAction（例えばパスワード変更）のEffect（Allow/Deny）」の構文の記述が含まれる．

### ロール
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_terms-and-concepts.html

ちょっと理解が追いついていないので，理解出来たら追記する．

### アクセスキー
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_access-keys.html

IDとシークレットキーのペアから成る認証情報．両方とも長いランダム文字列であり，またシークレットキーの参照は1度しか出来ない．1つのIAMユーザに対して複数のアクセスキーを設定できる．

### Amazon VPC
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/what-is-amazon-vpc.html

Virtual Private Cloudの略．AWSの各リソースを，仮想のネットワークでつなぎ合わせるサービス，およびそれで作られた仮想ネットワークそのものを指す．デフォルトだと「172.31.0.0/16」上に「172.31.0.0/20」「172.31.16.0/20」「172.31.20.0/20」の3つのサブネットを含有するVPCが生成される．

### インターネットゲートウェイ
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/VPC_Internet_Gateway.html

VPCとインターネットの接続点．VPCにインターネットゲートウェイをアタッチし，デフォルトルート（0.0.0.0/0）にインターネットゲートウェイをアタッチすることで，そのVPC内のリソースは外部にアクセス可能となる．

### ARN
https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws-arns-and-namespaces.html

Amazon Resource Nameの略．リソースを一意に特定する文字列で，`arn:partition:service:region:account-id:resource`のような構文で記述される．IAMで制御対象を指定するときに用いる．

### CIDR
https://tools.ietf.org/html/rfc4632

Classless Inter-domain Routingの略．AWS用語ではないが忘れがちなので書いておく．サブネットの定義ルールのようなもの．「172.16.0.0/12」の場合，「172.16.0.0」から「172.31.255.255」までのIPアドレス空間を指すようになる．
enumセレクトボックスの日本語化とエラー `is not a valid`

## 3行で

- 日本語の表示はできるがテーブルにデータが渡らない
- 数値で出なきゃいけないものが文字列で出てしまっている
- ビューの記述に問題があった

## 日本語化

ひとしきりenumを設定し終えて、とりあえず英語の状態で問題なくデータベースに値を渡すことができたら日本語化します。
手順は以下

- gemの『**enum_help**』を入れて `bundle install`
- **config/locals/ja.yml** に日本語を設定
- **config/application.rb** に↑を読み込む旨を書く

```yml:ja.yml
ja:
  person:
     judgement:
      guilty: 有罪
      not_guilty: 無罪
```

```ruby:application.rb
class Application < Rails::Application   
  config.i18n.default_locale = :ja
  config.load_defaults 5.2
end
```

設定はこれで完了

## ビューに反映させる

日本語で表示するだけなら↓でいいんですけど、これだとsubmitを押したときにデータがテーブルに渡りませんでした。

```haml
= f.select :judgement, person.judgements_i18n.invert.keys.to_a
```

**「数値で保存されなければいけないところが文字列で保存されようとしています」**といった旨のエラーが出ていました（ただ、カラムのデータ型が文字列型になっていてもエラーになったので、正直ここはよく理解ができていません）。

【参考】
[enumを使うとArgumentError in TasksController#create => '2' is not a valid status_id](https://qiita.com/e-yoshida/items/fa4d300786fbedf9845d)

下記の記述で解決しました。

```haml
= f.select :judgement, Person.judgements.keys.map { |k| [I18n.t("enums.person.judgement.#{k}"), k]}
```

【参考】
[i18n、enumを利用して、formforのf.selectを日本語化する](https://qiita.com/e-yoshida/items/fa4d300786fbedf9845d)
[Python3] 画像サイズ一括変更

ざっくりと。

# Python3, OpenCVで画像サイズを一括変更する方法
今回は、画像の幅が2000pxより大きいときに2000px以下になるまで半分にしていきます。
縦横比は変わりません。

○○.jpgが一杯入っているフォルダに、以下のpyファイルをおき、実行します。

```python:resize_images.py
import cv2
import glob

files = glob.glob("*.jpg")
for i, filename in enumerate(files):
    img = cv2.imread(filename)
    height = img.shape[0]
    width = img.shape[1]
    while width > 2000:
        width = int(width*0.5)
        height = int(height*0.5)
    img2 = cv2.resize(img, (width, height))
    cv2.imwrite(filename, img2)

print("resized.")
```

半分にするなどの処理を変更したい場合は、

```python

    while width > 2000:
        width = int(width*0.5)
        height = int(height*0.5)
```
ここを好きなように書き換えてください。

ファイル名に全角文字が入っているとうまく読み込めないようです。注意してください。