OmniAICreator/Qiita-1.07M huggingface dataset:
LaravelでAWS Elastic Cashe Redisを使ってみた

LaravelのSchedulerにてonOneServerを使用したかったので、共通のキャッシュサーバーとしてAWSのElastic Cache Redisを使った時のメモ。

先にキャッシュサーバー用のセキュリティーグループを作成しておく。
![スクリーンショット 2020-10-10 2.40.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209804/73aa6a57-85ad-33a8-a7dc-155f3cb23b85.png)
port6379を開け、ソースにプライベートサブネットにおいたEC2のセキュリティグループを選択し保存
![スクリーンショット 2020-10-10 2.40.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209804/46aeed52-bb71-db1b-ffc7-6ba09b68c558.png)
ElasticCacheのダッシュボードから作成をクリック
![スクリーンショット 2020-10-10 2.37.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209804/54ecddc3-f886-ea1e-2028-5ac4791b8105.png)
redisを選択。ロケーションはクラウド
![スクリーンショット 2020-10-10 2.38.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209804/7465c9da-4239-bbf1-ae81-a939be4e11f3.png)
Redisの設定、エンジンバージョンの互換性とパラメータグループはデフォの設定
ノードのタイプは用途に合わせてサイズ選択。今回は極小。
![スクリーンショット 2020-10-10 2.38.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209804/35d9f940-fd1d-a5d2-6859-5d716ed8f22d.png)
配置するVPCサブネットを選択しサブネットグループを作成
![スクリーンショット 2020-10-10 2.39.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209804/ce7d698d-08fb-def4-4b30-2d842545442c.png)
以下はとりあえずデフォ
![スクリーンショット 2020-10-10 2.39.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209804/d6cbe423-9c83-a90d-bbbf-29e4c4894b90.png)
![スクリーンショット 2020-10-10 2.39.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209804/d00e197a-ff09-ce8e-827e-76e1d3b384a9.png)

# 接続テスト
対象のEC２インスタンスに入り、ping

```terminal:EC2
ping your-elastic-cache-host
```
#環境変数設定
```:.env
REDIS_HOST=your-elastic-chache-host
REDIS_PASSWORD=null // 今回はAUTH設定していないため
REDIS_PORT=6379
```
#laravelから接続テスト
適当なコマンドを作成して、操作可能かチェック

```terminal:EC2
cd to/your/dir
php artisan make:command RedisConnectionSample
```

```php:RedisConnectionSample.php
namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
class RedisConnectionSample extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'connect:redis';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        Redis::set('key', 'value');
        $data = Redis::get('key');
        var_dump($data);
    }
}
```

```terminal:EC2
php artisan connect:redis
```

うまくいかない時には、phpredisかpredisが入っていないケースがあるので、その場合には入れる。amazon linux 2の場合にはextrasから入手可能。
pipenv で sympy を使って整式の次数と定数項を得る方法

```
pipenv --python 3.8
pipenv install sympy
```

```python
vim hoge.py

from sympy import *
from sympy.abc import x,y

expr = '-2*x + 3*y + x**2 + 5*x -y'

print("元の式")
print(expr)
print("")
expr = sympify(expr)

print("同類項にまとめる")
print(expand(expr))
print("")

print("x の次数")
print(degree(expr,x))
print("")

print("x の定数項")
print(expr.coeff(x,0))
print("")
```

```zsh
pipenv run python hoge.py
```

```
元の式
-2*x + 3*y + x**2 + 5*x -y

同類項にまとめる
x**2 + 3*x + 2*y

x の次数
2

x の定数項
2*y
```
firebaseのCrashlyticsの移行

「2020 年 11 月 15 日以降も引き続き Firebase コンソールでクラッシュ レポートを取得するには、一般提供版の Firebase Crashlytics SDK にアップグレードしてください。 」

と言う割に、gradleの設定に手こずったのでメモ。

公式の導入手順はiOS版の設定しか見れない...。なぜか、Androidの方を見ようとしてもiOSの手順が表示されてしまう。ブラウザの問題かなー。
2020/10時点では、バージョンもシビア？（多分レポジトリ 設定が足りてないせい)で、crashlytics:17.2.2が見つからなく、バージョンをあげるとビルドできなかった....。


### プロジェクトのbuild.gradle

```gradle
dependencies {
    classpath 'com.google.gms:google-services:4.3.3'
    classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
}
```

### アプリケーションのbuild.gradle

```gradle
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'


android {
    productFlavors {
        production {
            manifestPlaceholders = [crashlyticsEnabled: true]
        }
        development {
            manifestPlaceholders = [crashlyticsEnabled: false]
        }
    }

    buildTypes {
        release {
            manifestPlaceholders = [crashlyticsEnabled: true]
        }

        debug {
            manifestPlaceholders = [crashlyticsEnabled: false]
        }
    }
}

dependencies {
    // firebase
    implementation 'com.google.firebase:firebase-analytics:17.4.4'
    implementation 'com.google.firebase:firebase-messaging:20.2.3'
    implementation 'com.google.firebase:firebase-crashlytics:17.1.1'
}
```


### AndroidManifest.xml

```xml

    <meta-data
        android:name="firebase_crashlytics_collection_enabled"
        android:value="${crashlyticsEnabled}"
        />
```

変数の優先度は、buildTypes < productFlavors の順なので、productionのreleaseビルドの時だけ、有効になってあとは無効化される。
Amazon Linux 2 にphp-pecl-redis入れる

LaravelにてRedisを使う必要があり、phpredisかpredisいれてねとのことだったのでその時の備忘録

Laravel6.xのドキュメントにあるように、predisはもうLaravelに含めないようにしますとのことだったので、phpredisを入れる方向で検討

remiを使えとの記事が多かったがよくよく見ればamazon-linux-extrasにすでにあるようなのでそちらから入れた。

```terminal:AmazonLinux2
sudo yum install php-pecl-redis 
sudo systemctl restart php-fpm
```
うまくいかない場合にはお使いのPHPのバージョンのrepoが有効化されているか確認して下さい。
【JMH】JMH Gradle PluginはWindows 10で正常に動作しない【Gradle】

タイトルの通り、`JMH Gradle Plugin`（ここでは`me.champeau.gradle.jmh`）は、少なくとも`0.5.2`時点では`Windows 10`で正常に動作しません。
1度ベンチマークの実行を始めると、中断してもプロセスが死なず、それのせいで実行ファイルがロックされて再ビルドが通らないなどの異常な動作が生じます。

自分の環境では、`gradle jmh --no-daemon`として、`--no-daemon`フラグ付きで実行すればこの症状は抑制できました。
その他詳しい情報に関しては下記を参照して下さい（対応策の出展もこちらです）。

