OmniAICreator/Qiita-1.07M huggingface dataset:
バッチシェルからSQLを実行して終了コードを返す

# 実現したかったこと
JP1やタスクスケジューラなどよりバッチ処理を行う。
バッチの内容はSQL実行。
SQLの処理結果によって終了コード（エラーコード）を返し、起動元でエラー判定を行えるようにしたい。

# ファイル構成
```
sh　フォルダ
├ db_env.sh　　←DBの接続情報等を保持する
├ exec_sql.sh　←SQLを実行するシェル
└ sql　フォルダ
  ├ hoge.sql
  ├ 他にも色々SQLファイルを置くことができる
```

# コード

## DB接続情報のファイル
設定情報は各自の情報に変更してください。

```sh:db_env.sh
export db_user=DBユーザー
export db_pass=パスワード
export db_host=192.168.XXX.XXX:1521
export db_service=orcl
```

## SQL実行バッチ
バッチ引数に、実行したいSQLのファイル名（例えば`hoge.sql`）を与えることで、該当のSQLを実行できます。
SQLの実行においては引数は指定しない想定。

```sh:exec_sql.sh
#!/bin/sh
# このファイルが置かれているディレクトリを変数に取得
SCRIPT_DIR=$(cd $(dirname $0); pwd)
SQL_FILE=$1

# set Oracle environment
. $SCRIPT_DIR/db_env.sh

if [ "$SQL_FILE" = "" ]; then
	# NO arguments
	exit 1
else
	if [[ -f $SCRIPT_DIR/sql/$SQL_FILE ]]; then
		sqlplus $db_user/$db_pass@$db_host/$db_service @$SCRIPT_DIR/sql/$SQL_FILE;
	else
		# NOT exists such file
		exit 2
	fi
fi
# return SQL Result
exit $?
```

## SQLスクリプトのファイル
これが曲者でした。
連続するSQLの処理を行いたいし、例外やロジカルエラー（例えば処理件数が0件だったらエラーとしたい等）はキャッチしたいし、それらの処理結果を終了コードとして返したかったけど、事情があってストアド定義はできないので無名ブロックを利用することにしたら`RETURN`が使えなかったり、無名ブロックの外で`EXIT`したいけどブロック内の変数は外側で使えないし・・・

```sql:hoge.sql
-- SQLエラーが発生した場合はエラーコードを返す
WHENEVER SQLERROR EXIT sql.sqlcode;
-- 独自エラーコードを変数定義
VARIABLE V_ERROR_CD number;

DECLARE
	-- 影響件数を格納する変数
	RESULT_COUNT NUMBER;
	-- ユーザー定義例外の宣言
	USER_EXCEPTION EXCEPTION;
BEGIN
	-- oracle12c以降なら、変数宣言時に初期値をセットできます。
	-- 今回の環境が11gだったのでここで初期化しています。
	:V_ERROR_CD := 0;
	
	-- SQL処理（サンプル）
	DELETE FROM hoge_table
	;
	-- 上記処理の影響件数を取得
	RESULT_COUNT := SQL%ROWCOUNT;
	
	-- 条件によりエラーコードを指定し、ユーザー定義例外へ
	IF RESULT_COUNT <> 0 THEN
		:V_ERROR_CD := 1;
		RAISE USER_EXCEPTION;
	END IF;
	
	-- 他にもこの辺にSQL処理を書けます。
EXCEPTION
	WHEN USER_EXCEPTION THEN
		-- ユーザー定義例外発生時の処理
		ROLLBACK;
	WHEN OTHERS THEN
		-- SQL例外発生時の処理
		ROLLBACK;
END;
/
-- 呼び出し元にエラーコードを返す
EXIT :V_ERROR_CD;
```

# 実行方法
JP1とかタスクスケジューラから実行って書いてたんですが、手元に該当ツールを用意できていないので、コマンド上でシェルを直接実行してます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394995/86d2e5eb-98f6-f254-0d67-3125da2b867a.png)

# 実行結果確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394995/bce72ff4-28cc-ce83-a593-1928afa922c8.png)