- [Not exiting JVM and keeping a lock on file after benchmarking is over, Windows 10 · Issue \#134 · melix/jmh\-gradle\-plugin](https://github.com/melix/jmh-gradle-plugin/issues/134)
- [jnellis/jmh\-gradle: A drop\-in replacement workaround for the current issues with jmh\-gradle\-plugin on windows\.](https://github.com/jnellis/jmh-gradle)
楽天のラッキーくじをちょっと使いやすくしてみた2

## 楽天のラッキーくじをちょっと使いやすくしてみた その2

前回の記事
[楽天のラッキーくじをちょっと使いやすくしてみた](https://qiita.com/tasuku_sinsora/items/050f087effab5d2d0bd0)

前回は[トップページ](https://kuji.rakuten.co.jp/)のPRやSPを削除してPCだけを表示するという記事を書きました。

今回は開いた先のくじをクリックするプログラムを書きました。
**時々失敗するのでまだ検証中**

## 作ってみよう

### 環境
macOS Catalina ver 10.15.7
GoogleChrome 86

### Chromeの拡張機能
**拡張機能は省略**

## プログラムについて
### プログラムの内容
```JavaScript
function foo(){
    let image = document.getElementById("entry");
    console.log("events");
    if(image.getAttribute("src").indexOf("entry.gif")){
        console.log("entry!")
        image.click();
    }
}
let image = document.getElementById("entry");
image.setAttribute("onload","foo()");
```

### 簡単な説明
関数`foo()`の中身

`let image = document.getElementById("entry");`
画像を取得して
`console.log("events");`
consoleに書き込む

`if(image.getAttribute("src").indexOf("entry.gif"))`
画像のsrcにentry.gifが含まれていたら
`console.log("entry!")`
consoleに書き込んで
`image.click();`
画像をクリックする

これだけじゃ画像が読み込み中とかはエラーになっちゃうので、
ページ作成時に
`image.setAttribute("onload","foo()");`
画像にonload時にfoo関数を呼び出すように書き込む。

これで画像が読み込まれた時に、
画像のsrcにentry.gifが含まれていたら、
画像をクリックしてくれるプログラムの完成

### 完成はしたんだけど...

時々失敗するので確実にするためには何か考える必要がある
sleepだと他の処理も止まる可能性があるので、
非同期関数とかよくわかってないけどお勉強してもう少し楽にできる方法を模索しましょうねという感じ...
[Golang] 当番を乱数で決定するプログラム

## はじめに
名簿一覧から**x人組**作って欲しいという要望があったので折角だしgo言語で書いてみました。



```go
package main

import (
	"fmt"
	"math/rand"
	"time"

)

func main() {
	list := [...]string{"mano", "hiori", "meguru",
		"kogane", "mmm", "mitsumine", "sakuya", "kiriko",
		"kaho", "choko", "jyuri", "rinze", "natsuha",
		"amana", "tenka", "chiyuki",
		"asahi", "huyu", "mei",
		"toru", "higuchi", "pya", "hinana"}

	// 何等分するか
	parse := 5
	// 出現した乱数を保持するスライス
	var slice []int
	rand.Seed(time.Now().UnixNano())

	for i := 1; i < len(list)+1; i++ {

		var r int
		// 乱数生成
		for {
			r = rand.Intn(len(list))
			if !contains(slice, r) {
				slice = append(slice, r)
				break
			}
		}

		fmt.Printf("%v ", list[r])

		if i%parse == 0 {
			fmt.Println("")
		}
	}

}

func contains(s []int, e int) bool {
	for _, v := range s {
		if e == v {
			return true
		}
	}
	return false
}

```

#### 出力結果 例
```
huyu amana kaho asahi toru
kogane kiriko mano rinze meguru
hiori mitsumine sakuya higuchi jyuri
natsuha tenka chiyuki pya choko
mei mmm hinana
```


https://gawawa124.hatenablog.com/entry/2015/04/08/193237 

https://takeshiyako.blogspot.com/2015/10/go-lang-rand.html
絵文字を日本語に変換する gem

## Google は絵文字で検索できる……！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97243/5f8546b6-3de4-0ec2-d908-218090d4da9b.png)


## 絵文字を日本語に訳してくれるライブラリ

日本語 -> 絵文字 は事例がありました。
[日本語で絵文字入力するための IME 追加辞書を公開しました](https://qiita.com/peaceiris/items/c40ba39679daeb7555c2) 

ですがその逆が見当たらなかったので作ってみました。
https://github.com/d-mato/emoja

```ruby
Emoja.translate("🍎食べたい")
# => "赤リンゴ食べたい"
```

翻訳結果を MeCab に投げれば形態素解析をしてテキストマイニングもできるし、 ElasticSearch に投げれば全文検索もできそうです。

## 日本語 -> 絵文字 にも一応対応

用途が思いつかないけど、日本語から絵文字のサジェストも可能です。

```
Emoja.search("猫")
# => ["😺", "😸", "😹", "😻", "😼", "😽", "🙀", "😿", "😾", "🐱", "🐈"] 
```

## 絵文字辞書

こちらのリポジトリにある `emoji_ja.json` を活用させていただいております:bow: 
https://github.com/yagays/emoji-ja


---

みなさまの~~お遊び~~プロジェクトで使ってもらえたら幸いです！
Mac OSのコマンドラインでアプリを自在に開く方法とそのユースケース

バッチファイル .command にこのコマンドを記述するとお望みの動作が簡単に実現できます。
スクレイピングツールと一緒にバッチファイルに記述してよく使っているので激しくおすすめです。
#アプリを自在に開くコマンドの指定方法
-aの隣にアプリ名を入れます。その横にはさらに起動オプションもつけることが可能です！！

```
open -a "Google Chrome" https:/qiita.com
```

#何が便利なの？
例えば、私は以下のような文脈で最近使いました。

1.大学のポータルサイトから授業ページをスクレイピングするopu-scraping.jsを起動する
2.1.のプログラムにより受講している全ての授業ページのテキストがローカルに保存される
3.それぞれの授業ページをGitHubにPushする
4.ChromeでGitHubのリポジトリの変更履歴を開く

以上の流れを下のようにバッチファイル (.command)に記述しました


```opu-watchdog.command
#!/bin/bash
cd
cd dev/opu-lss-watchdog
node opu-scraping.js
git add .
git commit -m "automatically updated"
git push -u origin master
open -a "Google Chrome" https://github.com/kantasv/opu-lss-watchdog/tree/master/logs
```

これをMacのDockerにショートカットを置いておき、更新確認したいときにワンクリックで所望の動作が実現できるようになりました。
![スクリーンショット 2020-10-10 1.22.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/614389/bb9d6927-a035-e891-6804-5fd4a1f2599f.png)

##デモ動画
上のバッチファイルの実際のユースケースのデモです
コマンドから自由にアプリが開けることの応用性に気づいてもらえたら幸いです。
「府大の支援システムをスクレイピングし差分をGitHubで確認する」
https://www.youtube.com/watch?v=6aHnijjcSaQ&feature=youtu.be
Reactでvideoタグにmutedを効かせる方法(の代替)

Reactでvideoタグにmutedを添えられない問題について、
良い解決案を見かけたのでその紹介をします。

### 結論、mutedの指定は可能？
答えはNoです。
videoタグにmutedを添えることは、現状のreactでは実現できません。(2020/10/09現在)
(※保証されない、と表現されていますが、実際、mutedはvideoタグに反映されません。)

### どうやって解決するの？
[本家の該当issueにて、こんな書き込みがありました。](https://github.com/facebook/react/issues/10389#issuecomment-320493682)

`Mute is just equal to volume being zero`
(ミュートは、ボリュームゼロと同じ。)

え、たしかに。
なぜmutedにこだわる自分が居たのかが謎になるくらい目からウロコな意見で、
実際に `volume = 0` を試したらmuted相当の動きになりました :thumbsup: 

ちなみに、自分は使うあてが無かったですが、`onVolumeChange` なども扱えるようです。

### サンプルコード
↓こちら試してないですが、概念として。

```
const SomeVideoComponent = ({ muted }) => {
  const videoRef = useRef(null);

  ...
  useEffect(() => {
    if (videoRef.current !== null) {
      const mutedCurrently = videoRef.current.volume === 0;
      if (mutedCurrently !== muted) {
        videoRef.current.volume = muted ? 0 : 1;
      }
    }
  }, [videoRef, muted]);

  return <video ref={videoRef} />;
}

```

### そもそもmutedを使えない背景/調査補足
理由は、[こちらの本家facebook/reactのissue](https://github.com/facebook/react/issues/10389)にて色々議論されているので、ここでは割愛します。

そのため、[Qiita: React videoでmutedが消えるバグのパワー系解決](https://qiita.com/takujiro_0529/items/8ebee4e7daf009d9cc15) で紹介されているように、
[dangerouslySetInnerHTMLなど駆使してどうにか解決しようとするライブラリ](https://github.com/DylanVann/react-video-tag)もあるようです。
ただ、こういったライブラリも、スタイリングしたい場合に鬱陶しかったり、
そもそもこの些細なことのためにライブラリいる？っていう話もあると思います。


この記事もReactでMediaStream扱っている方にとって、何かの足しになればと思います。
単純な話なのですが、ちょっと呆気にとられたのでシェアでした。

それでは良き開発を〜
MySQLでrootユーザーのパスワードを設定時にERROR 1064 (42000)で悩まされまんた！

タイトル通りMySQLのrootユーザーのパスワードを変更しようとした所ちょっとハマりました。


```terminal:terminal
mysql> set password for root@localhost=password('hogehoge');
```

ググった所このコマンドでrootユーザーのパスワードを設定できるとのことでしたがこんなエラーが...！

```terminal:terminal
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'password('hogehoge')' at line 1
```

ぴえん。

syntaxエラーと目に飛び込んできたので、「どうせ''が変だったりそんなオチだろう」と思って色々弄りましたが直らず。

```terminal:terminal
mysql> update user set password=PASSWORD('hogehoge') where User='root';

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '('hogehoge') where user = 'root'' at line 1
```

こちらのコマンドも試しましたが、やはりERROR 1064(42000) syntax文法エラーがあるとのこと、、

##解決方法

色々ググってみた所、同じ症状の人がいました。
どうやらMySQLのバージョン5.6.7で上記のようなパスワード記載方法は廃止になったとのこと。
代わりに以下のコマンドを打ったところ、無事パスワードを設定できました。

```terminal:terminal
mysql> set password for 'root'@'localhost' = 'hogehoge';

Query OK, 0 rows affected (0.04 sec)
```

check the manual that corresponds to your MySQL server version
思いっきりエラー文にMySQLのバージョンマニュアルをチェックしてねって書いてました。。。

エラー文、英語ちゃんと読みますぴえんぱおんパオパオチャンネル

####参考
https://qiita.com/arm_band/items/12208908041a5506d7f4
Pythonでyoutubeを再生する

#はじめに
計算が終わったのがわかるようにbeep音を鳴らそうと思いましたが、Macだと難しかったです。
音声ファイルを入手するのも面倒だったので、youtubeを再生するようにしました。
Pythonだと標準モジュールでかなり簡単にできました。
CMが再生されるのがたまに傷。。。
#環境
mac OS Catalina 10.15.6
Python 3.5.5
#コード
```python:play_youtube.py
import webbrowser
webbrowser.open("再生したい動画のURL")
```

かなり簡単、2行でできちゃった。
ちなみに僕が使ってるのは[これ](https://youtu.be/NHKq8IOXPxA/)
jmeter > 動作がもっさり遅かった件


環境：Mac
実施日：2020/10/9

---


jmeterを導入したものの、動作がもっさりすぎてテストにならずに悩んでいました。
高確率でフリーズするくらいでした。
同マシンで、ParallesでWindows環境でテストしてみると快適に動作するので、ネットワークの問題でもなさそうです。

![画面収録 2020-10-09 23.14.40.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49910/7ce29948-174a-24a7-6c66-783807b5b131.gif "画面収録 2020-10-09 23.14.40.gif")


## 環境調査

### java_home


`/usr/libexec/java_home -V` コマンドで環境を調べてみました。

```
$ /usr/libexec/java_home -V
Matching Java Virtual Machines (3):
    11.0.3, x86_64:	"Amazon Corretto 11"	/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home
    1.8.0_265, x86_64:	"Amazon Corretto 8"	/Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home
    1.8.0_265, x86_64:	"AdoptOpenJDK 8"	/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home
```


### jmeter

```
WARNING: package sun.awt.X11 not in java.desktop
================================================================================
Don't use GUI mode for load testing !, only for Test creation and Test debugging.
For load testing, use CLI Mode (was NON GUI):
   jmeter -n -t [jmx file] -l [results file] -e -o [Path to web report folder]
& increase Java Heap to meet your test requirements:
   Modify current env variable HEAP="-Xms1g -Xmx1g -XX:MaxMetaspaceSize=256m" in the jmeter batch file
Check : https://jmeter.apache.org/usermanual/best-practices.html
================================================================================
```

jmeterはJAVA11でも動くようだが、推奨環境はJAVA8ということらしい
常駐ソフトを落としたり、設定をかなり見直してみるも改善せず。


## 他環境で検証

jenvを入れてJDKのバージョンを切り替えてテストしました。

[Mac > jEnvで複数バージョンのJDK（JAVA環境）を切り替える - Qiita](https://qiita.com/sugasaki/items/e10071cc791d23dd16c8)

### jenv

`jenv versions`で環境を調べてみました

```
$ jenv versions
  system
  1.8
  1.8.0.265
  11
  11.0
  11.0.3
  corretto64-1.8.0.265 (set by /Users/atsu/.jenv/version)
* corretto64-11.0.3
  openjdk64-1.8.0.265
```



JDKのバージョンを変更してテストしてみました。


## JAVA8

### corretto 8 (1.8.0)

```
jenv global corretto64-1.8.0.265
```

![画面収録 2020-10-09 23.40.30.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49910/24fa17cb-1db6-6b4e-9b0d-56d605469baa.gif "画面収録 2020-10-09 23.40.30.gif")

JDKを変えただけで、スムーズに動いてしまった。


### adoptopenjdk 8 (1.8.0)

```
jenv global openjdk64-1.8.0.265
```

![画面収録 2020-10-09 23.34.24.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49910/f2fda1ff-78e5-2879-0b47-1c2f1ab4056d.gif "画面収録 2020-10-09 23.34.24.gif")

同じく、スムーズに動く

## JAVA 11


### adoptopenjdk 11


```
jenv global openjdk64-11.0.8
```

![画面収録 2020-10-09 23.54.24.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49910/1b07884c-5c29-c89e-c9ab-f94ea4e64b39.gif "画面収録 2020-10-09 23.54.24.gif")

同じく、スムーズに動く


### correto 11 (11.0.8)

ダメ元で、11.0.3 → 11.0.8に上げてみました。

```
jenv global corretto64-11.0.8
```

![画面収録 2020-10-10 0.04.58.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49910/a07ed572-75e6-fc80-6779-86afe57f3083.gif "画面収録 2020-10-10 0.04.58.gif")

快適に動作するようになってしまいました。


結局、バージョンを踏んでしまっていたのかもしれません。


しかし、誰かの参考になればと思い記録を残します。
[メモ] pandasのunstack

# TL;DR

multiIndexのseriesを見やすくするのに`unstack`が便利でした。

# group by後の処理

pandasのDataFrameは複数のcolumnで`groupby`をするとindexが[MultiIndex](https://pandas.pydata.org/pandas-docs/stable/user_guide/advanced.html)というものになります。処理するのに少し詰まったので備忘録として、やったことを書きます。


## 環境

ここでは、

* Python == 3.8
* pandas == 1.1.3

で実行しています。

## データの準備

たとえば、つぎのようなデータがあったとします。

```python
import datetime
import random
import pandas as pd

item_list = ['A', 'A', 'A', 'B', 'C','C', 'D']
data_records = []
ts = datetime.datetime.now()
for _ in range(1000):
    ts += datetime.timedelta(seconds=random.randint(200, 3600))
    data_records.append({
        'ts': ts,
        'wday': ts.weekday(),
        'item': random.choice(item_list),
        'qty': random.randint(1, 5)
    })
df = pd.DataFrame(data_records)
```
`df`として
![Screenshot from 2020-10-10 00-35-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30977/5564706c-5d84-b9b8-60de-eee5d0acaec0.png)
こんな感じのものが得られるはずです。

ここで、

* ts: タイムスタンプ
* wday: 曜日
* item: 商品(ID)
* qty: 個数

を何かのECサイトのログのようなものを想定します。

## やりたいこと

ここで、曜日ごとにどのアイテムが合計どれだけ売れるのかをみたいとします。本当は`ts`で期間を指定するのがふつうですが、それはおいておいて、次のようなことをすると思います。

```python
df.groupby(['wday', 'item']).qty.sum()
```

すると次のようなものが得られます。
![Screenshot from 2020-10-10 00-40-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30977/6b53c3e5-6281-6e5f-6afa-c3c33bef2cf5.png)
悪くはないのですが、もう一つ見にくいです。ここで、`unstack`をすると、

```python
df.groupby(['wday', 'item']).qty.sum().unstack()
```

![Screenshot from 2020-10-10 00-42-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30977/b79800e4-af59-18d3-8f32-495f284621c7.png)
となりました。

## 参考

くわしくは[pandasの公式ドキュメント](https://pandas.pydata.org/pandas-docs/stable/user_guide/reshaping.html)にしっかりと書いてあります。
Dockerと仮装サーバーとコンテナについて

#Docker！仮想サーバー！コンテナ！について
##Dockerとは
###コンテナ化を用いてアプリケーションを開発・配置・実行するためのオープンソースソフトウェアのこと！
![bit201712121505387423.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/725167/e9338ce6-b53b-9d93-b28c-68b2d8495a58.jpeg)
##仮想サーバーとは
###1台のサーバーで仮想的に複数のサーバーを稼働させる仕組みのこと！
##コンテナとは
###他のユーザーから隔離された実行環境のこと。仮想サーバーに比べて起動時間が短く、同じ性能のハードウェアであれば、より多くのコンテナを同時に動かすことができる！
#デプロイの種類について
##カナリアリリースとは
###一部のユーザーにのみ新機能の公開を行い、新しいバージョンにバグなどがないかを検証する手法のこと。サービスに不具合があっても全体に影響しませんよ！というもの。
##ブルーグリーンデプロイメントとは
###仮想サーバーを用い、2つの本番環境を用意し、それぞれバージョンを設定することができるデプロイの運用方法のこと。2つ用意するからどちらかに不具合が生じた場合どちらかにリクエストの方向を変える事ができる為、その間に不具合が生じた方をロールバックする事ができる！
##イミュータブルデプロイメントとは
###常に変更を行わない環境を構築することで、デプロイ時に新しい環境に切り替える手法のこと。古い環境を消去しますよというもの！

##Docker公式   http://docs.docker.jp/

#まとめ
Dockerについて概要を知るkとができましたね。おまけでクラウドの種類もよく使われるので載せておきます。

#おまけ
##SaaS
「Software as a Service」の略で、「サース」または「サーズ」と読む。
クライアント側に導入せずに、サービスを提供しているサーバーに直接アクセスをしてサービスを利用する状況を指す！
(例)
Microsoft Office 365などのオフィスソフト
GmailなどのWebメール
Dropboxなどのオンラインストレージ
サイボウズなどのグループウェア


##PaaS
「Platform as a Service」の略で、「パース」と読む。
作成したアプリケーションなどを、ネットワーク上に公開するためのプラットフォームを提供するサービスのこと！
(例)
<font color="Red">Heroku</font>


##IaaS
「Infrastructure as a Service 」の略で、「イアース」や「アイアース」と読む。
サービスを利用するユーザーが、仮想化をしたCPUやメモリ、ストレージなどをインターネット経由で提供するサービスのこと!
(例)
Microsoft Azure
Google Compute Engine

##現場からは以上です！
[Ruby on rails] いいね機能の実装

#はじめに
投稿アプリで他のユーザーがいいねを出来るようにいいね機能を実装。
usersテーブルとpostsテーブルとlikesテーブルがあるものとします。

#アソシエーション
まずは、各テーブルの関係性を考え、アソシエーションを定義します。
**ユーザー(1):いいね(多)**
**投稿(1):いいね(多)**
１人１投稿に１回の**いいね**までにしたいのでバリデーションもかけます。

```like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :post

  validates_uniqueness_of :post_id, scope: :user_id

end
```

投稿が削除されたされた場合**いいね**も削除。

```post.rb
has_many :likes, dependent: :destroy
```


```user.rb
has_many :likes, dependent: :destroy

def already_liked?(post)
  self.likes.exists?(post_id: post.id)
end
```

#コントローラー実装

```likes_controller.rb
class LikesController < ApplicationController

  def create
    @like = current_user.likes.create(post_id: params[:post_id])
    redirect_back(fallback_location: root_path)
  end

  def destroy
    @post = Post.find(params[:post_id])
    @like = current_user.likes.find_by(post_id: @post.id)
    @like.destroy
    redirect_back(fallback_location: root_path)
  end

end
```

#ルーティングの設定

```routes.rb
resources :posts do
  resource :likes, only: [:create, :destroy]
end
```

`post_likes `
`DELETE /posts/:post_id/likes(.:format) likes#destroy`
 `POST  /posts/:post_id/likes(.:format) likes#create`

#viewの実装

```~.html.erb
<% if current_user.already_liked?(post) %>
  <%= link_to post_likes_path(post), method: :delete do %>
    <i class="fas fa-heart"></i>
  <% end %>
<% else %>
  <%= link_to post_likes_path(post), method: :post do %>
    <i class="far fa-heart"></i>
  <% end %>
<% end %>
<%= post.likes.count %>    //いいねの数を表示
```

すでにcurrent_userがいいねしているか？
`trueであればいいねを解除`して、`falseであればいいね`をする。

#最後に
いいねの実装方法は他にも非同期での実装などもあります。
まだまだ勉強中ですが、色々な技術を勉強し出来ることを増やしていきたいと思います。
最後まで読んでいただきありがとうございます:grin:
セキュリティの資格攻略法

#はじめに
資格の良し悪しは別として、入札などで資格保持者が必要になるケースはそれなりにあると思います。所属する組織によっては受験費用を負担してくれたり、報奨金が出る場合もありますね。ということで、私がこれまでに取得した資格について、資格試験の概要と勉強法についてまとめてみることにしました。なお、中にはかなり前に取得したものもありますので、現在の試験とは異なる可能性があります。
各試験の試験時間、問題数、試験形式、個人的な難易度、試験費用、更新費用をつけています。

#情報処理安全確保支援士
更新料が高いだの講習がめんどくさいだのと色々言われていますが、試験費用が安く、国家資格であるというのは他の資格に比べて優位性があると思います。ただし更新費用は非常に高額なので、所属する組織が更新費用を負担してくれる場合にのみ登録した方が良いでしょう。試験合格だけでも十分意味があると思います。
午前Ⅰ、Ⅱ、午後Ⅰ、Ⅱの4種類の試験すべてで60点以上を取る必要があります。午後Ⅰ、Ⅱは筆記試験です。
他の資格とは異なり、過去問が公開されているため対策はしやすいと思います。過去問を数年分解くだけでもかなり合格率は上がると思います。私は2回で合格しましたが、中には何度も受ける人もいるようです。過去問による対策が可能なので、他の資格試験に比べれば合格は容易でしょう。

| 試験名 | 情報処理安全確保支援士 |
|:-:|:-:|
| 試験時間  | 40～120分  |
| 問題数  | 選択式30問、20問、筆記2問、1問  |
| 難易度  | 中  |
|試験費用|5,700円|
|更新費用|約140,000円|

#CISSP
業界では最も著名な資格の1つでしょう。情報処理安全確保支援士より更新料が安く、国際資格として広く認知されています。米国では取得者の年収は1000万円を超えるという話もあります。日本国内ではそこまでではありませんが、高く評価される資格と言えるでしょう。

取得難易度は高いですが、持っていると高く評価されると思います。認定条件として、試験範囲ドメイン2つ以上について、4年以上（大卒の場合）の業務経験が必要です。
公式テキストを読み込み、練習問題をたくさん解くと良いです。できれば公式トレーニングを受講すると良いです。

| 試験名 | CISSP |
|:-:|:-:|
| 試験時間  | 6時間  |
| 問題数  | 250問  |
| 難易度  | 難  |
|試験費用|約73,000円|
|更新費用|約13,000円|

#SSCP
CISSPを簡単にしたバージョンという感じです。必要な業務経験が1年に短縮されているため、若手でも取得しやすくなっています。
こちらも公式テキストを読み込めば良いです。情報処理安全確保支援士に合格したら、こちらも挑戦してみても良いと思います。

| 試験名 | SSCP |
|:-:|:-:|
| 試験時間  | 3時間  |
| 問題数  | 125問  |
| 難易度  | 中  |
|試験費用|約26,000円|
|更新費用|約13,000円|

#CompTIA CySA+
主に防御技術に関する出題が行われます。CISSPほどではありませんが、それなりに試験範囲は広く、技術的な内容が問われます。
業務経験がない人には少々難しいかもしれません。
私は模擬試験だけで合格できました。

| 試験名 | CompTIA CySA+ |
|:-:|:-:|
| 試験時間  | 165分  |
| 問題数  | 最大85問  |
| 難易度  | 中～高  |
|試験費用|45,071円|
|更新費用|約15,000円|

#CompTIA Pentest+
ペネトレーションテストに関する出題が行われます。CySA+に比べれば試験範囲は狭いです。実技試験ではないので、「このサーバに侵入せよ」みたいな出題はありません。その代わり、スコープや報告といった実務で良く出てくる内容が試験範囲となります。
私が受験した時はテキストがありませんでしたが、現在はテキストがあるのでこれを読むと良いと思います。

| 試験名 | CompTIA Pentest+ |
|:-:|:-:|
| 試験時間  | 165分  |
| 問題数  | 最大80問  |
| 難易度  | 中～高  |
|試験費用|45,071円|
|更新費用|約15,000円|

#CompTIA Security+
セキュリティの入門的な資格です。試験範囲はそこそこ広いですが、セキュリティ関係の業務を行うのであれば、知っておいた方が良い内容が出題されます。情報処理安全確保支援士よりはやや技術寄りですので、業界1～3年目くらいの方が受験すると良いと思います。
なお、試験費用がCySA+やPentest+とさほど変わらないことから、報奨金が出るなどの理由がない限りは、業界歴が4年以上の方はこの資格より上位の資格を受検した方が良いと思います。

| 試験名 | CompTIA Security+ |
|:-:|:-:|
| 試験時間  | 90分  |
| 問題数  | 90問  |
| 難易度  | 低～中  |
|試験費用|43,732円|
|更新費用|約15,000円|

#その他
私の場合は、上記以外にLPIC 303、304、GCFAを取得しています。GCFAはかなりマニアックなので、ここでは記載しません。以下の記事をご参照ください。
LPICはググればすでに多くの方の体験記があるため割愛させていただきます。基本は問題集、理解が薄いところを実機で確認という感じが良いと思います。

GCFA勉強法
https://qiita.com/log0417/items/26d89dac511c6b286875
[Laravel] ユーザ登録時のリダイレクト先変更

## 概要

Laravelアプリでユーザ登録完了後のリダイレクトパスの変更方法をまとめた個人メモです。

## 設定

結論から、`app/Providers/RouteServiceProvider.php`の以下箇所を変更することでリダイレクト先を変更することが可能です。
デフォルトでは`/home`となっています。

```app/Providers/RouteServiceProvider.php
public const HOME = '/home';
```

ユーザ登録のコントローラである、`app/Http/Controllers/RegisterController.php`から呼ばれています。

```app/Http/Controllers/RegisterController.php
protected $redirectTo = RouteServiceProvider::HOME;
```

Laravel本体のソースを見ると、トレイトで実装されている`RegisterUsers `と`RedirectsUsers`により処理が行われていることを確認できます。

https://github.com/laravel/framework/blob/6.x/src/Illuminate/Foundation/Auth/RegistersUsers.php
https://github.com/laravel/framework/blob/6.x/src/Illuminate/Foundation/Auth/RedirectsUsers.php

```RegisterUsers.php
    public function register(Request $request)
    {
        $this->validator($request->all())->validate();

        event(new Registered($user = $this->create($request->all())));

        $this->guard()->login($user);

        return $this->registered($request, $user)
                        ?: redirect($this->redirectPath());
    }
```

```RedirectUsers.php
    public function redirectPath()
    {
        if (method_exists($this, 'redirectTo')) {
            return $this->redirectTo();
        }

        return property_exists($this, 'redirectTo') ? $this->redirectTo : '/home';
    }
```
Exposed + MySQLの場合に気をつけるべき点

## Driver未指定時の挙動

`Database.connect`にて、Driverを指定しない場合、 `com.mysql.cj.jdbc.Driver` が自動的に設定されるが、これはMySQL8.x用のドライバーであるため、MySQL5.xでは動作しないため注意。 
MySQL5.x系を使う際はDriverに`com.mysql.jdbc.Driver`を指定する。
GoToEat 大阪キャンペーンのページをスクレイピングしてcsvファイルに

# 概要
GoToEat大阪のキャンペーンページには、店舗検索のページはありますがマップ表示ができないんですね。

> https://goto-eat.weare.osaka-info.jp/gotoeat/

Google Map APIを使えばマップ表示できますが、その前にまず対象店をテーブル形式で保管する必要があります。

RubyのNokogiriを使ってスクレイピングしました。

# コード

``````ruby
require 'open-uri'
require 'nokogiri'
require "csv"

@id=0
@page=1
@hashes=[]

# GoToEat大阪のページ(1ページ目)
@url = 'https://goto-eat.weare.osaka-info.jp/?search_element_0_0=2&search_element_0_1=3&search_element_0_2=4&search_element_0_3=5&search_element_0_4=6&search_element_0_5=7&search_element_0_6=8&search_element_0_7=9&search_element_0_8=10&search_element_0_9=11&search_element_0_cnt=10&search_element_1_cnt=18&searchbutton=%E5%8A%A0%E7%9B%9F%E5%BA%97%E8%88%97%E3%82%92%E6%A4%9C%E7%B4%A2%E3%81%99%E3%82%8B&csp=search_add&feadvns_max_line_0=2&fe_form_no=0'

def scraping(page)
  # ページを開き、htmlを読み込んで変数htmlに渡す
  html = open(@url) {|f| f.read}
  charset = "utf8"
  # htmlをパース(解析)してオブジェクトを作成
  doc = Nokogiri::HTML.parse(html, nil, charset)

  doc.xpath('/html/body/div/div[1]/main/section/div/div/ul/li').each { |node|
    # データ格納庫であるhashを作成
    hash=Hash.new(nil)
    keys=[:id,:name,:address,:tel,:open,:close,:category1,:category2]
    keys.each {|key| hash.store(key,nil)}

    hash.store(:id,@id)
    hash.store(:name,node.xpath("p").inner_text)
    trs=node.xpath("table").xpath("tr")
    address=trs[0].xpath("td").inner_text
    address.gsub!("\r\n","")
    address.gsub!(/[[:space:]]/,"")
    hash.store(:address,address)
    hash.store(:tel,trs[1].xpath("td").inner_text)
    hash.store(:open,trs[2].xpath("td").inner_text)
    hash.store(:close,trs[3].xpath("td").inner_text)
    categories=node.xpath("ul").xpath("li")
    hash.store(:category1,categories[0].inner_text)  if categories[0]
    hash.store(:category2,categories[1].inner_text)  if categories[1]
    @id+=1
    @hashes << hash

    CSV.open("GoToEatOsaka.csv", "a", headers: hash.keys) {|csv|
      csv << hash.values
    }
  }

  # 次のページを探します
  as=doc.xpath("/html/body/div/div[1]/main/section/div/div/div[2]").xpath("a")
  as.each{|a|
    content=a.get_attribute("title")
    if content=="Page #{page}" then
      puts page
      @url=a.attribute("href")
      break
    end
  }
end

(2..221).each{|page|
  scraping(page)
}
``````

# 実行結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/321960/8ce95782-4168-9896-2e6b-f7c5b5b105bb.png)

いい感じにcsvとして保存できました。
Ruby print puts p printf 出力メソッド

# はじめに
学習用のメモになります。

Rubyにはログやコンソールに何らかの処理の結果を目に見える形で出力するためのメソッドとしてprintメソッドがあります。

そして、Rubyにはprintメソッド以外にも様々な出力系メソッドが存在します

# printメソッド
printメソッドは以下のサンプルコードのように、`半角スペースを空けて出力する値を指定する`だけで利用できます。

printメソッドの特徴は`改行を入れずに引数に指定した値を出力する`ことです。

```
1.print 'こんにちは'
2.print 'hello world'
```
[実行結果]

```
1. こんにちはhello world
```

# putsメソッド
putsメソッドも同様に出力する値を半角スペース後に指定することで利用できますが、printメソッドとの違いは、`末尾に改行が入る形で出力される`ことです。

```
1.print 'こんにちは'
2.print 'hello world'
```
[実行結果]

```
1. こんにちは
2. hello world
```

# pメソッド
pメソッドは`出力する値と共に型情報（文字列や数値型など）を一緒に出力`します。

```
p '私の年齢'
p 40
p '歳です。'
```
[実行結果]

```
"私の年齢"
40
"歳です。"
```

文字列はダブルクォーテーションで囲まれ、数字はそのまま出力されていることが確認できるかと思います。

# printfメソッド

printfメソッドは書式を指定して出力するためのメソッドとなります。

具体的なサンプルをまずは確認してみましょう。

```
printf('商品情報: カテゴリー %s 型番 %d', 'bag', 10111)
```
[実行結果]

```
商品情報: カテゴリー bag 型番 10111
```
これまでの出力メソッドと異なり、カッコで出力内容をくくります。

また、%sや%dはその後のカンマで区切った引数の値が代入されるものとなり、その引数の型（文字列や数値型）によって、%sと%dの指定を使い分けます。
# 最後に
このほかにも様々なメソッドあるので日々更新していきます。
ワコム Wacom Intuos BT S + Ubuntu 18.04 で手こずった２

# 環境
OS: Ubuntu 18.04
Kernel: 5.4.0-48-generic
問題のペンタブ: Wacom Intuos BT S (CTL-4100WL)

# 問題
USB接続時に、反応する領域が正常に認識されない。ペンタブの左側 3/1 ぐらいしか認識しない。

# 解決

##まず確認方法

```bash
$ lsusb
...（略）
Bus 001 Device 007: ID 2d1f:0376
...（略）
```

本当は上記の行の右側にデバイス名が書いてないといけないのですが、この時点では空欄です。

## 解決方法
以下のサイトに情報あり。
https://github.com/linuxwacom/libwacom/wiki/Troubleshooting#special-cases

ワコムのペンタブの中でも特殊なケースのようです。
> 第3世代のWacomIntuosは、Androidサポートを有効にするためのファームウェアアップデートを受け取りました。このサポートは、Androidの汎用HIDドライバーを使用して追加されました（Androidには通常Wacomドライバーが付属していません）。このアップデートでは、ファームウェアがAndroidシステムに接続されているかどうかを「検出」しています。タブレットがAndroidシステムに接続されていると判断した場合、システムに2d1fベンダーID（VID）が表示されるため、libwacom（およびコントロールセンター）はタブレットを認識しません。残念ながら、この検出メカニズムは、LinuxシステムをAndroidシステムとして識別する場合があります。タブレットを手動で元のモード（056a VID）に戻すには、ボタン1と4をN秒間押し続けます。

今回のモデル CTL-4100WL は、この文中で言う「第3世代のWacomIntuos」に該当します。USB接続したときに、PCをAndroid とご認識する訳ですね。対処方法は文中にもあるとおり、「ボタン1と4をN秒間押し続ける」です。一番左と、一番右のキーを同時に押し続けます。何秒押し続けるか？２〜３秒です。インジケータの明るさが変化するので分かり易いと思います。
ちなみに、ずっと押し続けていると、Androidモード → PCモード → Andro... と繰り返します。

## 再び確認

```bash
$ lsusb
...（略）
Bus 001 Device 007: ID 2d1f:0376 Wacom Co., Ltd
...（略）
```

今度はちゃんと「Wacom Co., Ltd」と表記されています。

## 同じ影響を受けるモデル
以下のモデルも同様に事象が起きる可能性があるようです。私と似たような目にあっている方は試してみてください。

| 名前 | モデル名 | VID：PID |
|:-:|:-:|:-:|
| Wacom Intuos M | CTL-6100 | 056a：0375 |
| Wacom Intuos BT M | CTL-6100WL | 056a：0378 |
| Wacom Intuos S | CTL-4100 | 056a：0374 |
| Wacom Intuos BT S | CTL-4100WL | 056a：0376 |
X-Content-TypeOptionsについてと、それにまつわる脆弱性

#X-Content-TypeOptionsとは
MIMEタイプの解釈を厳密にし、MIMEタイプをブラウザに誤認させるタイプの攻撃や、JSONハイジャック攻撃を防ぐことが出来るもの。

攻撃については下でざっくり説明しますが、アプリ側が想定してるMIMEタイプ(ContentType)とは違うタイプで誤認されてしまうと、これを使った攻撃ができちゃうよ！ということ。

###どんな時に使うの？
古いIEはレスポンスヘッダContent-Typeを無視して、拡張子を元にMIMEタイプを判断する性質がある。
この脆弱性の対策として、IE8以降であればレスポンスヘッダに「X-Content-TypeOptions: nosniff」を設定することでレスポンスヘッダを元にMIMEタイプを判定するように修正することが可能。
ただこの指定を行なってもIE7以前では防げないので注意。

###IE7以前でも対策したい場合は？
→ JSONの > や <のエスケープを行う。
JSON内にXSSを発生させる文字が出現しなくなるので、MIMEタイプの誤認が発生したしてもXSSは起こらなくなります。
ここまでやるのかは方針によってだと思いますが…。

ちなみにPHPの場合、json_encodeのオプションパラメータとして JSON_HEX_TAG などを指定することで、小なり記号などのエスケープが可能

###設定方法は？
Apacheのhttp.confファイルに以下を記述
設定変更はApacheの再起動かけないと反映されないので注意

```
Header always append X-Content-Type-Options: nosniff
```
##対策となる脆弱性の例
1. MIMEタイプをブラウザに誤認させるタイプの攻撃
　- JSONを直接表示することによるXSS
　- ファイルダウンロードによるクロスサイト•スクリプティング

2. JSONハイジャック攻撃

がメイン。
一つ一つ、ざっくり概要だけまとめます


###１．JSONを直接表示することによるXSS（MIMEタイプをブラウザに誤認させるタイプの攻撃）
|||
|:---|:---|
|対象機能|JSONを生成するAPI |
|想定されるシーン|メールに記載されている罠サイトや攻撃を受けたサイトなど、罠サイト閲覧により|
|リスク|ユーザー側のブラウザで不正なJavaScriptが実行されたり、偽情報を表示されたりする|
JSONを返す処理で、JSON以外のContent-Typeに誤認されることで、ファイル内のJavaScriptが実行されてしまう。
例えばJSONを返す処理なのにMIMEタイプの指定が抜けていたりすると、デフォルトのtext/htmlとして扱われてしまいファイル内のJavaScriptが実行されてしまったり…

JSONを返すwebAPIは通常XML HttpRequestによるアクセスを想定していますが、APIが返すレスポンスデータがブラウザで直接閲覧されることにより攻撃が可能になる場合があります

・攻撃が可能なパターン
正しくないMIMEタイプで認識されることでおこる。
基本的にはMIMEタイプを正しく設定していれば問題ありませんが、古いIEではリスクが残ってしまう



**対策**

|優先度|対応内容|
|:---|:---|
|必須|MIMEタイプを正しく設定|
|強く推奨|レスポンスヘッダ「X-Content-TypeOptions: nosniff」を設定|
|推奨|小なり記号などをUnicodeエスケープ|
|推奨|XML HttpRequestなどCORS対応の機能だけから呼び出せるようにする|


###２．ファイルダウンロードによるXSS（MIMEタイプをブラウザに誤認させるタイプの攻撃）
|||
|:---|:---|
|対象となる機能|ファイルのアップロード、ダウンロード機能|
|リスク|なりすまし。特にセッション管理や認証のあるページでは被害が大きい|
アップロードしたファイルを利用者がダウンロードする際に、ブラウザがファイルタイプを誤認する場合がある。
例えばPDFファイルを想定しているにも関わらずPDFデータ内にHTMLタグが埋め込まれている場合、条件によってはブラウザかまHTMLファイルと誤認されてしまい、JavaScriptが埋め込まれていた場合に実行されてしまう。
(攻撃を仕掛ける場合は攻撃者がアプリケーション利用者に罠を仕掛けて誤認させるように仕向ける)

**IEの動作について**
ダウンロードコンテンツをIEが処理する場合、そのContent-Typeが IEの扱えるものかを判断し、扱えるものはC ontent-Typeに従って処理を行う。
しかし扱えない種類の場合、URLに含まれる拡張子からファイルタイプを判定していまう。



**ファイルダウンロード時の対策**

|優先度|対応内容|
|:---|:---|
|必須|ファイルのContent-Typeを正しく設定する|
|必須|レスポンスヘッダX-Content-Options: nosniff を出力|
|必要に応じて|ダウンロードを想定したファイルにはレスポンスヘッダとしてContent-Disposition:attachment を指定する(アプリ側で開かずダウンロードファイルの指定になる)|



###3．JSONハイジャック
|||
|:---|:---|
|対象となる機能|JSONを出力するAPIに秘密情報を提供しているもの|
|想定されるシーン|リンクのクリックなど、罠サイトの閲覧により|
|リスク|情報を抜かれることで起こるなりすましなど|
通常JSONデータをscript要素で受け取ることはできないが、JSONを script要素で受け取るための手法をJSONハイジャックという。
(例外: JSONPでは JSONをコールバック関数の引数にすることでアプリケーションからデータを受け取ることができる。)

基本的にはブラウザ側の脆弱性であり(過去古いfire Foxで脆弱性があった)、現在はブラウザ側で対応されていますが、JSONハイジャックの手法は常に考案されていてその度に対策する背景があるため、アプリ側でも対応しておく方がよい。



**対策**

|優先度|対応内容|
|:---|:---|
|強く推奨|レスポンスヘッダ「X-Content-TypeOptions: nosniff」を設定|
|推奨|リクエストヘッダ　X-Requested-With: XMLHttpRequestの確認|
|推奨|XMLHttpRequestなどCORS対応の機能だけから呼び出せるようにする|




以上X-Content-TypeOptionsについてと、それにまつわる脆弱性の例でした。
Raspberry pi向けVisual Studio Code Insiderをインストールしてみた

# はじめに

2020/10/08(アメリカ現地時間）にVisual Studio Code V1.50がリリースされました。
このリリースでは、「Visual Studio Code Insiders」もリリースされ、
Linux向けのARMv7/ARM64でも利用可能となり、raspberry piでもVSCodeが使えるようになりました。

詳しくはコチラの記事を参照してください。
>窓の杜
「Visual Studio Code」Linux ARMv7/ARM64ビルドがプレビュー ～ラズパイでも利用可能に
2020/10/09 11:00 樽井 秀人
URL:https://forest.watch.impress.co.jp/docs/news/1281847.html

試しにraspberry piにインストールしてみたので、インストール方法について紹介したいと思います。

# 環境

|ハード|raspberry pi 3B+|
|---|---|
|OS|Raspbian|
|bit|32bit|

■　raspberry piの詳細は[コチラ](https://www.switch-science.com/catalog/3850/)を参照してください<br>

URL：https://www.switch-science.com/catalog/3850/

■　Visual Studio Code Insidersのダウンロード先
URL：https://code.visualstudio.com/insiders/

# インストール手順
1. 下記のページからVisual Studio Code Insidersの[.deb]の[ARM]をダウンロードする（64bit版はARM 64をダウンロードする）
https://code.visualstudio.com/insiders/#linux

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288745/bb56c173-aa8b-408c-1960-e984f39c78a8.png)

2.ダウンロードしたDebianパッケージをダブルクリックでインストールする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288745/2a1f491d-a390-1fc6-6371-27c3572ab5e6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288745/13d2c4af-08b5-524a-7c73-35d31a011d51.png)

3.インストールをするのにパスワードが必要になるので、パスワードを入力する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288745/ab16c31b-26d7-5e83-cb17-c5ad1b1c1347.png)

4.インストールが終わるまで待つ
5.インストールが終わると、raspberry piメニューの「プログラミング」内に<br>「Visual Studio Code Insiders」が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288745/00dd2b2a-eca3-0f37-ba1d-7e7829114431.png)

# 使ってみての感想
まだ数時間程度しか触っていませんが、普段使っているWindows10 64bit Core i5 16メモリの使用感と比べるのが間違っているほど動作が重いです。
試しに日本語化とPython環境を構築しましたが、それだけのことを行うのにもストレスを感じます。

マシンスペックの高いraspberry pi 4ならまだ多少は使えるかもしれませんが、
あくまで「**おまけ**」程度。**インストールはできるけど、実用には向かないです**

「どうしてもraspberry piでVSCodeを使いたい！」という人以外は精神衛生上、使わないことをオススメします
ワコム Wacom Intuos BT S + Ubuntu 18.04 で手こずった１

# 環境
OS: Ubuntu 18.04
Kernel: 5.4.0-48-generic
問題のペンタブ: Wacom Intuos BT S (CTL-4100WL)

# 問題
Ubuntu 18.04 には、元々「ワコムタブレット」という設定メニューが標準で用意されているのですが、このメニューでペンタブが認識してくれませんでした。
虚しくも「タブレットを検出できません」の表示が、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513300/10a81477-a3ef-c859-dad7-6f0b4d49840a.png)

## 設定メニューが必要な事情
デュアルディスプレイ環境だと、ペンタブ幅が = 2画面分の幅になってしまうので、横に動かすとポインターがすごい距離を動いてしまいます。なので、１画面だけに限定されるよう設定したかったのです。その設定がまさにこのメニュー画面にある（はず）なのです。絶対に解決しないと。

# 解決
解決、しました。

## 認識確認
以下のコマンドで認識されているワコムのペンタブが確認できます。が、認識されていないと、何も返って来ません。

```bash
$ /usr/bin/libwacom-list-local-devices
```

## 解決方法
以下のディレクトリに、このペンタブに対応したファイルを配置する必要があります。
`/usr/share/libwacom`

ファイルは、こちらから探します。
`https://github.com/linuxwacom/libwacom/tree/master/data`

このモデルに対応したファイルは `intuos-s-p3-wl.tablet` です。（ファイルの内容、３行目に「CTL-4100WL」と明記されています）
ファイルはダウンロードしてきても、以下のようにエディタで開いてソースをコピペしても構いません。

```bash
$ cd /usr/share/libwacom
$ sudo touch intuos-s-p3-wl.tablet
$ sudo vi intuos-s-p3-wl.tablet
```

あらためて確認すると、

```bash
$ /usr/bin/libwacom-list-local-devices
[Device]
Name=Wacom Intuos BT S
DeviceMatch=usb:056a:0376;bluetooth:056a:0377;
Class=Bamboo
Width=6
Height=4
IntegratedIn=
Layout=intuos-s-p3.svg
Styli=0;

[Features]
Reversible=false
Stylus=true
Ring=false
Ring2=false
Touch=false
TouchSwitch=false
StatusLEDs=
NumStrips=0
Buttons=4
[Buttons]
Left=
Right=
Top=A;B;C;D;
Bottom=
Touchstrip=
Touchstrip2=
OLEDs=
Ring=
Ring2=
EvdevCodes=0x110;0x111;0x115;0x116;
RingNumModes=0
Ring2NumModes=0
StripsNumModes=0

---------------------------------------------------------------
[Device]
Name=Wacom Intuos BT S
DeviceMatch=usb:056a:0376;bluetooth:056a:0377;
Class=Bamboo
Width=6
Height=4
IntegratedIn=
Layout=intuos-s-p3.svg
Styli=0;

[Features]
Reversible=false
Stylus=true
Ring=false
Ring2=false
Touch=false
TouchSwitch=false
StatusLEDs=
NumStrips=0
Buttons=4
[Buttons]
Left=
Right=
Top=A;B;C;D;
Bottom=
Touchstrip=
Touchstrip2=
OLEDs=
Ring=
Ring2=
EvdevCodes=0x110;0x111;0x115;0x116;
RingNumModes=0
Ring2NumModes=0
StripsNumModes=0

---------------------------------------------------------------
```

ちゃんとデバイスが認識されていました。

私の環境では、このままではメニューまで表示されませんでしたが、PCを再起動したら表示されるようになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513300/4af39980-cefb-a09a-b735-f1012638d884.png)

で、「画面にマッピング」でディスプレイを指定し、目的達成！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513300/5bbb3e0f-da2c-ef28-25c7-a28148d45667.png)

この問題については以上ですが、USB接続でご認識するという問題もあったので、それはまた別の記事で。
Automation360  - タスクボット間のデータの受け渡し

#タスクBotコマンドを利用した子ボット実行時のデータ受け渡し

ボットを開発するうえでメンテナンス性を高めるために、特定の処理を一つのタスクボットとして開発し、
それを他のタスクボットから呼び出すといった処理を良く実装するかと思います。
イメージとしてはこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615564/97ab1ab4-eb4d-a158-0fb5-30dd876ff89c.png)
##どうやってデータを渡す？
ここで問題になってくるのが、どうやったら親タスクボットから子タスクボットにデータを渡せるのか。
そして、その逆に、子タスクボットからのデータを親タスクボットに戻すことが出来るのか。
ちょっと分かりづらいので、開発の流れをで書いていきます。

###手順1. 子ボットで変数を作る。
まず、子ボットの中で変数を作ります。

変数名は何でもよいのですが、ここでは「Input変数」という名前で変数を作成し、「入力として使用」にチェックを入れました。
これが、親ボットからの入力を受ける変数となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615564/f13e35b3-bbcc-cfdc-2095-3c5a73036df5.png)

次に、「Output変数」という名前の変数を作成し、「出力として使用」にチェックを入れました。
こうすることで、子ボットでこの変数に設定した値が親ボットへ戻されることになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615564/03b84c70-5ce6-2872-c243-b7668e46067c.png)

子ボットとしては、親からの入力をメッセージボックスで表示し、文字列を「Output変数」に設定する単純なものを作りました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615564/ea19de47-dea6-34c2-9535-7136ed0e8cdc.png)

###手順2. 親ボットを作る
次に、親ボットから子ボットを呼び出す処理を作ります。

パラメータで子ボットを選択すると、子ボットの中で「入力として使用」にチェックを入れた変数が表示されますので、
ここで渡したい値を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615564/cf2480c1-ea67-0016-d4b2-f07a9e6740f9.png)

そして、パラメータの一番下の部分で、ディクショナリ型の変数を作成して割り当てます。
子ボットからの戻りはこのディクショナリ型変数に入ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615564/479675ce-0e33-02df-1dd1-83b6b943a622.png)

今回は、どの様に戻ってきているのかを確認しやすいよう、Bot StoreからダウンロードしたJavaFXパッケージを利用してディクショナリ変数の値を表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615564/ad555784-cc34-e94d-9d4f-97fd2672bd40.png)

###手順3. 実行してみます。
親ボットを実行すると、子ボットが呼び出されメッセージボックスに親ボットで設定した値が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615564/f7522874-0a9a-807a-e249-ebf104d2b2b2.png)

そして、その次に、親ボットのJavaFXパッケージのDictionary Viewで子ボットから戻ってきた内容が表示されます。
Keyとして変数名、Valueとして子ボットで変数に設定した値が入っていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615564/b61c5524-cd7c-e7fa-e856-cc186dbc521d.png)

##まとめ
公式のドキュメントを見ても少し分かりづらい部分なのですが、
- 子ボットの中で設定したインプット用変数に親ボットからの入力が渡る
- 子ボットの中で設定したアウトプット用変数に親ボットに渡したいデータを設定する
- 親ボットでは、ディクショナリ変数で子ボットからの戻りを受ける
という形になります。
続・ActiveStorageを使って、画像投稿機能実装までの流れ

#はじめに
　ActiveStorageを使った画像保存から表示までの説明。前回の投稿で、ActiveStorageの導入方法については説明している。

リンク↓
[ActiveStorageを使って、画像投稿機能実装までの流れ](https://qiita.com/TerToEer_sho/items/7f8f42503e3100c652d5)

#画像の保存から表示までの流れ
1. アソシエーションの定義
2. ストロングパラメーターに画像保存の許可をする
3. HTMLのimg要素として生成
4. エラーの解消
5. 画像の大きさ調整
6. バリデーションの設定


###1. アソシエーションの定義
 `has_one_attachedメソッド`を用いて、各レコードと画像ファイルを１対１の関係で紐づける。
紐づけたいモデルファイルに記述

```ruby:model
class　モデル < ApplicationRecord
 has_one_attached :ファイル名
end
```
ファイル名には、呼び出す際の名前を設定する。また、パラメーターのキーになる。
ex):image, :file etc
イメージとしては、ファイル名のカラムができて、そこに添付ファイルが入っているような感じ。
このアソシエーションをすることで、
`モデル名.ファイル名`で添付したファイルにアクセスすることができる。