# 参考にしたサイト
- [sqlplusのsqlcodeをshの終了コードへ返す](http://yone098.hatenablog.com/entry/20060418/1145338381)
- [SQLPLUSからの戻り値をバッチスクリプト受ける処理](http://www.rexent.co.jp/blog/sqlplus%E3%81%8B%E3%82%89%E3%81%AE%E6%88%BB%E3%82%8A%E5%80%A4%E3%82%92%E3%83%90%E3%83%83%E3%83%81%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E5%8F%97%E3%81%91%E3%82%8B%E5%87%A6%E7%90%86)
- [忘れっぽいエンジニアのオラクルＳＱＬリファレンス(独自例外の処理)](http://oracle.se-free.com/plsql/d6_raise.html)
- [Oracle11gリファレンス - RETURN文](https://docs.oracle.com/cd/E16338_01/appdev.112/b56260/return_statement.htm#i35913)
動的に背景画像を変更する(Ruby on rails,JavaScript)

#はじめに
画像アドレスを入力することで、
背景画像を動的に変更する方法について書いていきます
#実行
基本となるビューファイルは次のようにします。

```haml:wall.haml.html
.body
  = text_area "object_name", {class: "wall-url"},{id: "wall-url"} 
  .wall-btn 画像を変える 

```
`texr_area`が画像アドレスを張り付ける部分で、`.wall-btn`を押したら画像を変わるものとします。

次にJavaScriptです。（jQueryで書いています）

```javascript:wall.js
$(function(){
  $(".wall-btn").on("click",function(){ 
  }) 
});
```
これで、`.wall-btn`をクリックした時に発火する関数ができました。
次は、処理の内容を書いていきます。

```javascript:wall.js
$(function(){
  $(".wall-btn").on("click",function(){ 
    var image = $("#wall-url").val(); 
  }) 
});
```

これでテキストエリアに入力された画像アドレスを取得し、変数`image`に代入できます。
テキストエリアの`id`が`wall-url`となっているので、`#wall-url`の中身を取得するようにすればよいわけです。

最後に、背景画像を取得した画像アドレスのものに変更します。

```javascript:wall.js
$(function(){
  $(".wall-btn").on("click",function(){ 
    var image = $("#wall-url").val(); 
    $(".body").css('backgroundImage',`url("${image}")`); 
  }) 
});
```
注意するところは、`background-image`を変更する処理ですが、
ここでは`backgroundImage`となっているところです。
`-`がなくなって、直後の文字が大文字になっているわけですね。

`url("${image}")`の部分は、`''`で囲むと`${image}`がただの文字列と表示されるので、この表記にしています。
また、`url(${image})`としてもエラーになるので、必ず`""`をつける必要があります。
WiFiで同じネットワークにあるデバイスが見れない。プライベートとパブリックの違い

ウェブサイトの開発するときに、同じWiFiにつないでIPを指定してスマホなどの別端末でみたりします。
たまにWiFiを借りたときとか、見れないなと思っていたのですが、具体的にどうなっているのか知らなかったので調べてみました。

#WiFiアクセスポイントの機能で切り替えられる

アクセスポイントの機能としてプライバシーセパレーター、ネットワーク分離など呼び方が変わるそうですが、
ネットワークをパブリックとプライベートのどちらにするかを切り替える機能です。

家庭やオフィスなど信頼できる人しか繋がないので、同じネットワークのデバイスが見れるのがプライベート。
カフェやWiFiスポットなど誰でも接続できるので、セキリュティのため見えないようにするのがパブリック。

見れる見れないで考えると逆な気がしますが、誰でも=パブリック、特定の人=プライベート。
と考えるみたいです。

WiFiアクセスポイントの機能なのでルーターまでは制御できず、有線で繋がっているデバイスは見えます。
複数のアクセスポイントを設置してネットワークを作っている場合、アクセスポイントそれぞれで制御をするので、別のアクセスポイントに繋がっている人は見えてしまいます。
ただ、高いWiFiルーターだとVLAN機能を使ってそこら辺をうまいことやって、細かく制御できるようです。

# 外でもプライベートネットワークを使いたい場合はデザリングを使う

iPhoneのデザリングはプライベートネットワーク扱いになるようで、
デザリングすると、iPhoneからIPを指定してPCの環境を見れるようになります。
なのでどうしてもって時はデザリングしましょう。

また調べた限りだと、モバイルルーターは、機種によってプライベート・パブリックは切り替えられるようです（安いやつはできないかも）


プライバシーセパレータという言葉が分からなかったので調べるの大変でした。
困っている人はあまりいないんでしょうかね。。。
Laravel 6系でAmazon SESからメールを送信するときの設定方法

# はじめに
Laravelにて、Amazon SES経由でメールを送信するための設定をまとめました

# 環境
- PHP 7.3
- Laravel Framework 6.13.1

# 前提
Amazon SESの設定、IAMの設定は完了しているものとします

# 設定の流れ
1. AWS sdkをインストールします
2. .envファイルにIMAのアクセスキーID、シークレットアクセスキー、リージョンの設定を追記します
3. キャッシュをクリアします


# 設定してみた
## 1. AWS sdkをインストールします
```bash
$ composer require aws/aws-sdk-php
```

## 2. .envファイルにIMAのアクセスキーID、シークレットアクセスキー、リージョンの設定を追記します
```.env
MAIL_DRIVER=ses
MAIL_FROM_ADDRESS=hoge@example.com

AWS_ACCESS_KEY_ID=【IAMのアクセスキーID】
AWS_SECRET_ACCESS_KEY=【IAMのシークレットアクセスキー】
AWS_DEFAULT_REGION=【Amazon SESのリージョン】
```

（設定不要な箇所は割愛しています）

## キャッシュをクリアします
```bash
$ php artisan config:clear
```


# 補足
上記の設定でメール送信ができるようになりますが、.envの「AWS_ACCESS_KEY_ID」は他のAWSサービス（Amazon DynamoDB、Amazon SQS、S3）の設定でも使用しています。
そのため、例えばSESとS3を使用するアプリケーションの場合、キーが重複してしまいます。
Amazon SESは他のキーを使用したい場合、Amazon SESの設定がある箇所のenvのキーを変更します

```config/services.php

'ses' => [
    'key' => env('AWS_SES_ACCESS_KEY_ID'),
    'secret' => env('AWS_SES_SECRET_ACCESS_KEY'),
    'region' => env('AWS_SES_DEFAULT_REGION', 'ap-southeast-2'),
],
```

その後、.envに変更したキーを追記します

```.env
AWS_SES_ACCESS_KEY_ID=【IAMのアクセスキー】
AWS_SES_SECRET_ACCESS_KEY=【IAMのシークレットアクセスキー】
AWS_SES_DEFAULT_REGION=【Amazon SESを使うリージョン】
```

キャッシュをクリアを忘れずに

# まとめ
キーの設定だけで、Amazon SESからメールが送信できるのは楽でいいですね！
仮想環境でvue-cliを実行するのに詰まった話（備忘録）

vue-cliで起動したサーバにホストブラウザからアクセスできなかったので、その解決方法。
0.0.0.0で外部からもアクセスできるようにするのは知っていたけどその設定をどこに書けばいいかわからりませんでした。

実行環境
virtulabox
centos7
node.js
vue-cli

#1.hostとportの変更

``` /configndex.js
~
 host: '0.0.0.0',
 port: 8080,
~
```

0.0.0.0で外部からアクセスできようにする。
portは自由に設定。

#2.vue.config.jsを追加
プロジェクトルートは直下にvue.config.jsというファイルを作成。
そして以下を記述。

``` /vue.config.js
module.exports = {
  devServer: {
    disableHostCheck: true
  }
}
```

これでプロジェクトのルートで

```
npm run dev 
```

を実行すれば
http://ゲストIP:8080
にアクセスできるはずです！
#参考にさせていただいたサイト・記事
https://qiita.com/white0221/items/d371a19b59af4cba8e8b
https://blog.ark3.net/2018/07/26/204/
inlineとinline-blockの違い

# display主要な4つの値
|display:|特徴|
|---|---|
|**block**|縦並び|
|**inline**|横並び|
|**inline-block**|横並び|
|**none**|非表示|


# inline,inline-blockとは
ほとんどのプロパティの初期値は`display:block`なので、要素間に必ず改行が挟まってしまいます。いいときはいいのですが、横並びに表示したい時は困ります。そういう時にinline,inline-blockを使います。inlineとinline-blockは、改行が入らないので横並びで使えます。

# 結論
||inline|inline-block|
|:-:|:-:|:-:|
|**高さ幅の指定**|できない|できる|
|**paddingとmargin**|左右のみ|上下左右|
FileMakerServer18のインストール作業メモ

###Windowsサーバーの設定時の外での確認用メモ
ざっくりとした内容。自分が良く忘れる事だけ記述。

#1.事前準備
・ダウンロードリンク
　もしくは、ファイル群を用意してもらう事（インストールとライセンスファイル）
・英字の会社名
・サーバーのメモリの確認。

#2.インストールと確認内容
・ライセンスファイルはインストーラーと同じ場所に置いてから、インストーラーを実行。
　英字入力があるので、なるべくコピペで入力。
・インストール終わったら
　・ファイアーウォールでポート番号5003の確認。なかったら追加。
　・キャッシュの設定しなおし。メモリの半分で設定（1K=1024　なので掛け算)

```
fmsadmin set serverconfig cachesize=99999
```


・パスワードなしのファイルを上げないといけない時

```
fmsadmin set serverconfig securefilesonly=false
```

・最後に設定の確認

```
fmsadmin get serverconfig
```




###バックアップ
詳しいお話はここ
https://ywc.com/filemaker/?p=6992
vue-i18nをVue.jsのJavascriptで使う方法

vue-i18nで定義したメッセージをVue.jsのJavascriptで使う方法についてです。

テンプレートで使う方法については検索すればたくさん出てきます。
Javascriptから使う方法がなかなか見つけられなかったので、投稿します。

# 環境

```json
    "vue": "^2.6.11",
    "vue-i18n": "^8.15.4",
```

# langファイル
利用するlangファイルは以下の通りです。

```javascript
export default {
    // 共通
    common: {
        errorLabel: 'エラー',
        toastCloseButtonText: '閉じる',
    },
}
```

# Javascriptで使う方法

```javascript
this.$i18n.t('common.toastCloseButtonText')
```
Kotlin 1.3のCoroutineのコンテキストとディスパッチャ⑦（コンテキストの要素の結合）

# 検証環境
この記事の内容は、以下の環境で検証しました。

* Intellij IDEA ULTIMATE 2018.2
* Kotlin 1.3.0
* Gradle Projectで作成
* GradleファイルはKotlinで記述（KotlinでDSL）

# 準備
詳細は下記の準備を参照してください。
https://qiita.com/naoi/items/8abf2cddfc2cb3802daa

# Combining context elements
[前回](https://qiita.com/naoi/items/72bb2246e7f6b250f726)に引き続き、[公式サイト](http://kotlinlang.org/docs/reference/coroutines/coroutine-context-and-dispatchers.html#combining-context-elements)を読み解いていきます。

前回はコンテキストの要素について説明しました。確か、スレッド名を変更するだけでした。
今回のタイトルを見るとその要素を結合するみたいです。
感のいい人はなんとなく予想がつくかと思います。

それでは、読み進めていきます。

> Sometimes we need to define multiple elements for coroutine context. We can use + operator for that. For example, we can launch a coroutine with an explicitly specified dispatcher and an explicitly specified name at the same time:

意訳込みですが、訳してみると...

> 開発をすすめていると時折、複数の要素を結合する必要がでてきます。その際、+演算子を利用します。+演算子を利用すると、明示的に指定した名前のディスパッチャーとコンテキストをもつコルーチンを起動することができます。

多分、launch関数の引数になにか+演算子を使って、ディスパッチャーとコンテキストの情報を追加する気がしてきます。

サンプルコードがあるので確認してみます。

```kotlin
import kotlinx.coroutines.*

fun main() = runBlocking<Unit> {
    launch(Dispatchers.Default + CoroutineName("test")) {
        println("I'm working in thread ${Thread.currentThread().name}")
    }
}
```

ほぼ、予想通りでした。
実行時に **-Dkotlinx.coroutines.debug**オプションが必要です。
忘れないようにしましょう。
結果を確認してみます。

```text
I'm working in thread DefaultDispatcher-worker-1 @test#2
```

スレッド名のところに順次付与された番号「#2」に、更にtestが付与されています。
コンテキストの要素は+演算子で結合出ていることがわかります。




# まとめ
このブロックで理解できたことは以下のことだと思います。

* コンテキストの要素は+演算子で結合できる
VSCodeで定義元にジャンプするショートカットを設定する

# 手順
1. `cmd + shift + P`で`Preferences: Open Keyboard Shortcuts(JSON)` を開く
2. 下記をペースト

```
[
    {
        "key": "cmd+d",
        "command": "editor.action.goToDeclaration",
        "when": "editorTextFocus"
    }
]
```

これで対象のキーカーソルが乗っているメソッド名の上で、 `cmd + d` を押下すると定義元にジャンプできます。
Outlook for Macで検索が上手く機能しない

# 症状
タイトルや送信者の名前で検索をかけても2週間前のメールしか引っかからない。

# 対策
https://docs.microsoft.com/en-us/outlook/troubleshoot/outlook-for-mac/useful-tools

https://support.apple.com/ja-jp/HT201716

Outlookの関連ファイルをre-indexすれば良いらしい。  

~/Library/Group Containers/UBF8T346G9.Office/Outlook/Outlook 15 Profiles/
上記のフォルダをspotlight プライバシータブの「Spotlightの検索から除外する場所」に追加。
追加後、即解除すると索引が直るそう。

# 結果
治った。
フッターをページの最下部に固定

コンテンツ量が少ない時、フッターの下に余白ができてしまう問題。
調べると、flexboxを使うやり方が割と主流な印象を受けました。

でも、そのやり方だと

```html
<body>
  <div class="content">
    <header>ヘッダー</header>
    <main>コンテンツ</main>
  </div>
  <footer>フッター</footer>
</body>
```
このようにフッター以外をdivでラップする必要があって、ちょっと気持ち悪い🤮と思いました。

##お気に入りの実装
```html:html
<body>
  <header>ヘッダー</header>
  <main>コンテンツ</main>
  <footer>フッター</footer>
</body>
```
```css:css
body {
  position: relative;
}

header {
  height: 100px;
}

main {
  min-height: calc(100vh - 100px);
  padding-bottom: 30px;
}

footer {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 30px;
}
```

## 追記
すみません。普通にflexboxでいい感じにできました。
この方法だとヘッダーの高さとか気にしなくて良いのでこれが良いです。

```html
<body>
  <header>ヘッダー</header>
  <main>コンテンツ</main>
  <footer>フッター</footer>
</body>
```
```css
body {
  display: flex;
  flex-flow: column;
  min-height: 100vh;
}
main {
  flex: 1;
}
```

##参考
- [[CSS]フッタを最下部に表示する5つのテクニック-コンテンツ量が少ない時は最下部に、多い時は成り行きに](https://coliss.com/articles/build-websites/operation/css/css-sticky-footer.html)
python:日付関係

#import

```py
import datetime
```

#文字列から日付へ変換

```py
str_day= '2020/1/15'
first_day= datetime.datetime.strptime(str_day, '%Y/%m/%d')
```

→`2020-01-15 00:00:00`

#datetimeからdateへ変換

```py
first_day.date()
```

→`2020-01-15`

#今日の日付の取得

```py
today=datetime.date.today()
```

#日付の差分
date同士やdatetime同士で引き算をすることができる。
下の例はtodayはdate、first_dayはdatetimeでdateに合わせ引き算をする例

```py
dt=today-first_day.date()
```

引き算した結果は何日と何時間という結果になる

→`51 days, 0:00:00`
結果から日の差分だけ取り出したいときは

```py
dt.days
```

とすると
→`51`
となる。

#日付の足し算

```py
s = first_day+datetime.timedelta(days=1)
```
プログラミング言語の種類

#プログラミング言語とは
主に人間がコンピュータプログラムを記述、編集するために用いる人工言語。

「プログラミング」（programming）・・・コンピュータに意図した動作を行わせるために、まとまった処理手順を作成し、与えること。
「ソースコード」（source code）・・・プログラミング言語で記述したプログラム。
「コンパイル」（compile）・・プログラミング言語で書かれたコンピュータプログラム（ソースコード）を解析し、コンピュータが直接実行可能な形式のプログラム（オブジェクトコード）に変換すること。

##プログラミング言語の種類
###C
1972年に誕生。プログラミング言語の中では誰でも名前を聞いたことがある原点のような言語。
特徴）汎用性が高くて、OSも作れる。また、デバイスドライバーやソフトウェアもゲームも作れる。PC上で動くものから家電の組み込みやロボットでも使用可能。

###Java
1995年登場。今やC言語よりも人口が多いかもしれない人気度ではほぼ1位ともいえる。
特徴）汎用性が高くスタンドアロンなアプリも作れ、WEBアプリ（サーバーで動作するJava:Servletと、ブラウザで動作するJSP）も作れるし、最近だとAndroidアプリもこの言語が使用される。

###BASIC
誕生は1960年代で、70年代から広まった古い言語。
特徴）プログラミング言語を学ぶのに入門的な存在として人気だったが、90年代では既に使われるシーンが無くなっている。

###COBOL
1959年に誕生。現在でも金融系のシステムで現役で可動しているプログラミング言語。
特徴）出来るだけ英語に近づけた言語で読みやすいとは言われており、処理としては料金計算、事務処理などに向いている。

###C++
1983年に誕生。C言語の次に出た言語である（シープラスプラスとかシープラプラなんて呼んだりする）。
特徴）C言語をオブジェクト指向にした言語で上位互換あり。ライブラリも多くC言語同様で様々な場面で使われ、ゲーム開発では必須の言語である。Androidアプリの開発にも使用可能。

###Visual Basic（Visual Basic.NET）
1991年に誕生。Microsoft社が作った言語である。Visual Studioで開発する。
特徴）Basicに似た構文で、グラフィカルなインターフェイスで見た目で部品を配置できたりして初心者でも取り組みやすいとされている。Windowsで動くアプリを作るのに最も簡単に実現するならこの言語を使用。

###Perl
1987年に誕生。Cやsed、awkといったテキスト処理などのプログラミング言語の影響を受けていて、主にテキストの整形や出力に使用される。

###Javascript
Javaと名前がついているがJAVA言語とは全く異なる。元々はNetscapeというブラウザ（今ではほとんどシェアが無く2002年に開発停止）用に作られたスクリプト言語である。
特徴）WEBブラウザ上で動作するプログラミング言語であり、簡単に言い換えるとホームページ上で何かしら動作させる（ボタンを動かしたり、詳細検索画面を開く）事が可能。

###Delphi
Windows、macOS、iOS、Android、Linux向けアプリケーションを開発するための統合開発環境。
ComboBoxでEnumを選択する

# はじめに
EnumをBindingする際、ComboBoxで選択できるようにしたい。
ViewModelでEnumの値リストを持たせてあげればできるが、毎回それを用意するのは面倒なので、他に良い方法がないか調べてみた。

結果としては、ObjectDataProviderを使用するとEnumの列挙が可能である。
さらに、TypeConverter属性を使用することで、Description属性で与えた自由な説明で表示が可能であった。

# 単純にEnumを列挙する
``` csharp:Enumの定義
public enum SampleEnum
{
    A,
    B,
    C
}
```
``` xml:リソース定義
<ResourceDictionary>
    <ObjectDataProvider x:key="Enums"
                        MethodName="GetValues"
                        ObjectType="{x:Type enums:SampleEnum}">
        <ObjectDataProvider.MethodParameters>
            <x:Type TypeName="enums:SampleEnum" />
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</ResourceDictionary>
```
``` xml:ComboBox
<ComboBox ItemsSource="{Binding Source={StaticResource Enums}}"
          SelectedItem="{Binding}" />
```

上記の場合、通常のEnumの値(今回の例だと「A」「B」「C」)がComboBoxの選択肢として表示される。

# Description属性で指定した文字列を表示する
Description属性を用いて、内部と表示部分の表記を分けられるようにする。

``` csharp:EnumDescriptionConverter.cs
using System;
using System.ComponentModel;
namespace Sample
{
    public class EnumDescriptionConverter : EnumConverter
    {
        public EnumDescriptionConverter(Type type) : base(type)
        {
        }
        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string))
            {
                if (value != null)
                {
                    var fi = value.GetType().GetField(value.ToString());
                    if (fi != null)
                    {
                        var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                        return ((attributes.Length > 0) && (!string.IsNullOrEmpty(attributes[0].Description))) ? attributes[0].Description : value.ToString();
                    }
                }

                return string.Empty;
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
}
```
``` csharp:Enumの定義
[TypeConverter(typeof(EnumDescriptionConverter))]
public enum SampleEnum
{
    [Description("選択肢1")]
    A,
    [Description("選択肢2")]
    B,
    [Description("選択肢3")]
    C
}
```
上記によって、Description属性で指定した文字列(今回の例だと「選択肢1」「選択肢2」「選択肢3」)がComboBoxの選択肢として表示される。
JavaとC# とJavascriptのクラスの書き方の違い

#今回はJavaとJavascript(ES6)とC#のクラスの書き方の違いについてあげてみようと思います。
##実際は複雑かもしれませんがクラスでぐらつく人って結構多いと思います。
昔の自分がそうだったんですが、いきなりJavaのプログラムを書こうとしたものでクラスがわからず困ったことがありました。
クラスという概念はメソッドの上のプログラムを拡張していると思っていただければ簡単にプログラミングができると思います。
実際いうとJavascriptのクラスはJavaやC#に比べて拡張がまだ粗いと思います。

```javascript
//Javascript(ES6)

import {hogehoge} from './hogehoge';

class hogehoge{
    static hogehoge(height, width){
        this.height = height;
        this.width = width;
    }
}
```

```javascript

//Javascript

import hogehoge from './hogehoge';

class hogehoge{
    static hogehoge(height, width){
        this.height = height;
        this.width = width;
    }
}

```

``` C#
//C#

using hoge;

class hoge{
     public void hoge(height, width){
          this.height = height;
          this.width = width;
     }
}
```

```java

//Java

import hoge;

class hoge{
    public void hoge(height, width){
        this.height = height;
        this.width = width;
    }
}
```
気が付いたと思いますがライブラリの組み込みの部分見てもらうとわかりやすいかもしれません。
実際に組み込みする場合JavaやC#は実際組み込む場合クラス名で組み込んでいます。
Javascript（ES6）やJavascriptの場合ですがクラス名と実装ライブラリ名が存在しています。
Javascriptのクラスの場合は組み込みライブラリ名とクラス名が一致しないといけません。
JavaやC#の場合はダイレクトにクラス名を書くことで実装できます。

あと気をつけないといけないところはメソッドの書き方です。
JavaやC#の場合は祖先の言語がCやC++なので必ずvoidクラスが存在していますがJavascriptの場合いきなりstaticメソッドを実装することで完了しています。
これはスクリプト言語の特徴でstaticメソッドと動的メソッドで実装するようになっています。
JavaやC#の場合は必ずコンパイルがついて回ります。
Javascript（ES6）の場合はトランスパイルと言う方法がとられているのも特徴です。

トランスパイルと言うのはES6の場合Javascriptの本来の書き方に戻してやらないと実装できない問題があります。
それを実装したのがトランスパイルです。
今のところトランスパイルはBabelで実装しています。

コンパイル言語とスクリプト言語の違いは実際に中間言語やOSに伴った形に置き換えているかとスクリプト自体の書き換えをしているかの違いになります。
気を付けないといけないのJavaやC#の場合はコンパイルがいるということとJavascriptの場合はES6で実装した場合トランスパイルが必要になるということです。
Javascriptで実装した場合はトランスパイルは不要になります。

このようなことがありますので注意していきましょう。
[Swift]NavigatonControllerで戻る押したら「Attempted to read an unowned reference but the object was already deallocated」

## 状況

```ナビゲーションの遷移先.swift
override func loadView() {
    bind()
}

func bind() {
     shareButton?.rx.tap
         .subscribe { [unowend self] _ in
             self.huga() //戻るボタン押したらここでcrash(signal: SIGABRT)
         //.subscribeのスコープ抜け出す時にcrashするみたい
         }
        .disposed(by: disposeBag)
}
```

#### エラー文

```
Fatal error: Attempted to read an unowned reference but the object was already deallocated
```

## 解決策

```ナビゲーションの遷移先.swift
override func loadView() {
    bind()
}

func bind() {
     shareButton?.rx.tap
         .subscribe { [weak self] _ in //弱参照に変更(unowend -> weak)
             self?.huga() //nilを許容
         }
        .disposed(by: disposeBag)
}
```

Rxが原因でも(遷移先で使用していた)WKWebViewが原因でもなく
**遷移時のクロージャの参照が原因**でした。
**強参照、弱参照、循環参照**どれも聞いたことはあるものの全然理解していなかったのであとでまとめます。
参考記事もわかりやすいものがたくさんありました。

## 参考
**・[Swiftの循環参照問題におけるunownedとweakの使い分けについて - Qiita](https://qiita.com/susieyy/items/f57f9362b8e0a89023cb)**
**・[SwiftのARCとクロージャのキャプチャ - しおメモ](https://scior.hatenablog.com/entry/2018/12/24/132704)**
**・[Swift [unowned self]を[weak self]に修正してみた | RE:ENGINES](https://re-engines.com/2019/06/10/swift-unowned-self%E3%82%92weak-self%E3%81%AB%E4%BF%AE%E6%AD%A3%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F/)**
wordpressをdockerで作ったときにテーマがアップロード出来なかったらする事

wordpressの環境を作って無料テーマを入れるとエラーが発生したので一番簡単な解決方法をメモ

## 何が起きたか
dockerでwordpressの環境を作成

dockerでのwordpress環境作成についてはこちらの記事が非常にわかりやすいので参考にしてください!
[Docker Compose でWordPress環境を作ってみる](http://bashalog.c-brains.jp/19/08/15-120000.php)


その後画面上で無料使える[Luxeritas] (https://thk.kanzae.net/wp/)というテーマをいれたところ

<img width="901" alt="スクリーンショット 2020-03-06 9.42.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367399/4e150829-5377-95e1-4da9-e259dea649f0.png">

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367399/ba8d3d39-4299-c148-c24f-6016463e77ef.png)



## 原因
デフォルトのdockerで構築したwordpress環境にはphp.iniファイルがデフォルトで存在しないことが原因

<img width="337" alt="スクリーンショット 2020-03-06 9.36.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367399/b11f2ef3-db3d-300e-209e-3aaea902fb0b.png">

php.iniがいない

そのため**ファイルアップロード上限**の環境変数である`upload_max_filesize`がデフォルトは**2M**となっていた。
ファイルは5.6MBだったので、エラーが返ってきていたというわけですね。

## 解決方法
まずはコンテナが動いていることを確認。

```
docker ps
```

<img width="1280" alt="スクリーンショット 2020-03-06 9.33.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367399/57fa18da-112a-711c-bb6c-9b9999b97468.png">

もし動いてなかったら

```
docker-compose restart
```

で立ち上げておきましょう

次にコンテナの中へ

```
docker exec -it wordpress_wordpress_1 bash
```

php.iniファイルにアップロードのファイル上限を10Mに設定

```
echo upload_max_filesize = 10M >> /usr/local/etc/php/php.ini
```

コンテナを抜けて

```
exit
```

コンテナをリスタート

```
docker restart wordpress_wordpress_1
```

そして再度テーマをアップデートすると、、

<img width="973" alt="スクリーンショット 2020-03-06 10.03.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367399/f6879cc0-9fcd-7890-4ec4-e985b9a8ba9b.png">

無事テーマをアップデート&インストールすることが出来ました！
【Tableau】 コロナウイルスの状況を可視化したダッシュボードを作成した。

※05/06 追記
より有用なダッシュボード等が他サイトで整備されてきたので、これ以降基本的にデータは更新しません。気が向けば時々更新しますが、適時性のあるデータではございませんのでご了承ください。
過去の遺産だと思っていただければ。

# 趣旨
これまではコロナウイルスのデータをRで可視化してきましたが、もう少し動的なカタチでビジュアル化してみたくなったので、今回は **Tableau** を使ってダッシュボードを作ってみました。

# 開発環境

- Windows 10 x64
- Tableau Desktop 2020.1
- Tableau Public 2020.1

私が使っている **Tableau Desktop** は有償版ですが、無償版の[**『Tableau Public』**](https://public.tableau.com/ja-jp/s/download)でも基本的に同じようなものが作れると思います。

※有償版との違いは以下を参考にしてみてください。
[Tableau Publicとは－Public版（無料）のTableauの概要をご説明します | newssdx.kcme.jp](https://newssdx.kcme.jp/tableau_what_is_tableau_public/)

# データセット

毎度おなじみのコチラから。
[都道府県別新型コロナウイルス感染者数マップ Coronavirus COVID-19 Japan Case (2019-nCoV)](https://gis.jag-japan.com/covid19jp/)

<BR>・・・といっても実のところ **提供元もダッシュボードを配信している**ので、折角作るなら提供元にない機能を少し追加してみました。

# 成果物
[COVID-19 in JPN @ Tableau - eveRyday](https://zakkii11.github.io/tableau/covid/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551476/a466591d-537f-2926-9bc2-df5bb0e8d2ce.png)



## 機能

- **罹患者マップ（都道府県・市長区村別）**
    - 延べ数
    - フィルター機能（性別・ステータス・発見日）
    - エリア別データの提供（男女内訳、死亡・退院の内訳）
- **世代別内訳**
    - ツリーマップの表示
    - フィルター機能（性別・ステータス・年代）
- **発症数**
    - ~~発症日別~~ 確定日別（変更）に発症数を集計
    - Tableauの予測機能に基づく将来予測（10日）
- **罹患者のステータス**
    - 性別・ステータス別のテーブル
    - 申し訳程度のソート機能
- **その他**
    - ~~情報量少な目~~ 見やすさ重視

# 留意点

**データの最新性・正確性**
Tableau自体はDropboxとの連携による自動更新が可能なのですが、そのためには変更を追える形で共有できないといけないので、色々試したのですが残念ながら今回は無理でした。なのでデータは手動更新です。
というわけでデータの最新性や正確性については担保できないので、そこはどうかご理解ください。

**予測機能について**
Tableauの仕様上の問題で非負条件等の細かいモデリングが不可能だったり、データ蓄積が不十分だったりするので、発症数の予測結果は参考程度に留めておいてください。

**免責**
ダッシュボードの利用等による損害等は負いかねます。

**レスポンシブへの対応について**
一応レスポンシブになっているようですが、訳も分からず色々と弄りまくってしまった結果、実際にスマホで見るとあまりモバイルフレンドリーではないので、PCでの閲覧を推奨します。

# おわりに
素人でもこんな感じで作れるなんていい時代です。
ありがとう、先人の皆様。

Enjoy!

おしまい。
iOS13においてFirebaseのPUSH通知が上手く動かない？

もしかすると、公式ドキュメントがおかしい(?)かもしれません。

``` swift
# https://firebase.google.com/docs/cloud-messaging/ios/client#method_swizzling_in_firebase_messaging
if #available(iOS 10.0, *) {
  // For iOS 10 display notification (sent via APNS)
  UNUserNotificationCenter.current().delegate = self

  let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
  UNUserNotificationCenter.current().requestAuthorization(
    options: authOptions,
    completionHandler: {_, _ in })
} else {
  let settings: UIUserNotificationSettings =
  UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
  application.registerUserNotificationSettings(settings)
}

application.registerForRemoteNotifications()
```

このような記述がありますが、調査を行ったところ

```swift
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) {granted, error in
    if let error = error {
        print(error)
        return
    }
    if granted {
        DispatchQueue.main.async {
            UIApplication.shared.registerForRemoteNotifications()
        }
    }
}
```

このように requestAuthorization 内で
registerForRemoteNotifications を呼び出す仕様に思えました。

またswizzlingと呼ばれるメソッドを差し替えるテクニックにより
device tokenのセットをFirebase側で行っているが
それがiOS13周りで壊れたのかもしれません(?)

swizzlingをoffにする設定もあります。
swizzlingは出来れば避けたほうが良さそうな雰囲気もあるためOFFにした。

※ 今回あげた2つの原因に対して同時に2つの対策を行ったため切り分けは行っていません。

上記の2点の対策を実行したところ無事にPUSH通知が飛ぶようになりました。

こちらはプログラミング相談サイトMENTAでご相談頂いた内容です。
PUSH通知が飛ばないなどのにお困りでしたら気軽にご相談ください。

https://menta.work/?nic=owAl5EbX7N3uIo7i&ref=ni
https://menta.work/plan/88
Twitterの内容をDiscordにPHPで投稿する

# Twitterの内容をDiscordにPHPで投稿する

### 事前準備

- Twitterの認証周りは[twitteroauth](https://github.com/abraham/twitteroauth
)を使用しています。
composerかレンタルサーバー等であればフォルダごと置いてあげればOK

- Discordの[Webhook Url](https://support.discordapp.com/hc/ja/articles/228383668-%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB-Webhooks%E3%81%B8%E3%81%AE%E5%BA%8F%E7%AB%A0
)を取得しておく

- Twitter Developperのアプリ登録をしてtoken等を取得しておく
[【2019年】TwitterのAPIに登録し、アクセスキー・トークンを取得する具体的な方法
](https://miyastyle.net/twitter-api)

```php
<?php

require "twitteroauth/autoload.php";
use Abraham\TwitterOAuth\TwitterOAuth;

$webhookurl = "https://discordapp.com/api/webhooks/~~~~~~";

$connection = new TwitterOAuth('~~~~~~~', '~~~~~~', '~~~~~~', '~~~~~~');
$content = $connection->get("account/verify_credentials");
$tweets = $connection->get("search/tweets", ["q" => "Youtube AND #cat OR #dog OR #animal filter:videos", "exclude_replies" => true, "count" => 1])->statuses;

foreach ($tweets as $tweet) {
  $json_data = array ('content'=>"$tweet->text");
  $make_json = json_encode($json_data);

  $ch = curl_init( $webhookurl );
  curl_setopt( $ch, CURLOPT_HTTPHEADER, array('Content-type: application/json'));
  curl_setopt( $ch, CURLOPT_POST, 1);
  curl_setopt( $ch, CURLOPT_POSTFIELDS, $make_json);
  curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1);
  curl_setopt( $ch, CURLOPT_HEADER, 0);
  curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1);

  $response = curl_exec( $ch );
}
?>
```

あとはcron等で上記スクリプトを叩く
エンジニア７つの大罪


# エンジニア７つの大罪

１０年ぐらいエンジニアやってて思った
良いエンジニアが行うべき７つの大罪


## １．暴食

チョコレートとコーラが脳を活性化する

## ２．色欲

ネットリテラシーとセキュリティの知識はエロサイトから得る

## ３．強欲

新しい情報、知識、ハイスペックマシンを常に欲しがる

## ４．憤怒

先人のバグに怒ることで、自分の品質が上がる

## ５．怠惰

めんどくさいことやりたくないからツール使って、なければ作る

## ６．傲慢

自分は天才だと思え、自分で自分を褒めることで良いものはできる

## ７．嫉妬

でも、自分よりデキるやつはいるので悔しがってパクって勉強しろ
Zip 4Gbyte問題は過去の話

#はじめに
2005年のzip3.0から64bit拡張ができたとのことなので、2020年の今頃は大抵大丈夫なんだとは思いますが、
いまだにzip4G問題が根強いので、実証実験結果としてあげておきます。

Ubuntu上で7G超のファイルをzip圧縮し、Windows10上で展開してみました。
結論から言うと**標準的な機能を使えば問題ないです**(対応していないツールがあるだけ)。


#実証環境
Zip(Ubuntu 18.04LTSには入っていなかったので2020/3/5にapt install zipで導入)
Zipのバージョン情報を見ると2008/7/5のもののようです。既に**12年近く前**

```
Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
This is Zip 3.0 (July 5th 2008), by Info-ZIP.
Currently maintained by E. Gordon.  Please send bug reports to
the authors using the web page at www.info-zip.org; see README for details.
```

#実証内容
元ファイル：Ubuntu上の7,704,787,456Byteの(H.264の動画)ファイル
 md5sum : 00c069969277dceddd3a2b44250f748c
 ↓
zip3.0で圧縮　7,353,431,881Byte
 ↓
Windows10に転送(SMBドライブ経由)
 ↓
Winndows10の標準の機能で展開
 MD5 : 00c069969277dceddd3a2b44250f748c

ファイルサイズもMD5も一致していました

**※尚Lhaplusは4G以上のZipファイルの解凍に対応しておらず、エラーがでて解凍できませんでした。**
raspberry pi4b  ssh設定メモ

元々opensshはインストールされてる。

sudo vim /etc/ssh/sshd_config
#port 22
の#を取って数字を変える

sudo apt install ufw
↑ufwをaptでインストールする。
sudo ufw enable
↑ファイアーウォールを有効にする。
sudo ufw allow sshd_configで指定したport番号
↑portを開ける。
sudo ufw status
↑portが開いてることを確認する。

sudo systemctl enable ssh.service
↑raspberry piが起動した時にsshサーバーが有効になるようにする。
sudo systemctl start ssh.service
↑sshサーバーを有効にする。


puttyなどでクライアントからログインする
gitでブランチ作り忘れてmasterにcommitしちゃったときの対処

## やらかすパターン

`master`のままで、勢いあまってコミットまでやっちまってることは稀によくある。
pushしようとした瞬間気づく。ここ`master`や……。

## 分身して出戻りすればいいじゃない

1. 同じコミットを参照する新しいHEADを参照するブランチを作る
2. masterの参照位置を`origin/master`まで移動する
3. 何事もなかったかのようにブランチで作業を続行する

```sh
git checkout -b NEW_BRANCH
git checkout master
git reset --hard origin/master
git checkout NEW_BRANCH
```
アセンブラからｃの関数

#概要

アセンブラからｃの関数、呼び出して見た。

#サンプルコード


```
import subprocess
import os

def gcc():
	s_file = 'main.s'
	test_s = """
.code32
.text
.extern test
.global main
main:
	call    test
	mov		$4, %eax
	mov		$1, %ebx
	mov	 	$msg, %ecx
	mov 	$6, %edx
	int	 	$0x80
	ret
.data
msg:
	.ascii 	"hello "
"""
	with open(s_file, 'w') as f:
		f.write(test_s)
	c_file = 'test.c'
	test_c = """
#include <stdio.h>	
void test()
{
	printf("world!");
}
"""
	with open(c_file, 'w') as f:
		f.write(test_c)
	os.system("gcc -no-pie test.c main.s")
	os.system("./a.out")

if __name__ == '__main__':
    gcc()




```

#成果物

https://paiza.io/projects/FwXzJl1sLFNEOXiJ0cHSlw

以上。
神がこんなゲームを作れと言ったから（夢で見たゲーム画面をモバイルで動くように実装するよ②ポストエフェクト編）


![神こん.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311402/0267c6e7-8beb-7790-e5f5-79ee04a77b98.png)

前回の続きです。




画面の絵をいい感じに仕上げるとき、ポストエフェクトを使えば、なんかこう・・・いいですよね。
ですが、ポストエフェクトをかける以上、ある程度の負荷がかかることを覚悟しなければなりません。

前回の記事に記述した通りモバイルで動かしたい前提です。
ポストエフェクトを付けるには自前でシェーダーを作成する方法と、UnityのPost Processing Stackを使用する２つの方法があります。
今回は手ごろに仕上げため、Post Processing Stackのエフェクトを調査します。


使いたいエフェクトは以下の４つ。

- Anti-aliasing
- AmbientOcculusion
- Bloom
- Depth of Field

では、調査していきましょう。

# Anti-aliasing
アンチエイリアス。ピクセルのギザギザが目立たないようにする処理。
ポストエフェクトのアンチエイリアスは、被写体にアンチエイリアスをかける処理をしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311402/1dc6217d-60d6-316d-29de-0ed3a3e05e80.png)

調べてみたところ、ポストエフェクトが行うアンチエイリアス処理は以下のアルゴリズムが使用されているようです。

- FXAA(Fast Approximate Anti-Aliasing)：
輝度に着目して、周囲のピクセルとの輝度の違いを計算
- TemporalAA(Temporal Anti-Aliasing)：
複数のフレームのピクセルから計算

なんのこっちゃって感じだと思いますが、モバイル的には比較的処理の軽いFXAAを使ったほうがいいとのことでした。
詳細は長すぎるので省きます。

# AmbientOcculusion
物体が近接して狭くなったところや部屋の隅などに、周囲の光（環境光）が遮られることによって影が現れる現象をアンビエント・オクルージョンという（wikipediaより）
描画するピクセルの１つ１つに対して、光の当たり具合、他のオブジェクトとの距離などの計算が入る。
そのため、モバイル向けのエフェクトではないらしい。（要するに重い）
ただ、前の記事で光影をベイクしたようにAmbientOcculusionの結果も、メッシュに直接ベイクすることができる。

Lighting設定のLightmapping SettingsのAmbient Occulusionにチェックを⼊れ、Bakeするだけ！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311402/8838f0bf-2e4d-c424-efd7-e6da5467a15a.png)

![ao.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311402/c1da0658-8b4f-495c-2c4c-4ef9871676f7.gif)


# BloomとDepth of Field

Bloomとは、暗い場所で明るい物を見たときに光がフワッと漏れ出すように見える効果のこと。
明度の高い部分だけを抽出してぼかし、元の画像に合成している。この「ぼかし」のアルゴリズムで処理負荷が大きく変わるらしい。
残念ながらPostProccessingStackのぼかしアルゴリズムはそこそこ処理負荷がある・・

Depth of Fieldとは、被写界深度の効果のこと。
ピントの合わない部分をぼかし、元の画像に合成している。この「ぼかし」のアルゴリズムで処理負荷が大きく変わるらしい。（デジャヴ？）
残念ながらPostProccessingStackのぼかしアルゴリズムはそこそこ処理負荷がある・・（デジャヴだ！）

これらは自身でシェーダーを書いて解決することもできるのですが、お金で解決しました。iPhone5でも動くらしいですよ。すごい・・。
[FxPro](https://assetstore.unity.com/packages/vfx/shaders/fullscreen-camera-effects/fxpro-bloom-dof-mobile-ready-22189?locale=ja-JP)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311402/08ca851c-0320-f5e0-8137-b8fc17a412e1.png)

インポートしたら、対象となるカメラにAdd Componentして、パラメータを良しなにいじるだけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311402/0670faf3-6680-e2f3-062b-1bc4db1bb9d9.png)

![fixpro.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311402/e8490684-32e7-83a5-0770-f15dd0c85806.gif)



# 完成！

最後にパーティクルで雪っぽいのを降らせて完成です！

![fuyu.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311402/6c59b4a5-578d-da48-4453-087f169096c8.gif)
[youtubeにあげた動画はこちら](https://www.youtube.com/watch?v=hhrDF0hc0dE)
CUI初心者がAndroidアプリ署名でapksignerを使う

# CUI初心者がAndroidアプリ署名でapksignerを使う
署名方法をJavaのjarsignerからdeveloperガイド記載のapksignerに移行したかった
## 環境
```MacOSX```に```Android Studio```をインストール済み。
## 方法
apksignerのあるディレクトリにpathを通す。
則ちターミナルにて```cd /Users/foo/Library/Android/sdk/build-tools/29.0.3```できるようにする。

1. ターミナルを起動
2. ```find```コマンドでapksignerの場所を確認
3. ```vim```コマンドで```.bash_profile```を編集
4. ```.bash_profile```に通したい```path```を記述
5. ```esc```で入力モードを終了し、```:wq```で保存
6. ```cd```で移動を試しpathが通ったか確認

### 1. ターミナルを起動
```:terminal
foo $
```

### 2. ```find```コマンドでapksignerの場所を確認
```:terminal
$ find . -name 'apksigner'
```

### 3. ```vim```コマンドで```.bash_profile```を編集
```:terminal
$ vim .bash_profile
```

### 4. ```.bash_profile```に通したい```path```を記述
```:.bash_profile
export $PATH:/Users/foo/Library/Android/sdk/build-tools/29.0.3/
```

### 5. ```esc```で```vim```の入力モードを終了し、```:wq```で保存

### 6. ```cd```で移動を試しpathが通ったか確認
```:terminal
$ cd Users/foo/Library/Android/sdk/build-tools/29.0.3
```

## 感想
apksigner使えて嬉しいし、少しずつCUIもわかってきた。便利！
【jQuery】"data"って何？

業務中に「？？？」となったので調べた備忘録です。


```
$("#button").on("click",  {when:"morning", message: "Good Morning"}, function (e){
	console.log(e.data.message);
});
```

これでボタンを押したら"Good Morning"が出力されます。

`foo.data.message`の`data`について、前から何だろうなと思いながらをあまり理解していませんでした。

#「data」の正体
「ボタンを押す」というような出来事のことをイベントといい、このイベントが発火した時に実行されるコードをイベントハンドラ（イベントリスナー）と言います。
このイベントハンドラは引数としてオブジェクトを受け取るのですが、これがイベントオブジェクトです。
つまり今回では`function(e){...}`がイベントハンドラ、`e`がイベントオブジェクトに相当します。

そしてイベントオブジェクトはイベント発生時の情報を持っているという特徴があります。
このイベントオブジェクトにはいくつものプロパティが定義されており、プロパティから多くの情報を取得できます。
プロパティには例えば以下のようなものがあります。

target: イベントの発生元の要素
pagex : イベントが発生したページ上でのx座標
pagey : イベントが発生したページ上でのy座標

**このプロパティの1つにdataがあります。
jQueryではdataプロパティを使うことで、イベントハンドラにオブジェクトと同時にデータを引き渡すことができます。**
上記の書き方によって、ボタンがクリックされた時にdataとして`{when:"morning", message: "Good Morning"}`をイベントオブジェクトであるeに受け渡すことができます。
そして受け渡したイベントハンドラではdataを呼び出すことができるようになります。

つまり、`e.data`でイベントオブジェクトeに受け渡されたデータをみますよーという合図になリ、さらに受け渡したデータのうち`message`をキーとして指定することで対応する値である`"Good Morning"`を取得できるというわけです。

改めて箱を用意しなくても、引数で渡すだけでデータの受け渡しができちゃうんですね。
便利です。

簡単ですが以上がdataの正体となります。
自分で定義していたわけでもないdataがどこから現れたか疑問だったのですが、ようやくスッキリしました。

＊2020/03/07 追記
私の理解の誤りをご指摘いただき、内容を見直し一部修正しております。
urllibとbeautifulSoupでtwitterのツイート数,フォロー数,フォロワー数,いいね数,を取得する

以下の記事で紹介
1000字程度

http://www.minekawada.com/twitter-follower-num-beautifulSoup.html


##内容は以下の通り

フォロワー,フォロー,いいね,ツイート数を取得する目的
seleniumではなくurllibを使う理由
コード
結果
paiza 翌営業日 - その1（練習問題・Cランク相当）

paizaの練習問題を[youtubeの解説動画](https://www.youtube.com/watch?v=433snMZwipk)を視る前に解いたもの。

[問題文](https://paiza.jp/works/mondai/dateset/working_day_1?language_uid=c-sharp)

```c#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NextWorkingDay
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] input = Console.ReadLine().Trim().Split(' ');
            int M = int.Parse(input[0]);
            int D = int.Parse(input[1]);
            string d = input[2];

            int[] nextWorkingDay = new int[2]; //翌営業日の日付

            //月～木、日なら翌日が営業日
            //金なら3日後が営業日
            //土なら2日後が営業日
            switch (d)
            {
                case "FRI":
                    {
                        nextWorkingDay = NextWorkingDay(M, D,3);
                        break;
                    }
                case "SAT":
                    {
                        nextWorkingDay = NextWorkingDay(M, D, 2);
                        break;
                    }
                default:
                    {
                        nextWorkingDay = NextWorkingDay(M, D, 1);
                        break;
                    }
            }
            Console.WriteLine("{0}月{1}日", nextWorkingDay[0], nextWorkingDay[1]);
        }
        //日付から何日後の日付を求める
        static int[] NextWorkingDay(int month,int day,int a)
        {
            for(int i = 0; i < a; i++)
            {
                //翌日の日付を求める
                day++;
                //月末を超えていた場合
                if(day > lastDay(month))
                {
                    day = 1;
                    month++;
                }
                if(month > 12)
                {
                    month = 1;
                }
            }
            return  new int[] { month, day };
        }
        //各月の最終日を返す
        static int lastDay(int month)
        {
            switch (month)
            {
                case 2:
                    return 28;
                case 4:
                case 6:
                case 9:
                case 11:
                    return 30;
                default:
                    return 31;
            }
        }
    }
}
```
【Django】Userモデルに使えるフィールド名・ユーザー登録・ログインの仕方

##はじめに
この記事はDjango初級者脱却を目指す筆者が同じく初心者の方に向けた勉強メモです。言葉選びや解釈に間違いの可能性があります。

##触れること
- Userモデルとは
- userの作り方
- ログイン/ログアウトの実装

## Userモデルとは
DjangoではUser管理用のデータベースがデフォルトで入っており、手間なく認証やユーザ管理を行うことが可能となっています。

Userモデルは以下のような継承関係にあり、以下に示すフィールド名を持ちます。
![Usermodel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392111/5b99e4a8-821e-a648-8727-ab125e3d7574.png)

※ちなみにこの図はこちらの本を参考にさせていただきました。
https://www.amazon.co.jp/dp/B07GK7BWB7/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

また、他のmodelオブジェクト同様にしてデータにアクセスできます。

```python
from django.contrib.auth.models import User

#ちなみにgetメソッドは値が取得できないとエラーを返す
User.objects.get(username='admin') 
User.objects.get(pk=1)

#ちなみにallメソッドはクエリセットオブジェクトを返す
#リストのような使い方ができる
User.objects.all() 

```

##Userの作り方
- コンソールで作る

```bash
(virtualenv)~~~~~$python manage.py createsuperuser
```
Adminサイトにアクセスできるすべての権限を持つスーパーユーザを作ります。

- adminサイトで作る
![admin.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/392111/a840e966-0c10-1ea9-1a4a-e13fe7de9046.jpeg)
runserverしてからAdminサイトへいき、Addをクリック


- views.py内で作る

```python
from django.contrib.auth.models import User

User.objects.create_user(username,email,password)
#ユーザ登録したこの直後にログインしたページへ飛ばしたいときは
#user = User.objects.create_user(username,email,password)
#とすると便利
```


##ログイン/ログアウト
```python
from django.contrib.auth import authenticate, login, logout

def loginfunc(request):
      #Userモデルに一致すればオブジェクトを、なければnoneを返すので以下のように
      #if文を使う
      #Userを登録した直後なら
      #user = User.objects.create_user(username,email,password)で代用可
      user = authenticate(request, username=username, password=password)
        if user is not None:
            #ログイン
            login(request, user)
            return redirect('userpage')
        else:
            return redirect('signup')

def logoutfunc(request):
    #ログアウト
    logout(request)
    return redirect('signup')


```
初心者によるプログラミング学習ログ 255日目

#100日チャレンジの255日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
255日目は、

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">おはようございます<br>255日目<br>・udemyでjavascript講座　インプット学習<a href="https://twitter.com/hashtag/%E6%97%A9%E8%B5%B7%E3%81%8D%E3%83%81%E3%83%A3%E3%83%AC%E3%83%B3%E3%82%B8?src=hash&amp;ref_src=twsrc%5Etfw">#早起きチャレンジ</a><a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E7%B9%8B%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニアと繋がりたい</a><a href="https://twitter.com/hashtag/100DaysOfCode?src=hash&amp;ref_src=twsrc%5Etfw">#100DaysOfCode</a></p>&mdash; ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) <a href="https://twitter.com/yudapinokio/status/1235652950376079364?ref_src=twsrc%5Etfw">March 5, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
WordPressの初期構築手順

#環境
ConoHa VPS
Cent OS 7
Apache/2.4.6
PHP 7.1

# WordPressのインストール
WordPress（以下wp）をインストールする用のディレクトリを作成
`mkdir {wpをインストールするディレクトリ名（以下hoge_dir）}`

ディレクトリ配下で下記コマンドを実行
`wget https://ja.wordpress.org/latest-ja.tar.gz`

取得したtar.gzファイルを展開
`tar -xzvf latest-ja.tar.gz`

tar.gzファイルを削除
`rm latest-ja.tar.gz`

展開したファイルをwpフォルダ配下から移動
`mv wordpress/* .`

wpフォルダを削除
`rm -rf wordpress`

ディレクトリを一階層あがる
`cd ../`

所有者、グループを再帰的に変更
`chown -R apache:developer {dir}`

ディレクトリの権限を再帰的に変更
`find {dir} -type d -exec chmod 775 {} +`

ファイルの権限を再帰的に変更
`find {dir} -type f -exec chmod 664 {} +`

wp用のDBを作成
必要であればhttpd.confを設定
ブラウザでwpをインストールしたディレクトリにアクセス
手順にしたがってwpを設定
設定が完了したらターミナルに戻ってwpをインストールしたディレクトリへ移動

# WordPressのセキュリティー対策
https://cybersecurity-jp.com/security-measures/22048
こちらのサイトをかなり参考

wp-config.phpの権限を400に変更
`chmod 400 wp-config.php`

wpをインストールしたディレクトリ直下の.htaccessに下記を追記

```apache
# シンボリックリンクを有効化
Options +SymLinksIfOwnerMatch

# ディレクトリの一覧表示を無効化する
Options -Indexes

# wp-config.phpにアクセスできないようにする
<Files wp-config.php>
 order allow,deny
 deny from all
</Files>

# XML-RPCを無効化
<Files xmlrpc.php>
 order deny,allow
 deny from all
</Files>

# REST APIを無効化
# BEGIN WordPress
<IfModule mod_rewrite.c>
 RewriteEngine On
 RewriteBase /
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteCond %{REQUEST_FILENAME} !-d
 RewriteCond %{REQUEST_URI} !^/wp-json/
 RewriteRule . /index.php [L]
</IfModule>
# END WordPress

<IfModule mod_rewrite.c>
 RewriteEngine On
 RewriteCond %{QUERY_STRING} rest_route=
 RewriteRule ^$ /? [R=404,L]
</IfModule>
```

wp-content/uploads/.htaccessに下記を追記（ファイルがない場合は作成）

```apache
 <Files *.php>
 deny from all 
</Files>
```

themeディレクトリ直下のfunctions.phpに下記を追記

```php
// WordPressのバージョン情報を非表示にする
 remove_action('wp_head','wp_generator');
 function remove_cssjs_ver2($src) { 
    if (strpos($src, 'ver=' )) 
        $src = remove_query_arg('ver', $src); 
        return $src;
 }
 add_filter( 'style_loader_src', 'remove_cssjs_ver2', 9999 ); 
add_filter( 'script_loader_src', 'remove_cssjs_ver2', 9999 );
```

# wpのログイン履歴や操作ログを記録
プラグイン「WP Security Audit Log」をインストール
参考：https://hacknote.jp/archives/50397/

# wpの定期的なバックアップ
プラグイン「BackWPup – WordPress Backup Plugin」をインストール
参考：https://bazubu.com/how-to-use-backwpup-23804.html
■ バックアップ対象
・ データベースのバックアップ
・ ファイルのバックアップ
・ インストール済みプラグイン一覧
■ アーカイブ形式
・ Tar GZip
■ バックアップファイルの保存方法
・ フォルダーへバックアップ

# スパムコメント対策
「Akismet Anti-Spam (アンチスパム)」を有効化
参考：https://www.adminweb.jp/wordpress-plugin/operation/index1.html
[C言語][Linux]簡単なLinuxコマンドを作成してみる　※足し算するだけ！！

### はじめに
C言語を全く使っていなかったため、久しぶりに使いたくなった。
そこで、Linuxの復習も兼ねて、足し算するだけのコマンドを自作してみる。
※実用性は全くないが。。

コマンドの仕様としては、コマンドラインの第二引数以降の数値の合計値を出力するというもの。

### 目的
・簡単なものを自作することで、コマンドがどのようなものかをイメージできるようにする。
・Linuxがコマンドを実行する際に参照するPATHの概念を理解する。
上記のため、実装について詳細な説明は割愛する。

### 環境
・Amazon Linux AMI release 2018.03
　※Cloud9です。

### 手順
#### 1.テスト用のディレクトリを作成
以降はこのディレクトリ下で作業を行っていく。

```:ディレクトリ作成コマンド
$ mkdir test; cd test
```

#### 2.ソースコードファイルを作成
ここではviにしているが、テキストエディタは何でも良い。

```:ファイル作成コマンド
$ vi test_sum.c
```

#### 3.実装
以下のソースコードをコピペ。

```:test_sum.c
#include <stdio.h>

int main(int argc, char *argv[])
{
  int sum = 0;
  int count = 0;

  if (argc < 3) {
    printf("ERROR: invalid of arguments number\n");
    return 1;
  }

  /* argv[0]はコマンド自身なので、計算対象からは除外するため、count=1から開始 */
  for (count=1; count<argc; count++) {
    sum += atoi(argv[count]);
  }
  printf("%d\n", sum);

  return 0;
}
```
尚、atoiは最上位の桁から変換していき、数字以外の文字を検出した時点で変換を停止させる。
例)"a"：変換できるものがないため、0を返す。
　"1a3"：最上位の1は変換するが、aを検出したため変換を停止、1を返す。

#### 4.コンパイル
gccを使い、ソースコード(test_sum.c)をコンパイル

```:コンパイルコマンド
$ gcc -o test_sum test_sum.c
```
動作は単純だが、これでtest_sumというコマンドが完成！

#### 5.簡単な動作確認
自作したtest_sumを使用してみる。
以下、いくつかのパターンでお試し。

```:test_sumを使った計算
$ ./test_sum 123
ERROR: invalid of arguments number
$ ./test_sum 123 23456
23579
$ ./test_sum 123 23456 812
24391
```
大成功！！
※最初のエラーは引数が少ない(3つ未満)の場合に出力しているエラー。

#### 6.PATH設定
このままだと、毎回パス指定("./"のこと)が必要になってしまうため、他のコマンドのようにパス指定なしで動作するように設定していく。
※同じディレクトリにいても指定が必要になる。

##### 6.1.PATHとは
Linuxが実行ファイル(ELF)を実行するときにファイルを探しに行くパスをまとめている環境変数のこと。
LinuxはPATHに設定されているパスを順番に探していき、最初に見つかった実行ファイルを実行する。
※lsとかfindとかが指定なしで使えるのはPATHに置き場所を保存してあるから。

##### 6.2.PATHの設定
PATHなどの環境変数を設定するにはexportを使用する。
以下のコマンドで今いるディレクトリ(カレントディレクトリ)のパスをPATHに登録。

```:カレントディレクトリのパスをPATHに設定
$ export PATH=$PATH:$(pwd)
```
これで、どのディレクトリにいてもパス指定不要！
<strong>シェルを落としたときにこの設定は消えてしまうことに注意！！</strong>

##### 6.3.動作確認
適当にディレクトリ移動して動作確認をしてみる。

```:動作確認
$ test_sum 1 2 3
6
$ cd ~
$ test_sum 1 2 3
6
$ cd /tmp
$ test_sum 1 2 3
6
```
大成功！！
これで、以降は他のLinuxコマンドのようにtest_sumコマンドを使用できる。
※足し算するだけだけど。。。

### おまけ
lsやfindなどのコマンドのパスを確認する場合はwhichを使用する。
もちろん、作成したtest_sumもwhichコマンドで場所を確認できる。

```:コマンドのパスを確認
$ which ls
alias ls='ls --color=auto'
        /bin/ls
$ which find
/bin/find
$ which test_sum
~/environment/test/test_sum
```

### 修正内容
・タイトルとか内容を少し変更
・PATHについて追記
・既にあるコマンドとの混同を避けるため、名前を変更
・終了ステータスを設定(戻り値を成功時に0、失敗時に1)
　@fujitanozomuさんご指摘ありがとうございます！
・目的を追記
build.gradle が Project ウィンドウ に表示されない

```
 From the Gradle Tool Window in Android Studio (View > Tool Window > Gradle),
1. Right click on the the project’s Gradle config with (root) next to its name.
2. Click on Ignore Gradle project,
3. and right click again then click on Unignore Gradle project.
```
Gradle の設定関連ファイル群を再作成すると良いのでした!!

<img width="549" alt="スクリーンショット 2020-03-06 8.58.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588914/4f73eda3-a883-43fd-37cb-c03e9418ab18.png">

<a href="https://android.benigumo.com/20200305/build-gradle-project-window/" target="_blank">👉 build.gradle が Project ウィンドウ に表示されない&nbsp;<img src="http://b.hatena.ne.jp/entry/image/https://android.benigumo.com/20200305/build-gradle-project-window/"></a>
Pipenv環境下でpsycopg2がpipenv lock時にエラーを吐く時の対処法

# 経緯
herokuにあげようとライブラリを追加していたとき、psycopg2がひっついてきて、Pipfile lockでエラー吐きまくるので困った話

## 環境

| 項目 | バージョン |
| ---- | ----- |
| os  | Ubuntu18.04LTS|
| python | 3.7 |
| pipenv | version 2018.11.26| 

# 解決方法

***なぜ効いたかなどは調べられていないので参考程度に。***

1. psqlをインストールします

```shell

$ sudo apt upgrade
$ sudo apt install postgresql postgresql-contrib

```

以上！！！（なんで？ﾜｶﾗﾅｲﾉﾄﾃﾓｷﾓﾁﾜﾙｲ....）
Windows 10 Homeのvagrantで、Dockerのインストールに失敗する

Vagrantでdebian/buster64でinitしてすぐでも、Dockerのインストールの際にcontainered.ioなどで`Hash sum mismatch`と出てインストールできない。

## 環境
* Windows 10 Home Insider Preview Slow ring (Windowsの環境が違っても読んでみてください)
* VirtualBox 6.1
* Vagrant 2.2.7
* (一応)debian/buster64

## 原因
WSL2を作成する際に追加されたネットワークアダプタが悪さをしていたようです。

## 解決策
WSL2をインストールしてしまった方は、OSをクリーンインストール、もしくは該当のネットワークアダプタと思われるアダプタを無効または削除すれば、直ると**思います(多分)**。

### 備考
OSクリーンインストールで何事もなかったかのようにインストールできました。
インストールしている Node.js のバージョンをダウングレードさせるために n を使ってみる

![logo-light.svg](https://nodejs.org/static/images/logo-light.svg)

# 2020/10/25 追記

記事内容は、[Zenn.dev](https://zenn.dev/ymasaoka/articles/using-n-for-downgrade-nodejs) へ移すことにしました。

Qiita での記事内容は削除しました。
Zenn.dev 側で掲載していた内容と同じものを公開しているので、そちらをご確認ください。

- [インストールしている Node.js のバージョンをダウングレードさせるために n を使ってみる](https://zenn.dev/ymasaoka/articles/using-n-for-downgrade-nodejs)
Pythonで数独を画像から認識して解く

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Pythonで画像を読み込んで数独を解いてみる<br><br>openCV OCR <a href="https://twitter.com/hashtag/Python?src=hash&amp;ref_src=twsrc%5Etfw">#Python</a> <a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E7%B9%8B%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニアと繋がりたい</a> <a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニア</a> <a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E3%81%A4%E3%81%AA%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニアとつながりたい</a> <a href="https://twitter.com/hashtag/%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E7%B9%8B%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#エンジニアと繋がりたい</a> <a href="https://t.co/lNVD3cQrLw">pic.twitter.com/lNVD3cQrLw</a></p>&mdash; たくみ@スカジャンのエンジニア (@hatt_takumi) <a href="https://twitter.com/hatt_takumi/status/1230477800504643591?ref_src=twsrc%5Etfw">February 20, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

この記事は
[Pythonで数独を解く](https://qiita.com/hatt_takumi/items/37a9c15b4e0ad2119ac6)
[Pythonで碁盤目を画像から認識する(数独)](https://qiita.com/hatt_takumi/items/47a46d5e85223a41afa4)
[PythonのOCRで1文字の数字を認識する](https://qiita.com/hatt_takumi/items/5f88cafc82f5e31e6375)
の三つを繋げた結果です。

#まとめ
画像から切り出し→認識→解くまで一連で流すことができた。
最後に、切り離した数独画像に回答の数値を書き込んで再び結合し、表示するようにしている。
mega-cmdをaptでインストールする（Ubuntu版）

日本語のドキュメントが意外となかったので

```bash
sudo sh -c "echo deb https://mega.io/linux/repo/xUbuntu_$((lsb_release -a 2> /dev/null)|grep Release | awk '{print $2}')/ ./ >> /etc/apt/sources.list"
curl -fsSL https://mega.io/linux/repo/xUbuntu_$((lsb_release -a 2> /dev/null)|grep Release | awk '{print $2}')/Release.key | sudo apt-key add -
sudo apt update
sudo apt install -y megacmd
```

参考
[ Installing megacmd in Ubuntu (console only) - ubuntuforums](https://ubuntuforums.org/showthread.php?t=2424593&p=13879100#post13879100)