###2. ストロングパラメーターに画像保存の許可をする
```ruby:controllers
 params.require(:message).permit(:text, :image)
```
アソシエーションの定義のときにも取り上げたように、パラメーターのキーにファイル名を用いる。このときにも、カラムができているイメージというのは当てはまるだろう。

以上が、保存までの流れ。



以下は、表示するまでの流れ。
###3. HTMLのimg要素として生成
`image_tagメソッド`というヘルパーメソッドを用いて、HTMLにimgタグを生成する。

```ruby:html.erb
<%= image_tag モデル名.ファイル名 %>
<%= image_tag message.image %>  #例
```
アソシエーションしているため、`モデル名.ファイル名`だけの記述で画像を表示させられる。


基本的にはここまでで、画像を表示できる。しかしこのままでは、画像が必ずなければ、エラーになってしまう。

###4. エラーの解消

　先の`image_tagメソッド`にif文で条件分岐させる。

```ruby:html.erb
<%= image_tag message.image, if message.image.attached? %>
```

`attached?メソッド` で画像が添付されていると、trueを返し、読み込まれる。画像が添付されていないと、falseを返し、読み込まれず、エラーが起きない。

###5. 画像の大きさ調整

ActiveStorageを導入していると使えるメソッドの`variantメソッド`を使う。
先のコードに付け足すと、

```ruby:html.erb
<%= image_tag message.image.variant(resize: '500×500'), if message.image.attached? %>
```
画像の表示サイズを指定すると、それ以上に大きくならない。

###6. バリデーションの設定

現状だと、LINEをイメージすると、テキストと画像が必ず両方ないとエラーになる。使い勝手がよくないため、テキストと画像のどちらかが存在すればよい、という仕様にする。

```ruby:model
validates :message, presence: true, unless: :was_attacher?

def was_attached?
 self.image.attached?
end
```
メソッドの返り値がfalseならば、バリデーションの検証を行う。（messageが存在しないとダメ）



##最後に
アソシエーションするだけで、簡単に画像にアクセスできるのが便利すぎる！！
機械学習に必要な数学　

# What

Chainerを利用して機械学習を学ぶにあたり、私自身が、気がついた点、リサーチした内容をまとめる記事になります。今回は、**機械学習に必要な数学**を抜粋して勉強します。

私の理解に基づいて記述しているため、間違っている場合があります。間違いは都度修正するつもりです、ご容赦ください。

# Content

### 機械学習とは
お決まりのなんぞや？って話ですね。 コンピューターを使ったパターンや規則のを見つけ出すことなんですね。人間では処理しきれない複雑なパターンや、規則についてコンピュータに解かせるという手法です。決して、コンピューターに無鉄砲なアウトプットを求めるわけではないみたいです。

>3.1. 機械学習とは
機械学習は、与えられたデータから、**未知のデータに対しても当てはまる規則やパターンを抽出**したり、それらを元に未知のデータを分類したり、予測したりする手法を研究する学術領域です。 機械学習は様々な技術に応用されており、例えば画像認識、音声認識、文書分類、医療診断、迷惑メール検知、商品推薦など、幅広い分野で重要な役割を果たしています。

### 教師あり学習
>機械学習の代表的な問題設定として、教師あり学習 (supervised learning) というものがあります。 これは、問題に対して予め答えを用意しておき、予想された答えと実際の答えの違いが小さくなるように訓練を行う方法です。

教師あり学習では答え合わせが必要なんですね。答えを誰もしらないような問題を解くにはまだ足りないと思っていいのか。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764781/cac4ab56-9cc0-e0d2-50dc-b259348284b5.png)
この図は、tとxの関係を示す、データ点の集合を表している。例えば、上の図に対して、近似線を引くとき、その精度が良ければ、予測精度が高いことを機械学習において*汎化性能が高い*と表現する。また、予測するに使うツール（例では近似線）をモデルと呼ぶ。テキストでは
>パラメータを使って何らかの計算を行うことで、与えられたデータの特徴や関係性を表すもの

と表現している。

###目的関数

>コンピュータに良いパラメータを自動的に見つけてもらうためには、何が良いパラメータなのかという指標を定義する必要があります。 そのような指標を表す関数を目的関数 (objective function) と呼びます。

変数y,xに対して y = ax + bを計算するとき。機械学習でa, bを精度良く決定したいとする。
ここで、𝑥 を入力変数 (input variable)、𝑦 を出力変数 (output variable) と呼びます。 今、任意の 1 点の 𝑥 座標の値を式に与えたとします。 このとき、式に与えた具体的な 𝑥 の値を、入力値 (input value) と呼びます。 そして、パラメータ a と b を用いて計算を行った結果得られる具体的な y の値を、予測値 (predicted value) と呼びます。 このとき、直線の式ではなく、実際の赤い点の持つ 𝑡 の値は、この式が予測したい目標の値なので、目標値 (target value) と呼びます。
目的関数は多くの場合、モデルの予測値と目標値を受け取って、その間の差異を測って返すような関数です。 差異は小さければ小さいほどモデルの予測が当たっていることを意味するため、この場合は目的関数の値を最小にするパラメータを見つけ出すのが目的になります。

目的値と予測値の数字の値を小さくするなら目的関数はそんな難しくなさそうだけど、対象が画像とか、音声とか人の感覚で差異を判断するようなデータに対して目的関数を決めろと言われてもパッとイメージできないですね・・・・
ここに機械学習の難しさがありそうです

###二乗誤差関数
内容読むと、誤差解析で学ぶ最小二乗法の話なので割愛。

### 目的関数の最適化
目的関数はモデルの精度の良さを数値が小さくなることで計る指標なので、ここでは最適化といえば目的関数を小さくすることだということになります。

### 目的関数を最適化するために必要な数学
ある関数の最小値を求めるためには、グラフの切片の傾きの傾向を掴む必要があります。
なので、微分が役に立ちますね。逆に最適化が難しい関数って何かあるのかな・・・・？

### データ点のまとまりを取り扱う数学
関数として取り扱うことができればまだしも、できない時は、行列、線形代数の出番です。

### データから特徴を読み取る数学
確率、統計です。

上記が必要最低限のようです。最適化のところ奥が深そうです。。。目的関数の最適化が単純な微分で解けないような問題だったら？？？

#Comment
３章はここで終わっています。大学に入った時、工学では数学を道具として扱うことが多いと教わり。当時は全く意味が分からなかったのですが、こうやってまとめてみると本当に道具のような気がします。実際にいろんなデータを取り扱って覚えていくしかなさそうです。
でもワクワクします。
Kaggle House Prices ③ ～ 予測・提出 ～

以下で作成したモデルを使用してテストデータの予測、及び submission ファイルの提出を行います。
[Kaggle House Prices ② ～ モデル作成 ～](https://qiita.com/jun-16/items/a7f028dcb4af83273b3b)

## ライブラリの読み込み
```py
import numpy as np
from sklearn.externals import joblib
```

## データの読み込み
```py
def load_x_test() -> pd.DataFrame:
    """事前に作成したテストデータの特徴量を読み込む

    :return: テストデータの特徴量
    """
    return joblib.load('test_x.pkl')

def load_model(i_fold):
    """事前に作成したモデルを読み込む

    :return: 対象foldのモデル
    """
    return joblib.load(f'model-{i_fold}.pkl')

def load_pred_test():
    """事前に作成したテストデータの予測結果を読み込む

    :return: テストデータの予測結果
    """
    return joblib.load('pred-test.pkl')
```

## テストデータの予測を実行
```py
# クロスバリデーションで学習した各foldのモデルの平均により、テストデータの予測を行う
test_x = load_x_test()
preds = []
n_fold = 4

# 各foldのモデルで予測を行う
for i_fold in range(n_fold):
    print(f'start prediction fold:{i_fold}')
    model = load_model(i_fold)
    pred = model.predict(test_x)
    preds.append(pred)
    print(f'end prediction fold:{i_fold}')

# 予測の平均値を取得する
pred_avg = np.mean(preds, axis=0)

# 予測結果の保存
joblib.dump(pred_avg, 'pred-test.pkl')
```

## 提出用の submission ファイルを作成
```py
pred = load_pred_test()
print(len(pred))
print(load_x_test())
submission = pd.DataFrame(pd.read_csv('test.csv')['Id'])
submission['SalePrice'] = np.exp(pred)
submission.to_csv(
    'submission.csv',
    index=False
)
```

## 提出結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764939/f8be4d40-8ca9-ed97-35e7-52fdcde72c15.png)
手当たり次第IPv6を無効にしてみる

#はじめに
最近は、いろんなサービスがIPv4, IPv6のいずれにも対応している。
今回そんなサービスたちをIPv4のみに限定してしまおう。

# Apache(http)
```/etc/httpd/conf/httpd.conf
#Listen 80
Listen 0.0.0.0:80
```

# Apache(https)
```/etc/httpd/conf.d/ssl.conf
#Listen 443 https
Listen 0.0.0.0:443 https
```

# ssh 

/etc/ssh/sshd_config

```
#AddressFamily any
AddressFamily inet
```

# MySQL(v8)
```/etc/my.cnf.d/mysql-server.cnf
[mysqld]
bind-address=127.0.0.1
mysqlx-bind-address = 127.0.0.1
```

# PostgreSQL
```pg_hba.conf
#host    all             all             ::1/128                 trust
host    all             all             127.0.0.1       255.255.255.255     trust
```
tkinterを使ってGUIでS3に画像をアップロードする

tkinterでGUIを作成して、ボタンを押すことでS3に画像をアップロードするソースコードです。

```python
import tkinter
from boto3.session import Session

session = Session(aws_access_key_id='access_key_id',
                  aws_secret_access_key='secret_access_key_id')

s3 = session.resource('s3')
bucket = s3.Bucket('bucket-name')


def btn_click():
    bucket.upload_file('sample.png', 'sample.png')
    print("uploaded !")


root = tkinter.Tk()
root.title("Image Uploader")
root.geometry("300x200")

btn = tkinter.Button(root, text='アップロード', command=btn_click)
btn.pack()

root.mainloop()
```

スクリプトを実行することで以下のようなGUIが表示されます。
アップロードボタンを押下することで指定された画像がS3にアップロードされます。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178012/b751e730-b2e3-ad31-e4c4-231c52accc80.png)


最後まで読んでいただきありがとうございました。
またお会いしましょう。

ps. ちなみにpyinstallerでexe化する時は以下のコマンドを使いました。

```console
pyinstaller .\uploader.py --onefile --noconsole  --hidden-import=configparser
```
【VSCode】拡張機能無しでbrタグのショートカット追加

![rectangle_large_type_2_969247d7d006986fbd588cca5e651ed4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189886/b25c8ccf-dbc3-9641-697f-8b3bfd50f39e.png)

## あらすじ
デザイン上のテキストをpタグなどにコピペして、それぞれの行にbrタグ入力して…ってやっていたけど流石にメンドすぎたので、ハックをGoogle.

`white-space: pre-line;` とかにしても良かったけど、開きタグの次は改行するっていうポリシーがあったのでやっぱやめた。


`wpautop` に慣れすぎるといけないな（でもやっぱり好きWordPress）

## キーバインディングの追加

`br` タグショートカットといえども、機能的には「任意のキーを押したとき「`<br>`」という文字列を出力する」というモノなので、あしからず。

まずは「Ctrl + P」でコマンドパレットを開き

```
keyboard
```

と入力します。

そして、「`基本設定: キーボード ショットカットを開く（JSON）`」をクリックします。

すると、`keybindings.json` というJSON形式のユーザー用設定ファイルが表示されると思いますので、そちらに追記します。

公式ドキュメントより（というか以下のコードコピペでよいけど）

```
{
    "key": "ctrl+enter", 
    //Ctrl + Enterを押したとき macはCmdでも○
    "command": "type", 
    //argsの内容をもとに入力
    "args": {
        "text": "<br>" 
        //入力内容は<br> 改行タグ
    },
    "when": "editorTextFocus && editorLangId == 'html'" 
    //発動できるのはエディタ内がフォーカスされている && HTML形式のファイルを開いているときのみ
}
```

を追記します。

保存してCtrl + Enterを押して`br`タグが入力されることを確認します。

めでたし！