OmniAICreator/Qiita-1.07M huggingface dataset:
DockerでMySQLコンテナを立ち上げ～実行確認


Dockerのお勉強シリーズその1(続く予定)

https://www.nogawanogawa.work/entry/backend_api

を参考にやってみたことのメモ。

最終的には
frontend + backend + db
の構成を作りたいの第一歩

## 実行環境

* Windows 10
* DockerDesktop for Windows

## ディレクトリ構成

```
.
├── docker-compose.yml
└── mysql
    ├── conf.d
    ├── initdb.d
    │   ├── schema.sql
    │   └── testdata.sql
    └── log
```

## docker-compose.yml

```yml
version: '3.3'
services:
  db:
    image: mysql:latest
    restart: always
    volumes:
      - .\mysql\initdb.d:/docker-entrypoint-initdb.d
      - .\mysql\conf.d:/etc/mysql/conf.d
      - .\mysql\log\ysql:/var/log/mysql
    environment:
      MYSQL_DATABASE: sample_db
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: rootpassword
    ports:
      - 3306:3306
```


## テーブル定義&初期データ

mysql/init.dにテーブル定義と投入する初期データのファイルを作成する

```sql:schema.sql
CREATE TABLE Comment
(
        id int NOT NULL AUTO_INCREMENT,
        title varchar(32) NOT NULL,
        category varchar(32) NOT NULL,
        content varchar(256) NOT NULL,
        PRIMARY KEY(id)
);
```

```sql:testdata.sql
INSERT INTO Comment (title, category, content) VALUES ('curry', 'food', 'good');
INSERT INTO Comment (title, category, content) VALUES ('poteto', 'vegitable', 'good');
INSERT INTO Comment (title, category, content) VALUES ('onion', 'vegitable', 'bad');
INSERT INTO Comment (title, category, content) VALUES ('poak', 'meat', 'very good');
```

## コンテナ起動&確認

docker-compose upでコンテナ起動

```sh
$ docker-compose up -d
```

docker execで起動したコンテナに入り、mysqlに接続。
テストデータが入っていることまで確認。

```
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
f225c6620b90        mysql:latest        "docker-entrypoint.s…"   11 minutes ago      Up 11 minutes       0.0.0.0:3306->3306/tcp, 33060/tcp   webapp_db_1
$ docker exec -it f225c6620b90 bash
# mysql --user=user --password=password
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| sample_db          |
+--------------------+
mysql> show tables;
+---------------------+
| Tables_in_sample_db |
+---------------------+
| Comment             |
+---------------------+
1 row in set (0.00 sec)

mysql> select * from Comment;
+----+--------+-----------+-----------+
| id | title  | category  | content   |
+----+--------+-----------+-----------+
|  1 | curry  | food      | good      |
|  2 | poteto | vegitable | good      |
|  3 | onion  | vegitable | bad       |
|  4 | poak   | meat      | very good |
+----+--------+-----------+-----------+
4 rows in set (0.01 sec)
```
【Laravel】モデルから配列やJsonにキャスト時、必要な属性だけ表示させる

モデルのキャスト時に、パスワードなど表示したくない属性がある。
反対に、表示したい属性のみを指定したいときもある。
その時はモデルのプロパティの`$hidden`もしくは`$visible`を使えばOK

```User.php
protected $hidden = [
    'password'
];
```

### 役割

| プロパティ | 役割 |
|:-:|:-:|
|  $hidden |  登録項目は JSON に含めない。 |
| $visible  |  登録項目は JSON に含める。 |

場合によって使い分けましょう
Get desired branch after a shadow clone(git clone with --depth option)

If you use `--depth` option when cloning a repo like this:

```
git clone --depth=50 repo.git
```

You will see `origin/master` branch of `git branch -a` command. But `git ls-remote --heads origin` will only see all remote branches. Nor we can't checkout a remote branch.

To checkout a remote branch in addition of `master`, you can do this:

```
git remote set-branches origin 'foo'
git fetch -v
git checkout -b foo origin/foo
```
bladeでDBを扱う

## bladeでDBに保存されているデータを呼び出したい
今回はUniversityと言うテーブルに保存されているlogo_urlというカラムをとってきたいと言う内容です。

```php:blade.php
<img src="{{\App\Models\University::first()->logo_url}}" alt="logo">

```
上記のようにModelを使って取得できます。
【npm】パッケージの脆弱性対処（備忘録）

nuxtでアプリをビルドしたときにパッケージの脆弱性に関する警告文が出た
npmの依存関係を修復したい
「**警告文を1㍈も理解せずnpm install で胡麻化してきた**」人にとって、一歩踏み出す補助輪になればとも思いメモを残します
根本的な理解については別途お調べください
# 目次
- [package.jsonとpackage-lock.json](#package.jsonとpackage-lock.json)
- [脆弱性の確認](#脆弱性の確認)
- [脆弱性の自動修正](#脆弱性の自動修正)
- [脆弱性の手動修正](#脆弱性の手動修正)
- [参考記事](#参考記事)

## package.json　と　package-lock.json
node_modules（パッケージの大群）はgitへpushするものではない
なので、再現するためにパッケージを記しておくファイル(**package.json**)が必要
**package.json**　で指定している内容を元に　npm install　では各種パッケージがインストールされる
が、実際にインストールされるものは　**package.json**　への書き方やタイミングでインストールされるものが変わる
そのため、実際にインストールされたものが　**package-lock.json**　へ記録される（package-lock.jsonが存在しない場合は生成される）
これにより、他の開発環境で npm install　する際は　**package-lock.json**　が参照され
固定されたバージョンで各パッケージがインストールがされるため、node_modulesが再現できる

## 脆弱性の確認
```javascript:脆弱性のチェック
npm audit
```
![npmaudit1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207237/1ec0b5e6-3905-f3ff-7aa9-d9efcc384aa0.png)
audit は監査という意味
パッケージの依存関係に脆弱性がある箇所を教えてくれるコマンド
※ただし、npm v6以上

## 脆弱性の自動修正
```javascript:脆弱な箇所の自動修正
npm audit fix
npm audit fix --force
```
![npmaudit2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207237/f3cd9b19-ae3b-e000-2484-078d66ab86bb.png)
ある程度は自動修正してくれる
**high**,**critical**を潰しておけばとりあえずはOK
自動修正してくれない部分はログを見て手動で直した

## 脆弱性の手動修正
```javascript:パッケージの在り処を参照
npm ls "パッケージ名"
```
パッケージ名を指定して、どこにそのパッケージがあるか探すことができる
不必要なパッケージを削除して、インストールしなおす


```javascript:packageのインストール
npm install "パッケージ名"
npm install "パッケージ名@バージョン" ※バージョン指定する場合
```

## 参考記事
#### Qiita記事
[脆弱性の警告を受けたnpmパッケージの依存関係を力技で直す](https://qiita.com/hibikikudo/items/0af352acac85fce28ec2)
[あなたがnpm installをしてはいけない時](https://qiita.com/jigengineer/items/2b15797b850179cb8be0)
[npmパッケージのvulnerability対応フロー](https://qiita.com/riversun/items/7f1679509f38b1ae8adb)
[【Node.js】package.jsonとpackage-lock.jsonについて簡単にまとめる](https://qiita.com/risto24/items/18837cc4eef176e8b6c7)
[package-lock.jsonについて知りたくても聞けなかったこと](https://qiita.com/fj_yohei/items/7ca887a45e0855917279)

#### 外部記事
["npm install"と "npm ci"の違いは何ですか？](https://www.it-swarm-ja.tech/ja/npm/npm-install%E3%81%A8-npm-ci%E3%81%AE%E9%81%95%E3%81%84%E3%81%AF%E4%BD%95%E3%81%A7%E3%81%99%E3%81%8B%EF%BC%9F/807196109/)
[package.jsonとpackage-lock.jsonの運用方法について](https://engineering.mobalab.net/2019/08/08/package-json%E3%81%A8package-lock-json%E3%81%AE%E9%81%8B%E7%94%A8%E6%96%B9%E6%B3%95%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/)



#### 以上
間違った理解や記述があればご指摘いただけると幸いです
Financial Services Cloud の操作方法⇒Financial Services Cloud を使用したクライアントリストの拡大⇒紹介を通じてクライアントを獲得する

１．Kotori が同僚への紹介を作成した後、それをフォローアップするための最も早い方法はどれですか?
２．会社で Einstein リードスコアリングを使用している場合、最初に接触する紹介として最も適している紹介はどれですか?
３．紹介を変換する前に完了しておく一般的な作業はどれですか?
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634241/99009c49-2c85-3533-f756-cc48b6d435db.png)
Julia ver1.4.2 を Jupyter Notebook で動作させるまで

# できること

Julia を Jupyter Notebook で使用するためにすることをメモします．

<img width="1680" alt="スクリーンショット 2020-07-29 14.15.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367076/e02e560f-c133-1999-c365-3d7e0d854591.png">


環境は，

```julia:julia-terminal
julia> versioninfo()
Julia Version 1.4.2
Commit 44fa15b150* (2020-05-23 18:35 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin18.7.0)
  CPU: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-8.0.1 (ORCJIT, skylake)
```

# 流れ

1. Julia の install (爆速通過)
2. Jupyter Notebook の install (爆速通過)
3. IJulia の設定

## Julia の install & Jupyter Notebook の install

Julia の install は　こちらから → https://julialang.org/downloads/
Jupyter Notebook の install は　こちらから → https://jupyter.org/install

### Julia が install されたか確かめる

アプリフォルダにアイコンが出ていますね．
<img width="229" alt="スクリーンショット 2020-07-29 14.18.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367076/c8cba470-f3c4-e9b0-4f6c-bf590dcda20a.png">

### Jupyter Notebook が install されたか確かめる

ターミナルで以下を入力してエンターキーを押す．

```terminal
jupyter notebook 
```

すると，使っているブラウザで以下のようなものが開きます．開けば成功．

<img width="656" alt="スクリーンショット 2020-07-29 14.20.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367076/ab9a12a4-5aab-4539-ad91-740ac3cf7a5c.png">



## IJulia の設定

install した Julia を立ち上げ，以下を実行します．

```julia
julia> using Pkg
julia> Pkg.build("IJulia")
using IJulia; notebook(detached=true)
```

## 確認

ここまで完了したら，もう一度ターミナルで

```terminal
jupyter notebook 
```

を実行します．右側の「New」タブに「JUlia 1.4.2」があればOKです．

<img width="306" alt="スクリーンショット 2020-07-29 14.45.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/367076/0a5261bf-ea20-6682-7b5e-0d8a4afda662.png">

# 最後に

メモクオリティでした．ぜひやってみてください．数値計算などするなら過程を区切ることができるのでお勧めです．
変更していないの行のgit差分

## 概要
コミットを試みたところ、変更していない行に差分があった。
差分の正体は改行コードだった

## 解決策1
trコマンドで置換する

参考
[【 tr 】コマンド――テキストファイルの文字を置換する／削除する](https://www.atmarkit.co.jp/ait/articles/1610/03/news017.html


## 解決策2
gitは自動で改行コードを変換する機能があるのでそれをoffにする

```
git config --global core.autocrlf false
```

参考
[気をつけて！Git for Windowsにおける改行コード - Qiita](https://qiita.com/uggds/items/00a1974ec4f115616580)

[Git - Git の設定](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%81%AE%E8%A8%AD%E5%AE%9A)
書式設定と空白文字 項目の部分

## 解決策3 vimで置換する

1. vimで改行コードを指定してファイルを開き直す

```
：e ++ff=unix 
```

2. ^M を削除する

```
:%s/\r//g
```
JavaScript（jQuery）で動的に追加した要素をフェードアウトしてから削除する方法

#jQueryで動的に追加した要素をフェードアウトしてから削除する方法

　JavaScriptで動的に追加したDOM要素は通常の方法ではイベントが起こらなくなる。
そこでdocumentを使ってHTML読み込み完了後にイベントを発動するよう設定することでイベントが起こる。
　また、queue()を使用することで、フェードアウトを実行してから要素を削除できる。
個人的にかなり躓いた部分なので記録に残します。

```js
  $(document).on('click', 'div', function(){
    $('div').fadeOut(500).queue(function(){
      $(this).remove();
    });
  });

```
Grav

## 参考

* [フラットファイル CMS (Flat-file CMS) 検討メモ – /dev/null](https://maruyuki.code.blog/2020/01/19/%E3%83%95%E3%83%A9%E3%83%83%E3%83%88%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB-cms-flat-file-cms-%E6%A4%9C%E8%A8%8E%E3%83%A1%E3%83%A2/)


## 起動

```sh:run.sh
docker run \
  --name=grav \
  --restart unless-stopped \
  -p 8080:80 \
  -e DUID=1000 \
  -e DGID=1000 \
  -v $(pwd)/grav/backup:/var/www/grav/backup
  -v $(pwd)/grav/logs:/var/www/grav/logs
  -v $(pwd)/grav/user:/var/www/grav/user
  dsavell/grav:admin
```

```yaml:docker-compose.yml
web:
  image: dsavell/grav:admin
  container_name: grav
  restart: always
  volumes:
    - ./backup:/var/www/grav/backup
    - ./logs:/var/www/grav/logs
    - ./user:/var/www/grav/user
  ports:
    - 3000:80
  environment:
    - DUID=1000
    - DGID=1000
```

## 手動インストール

### phpビルトインサーバーでの起動方法

```sh
apt install -y \
  php-curl

git clone "https://github.com/getgrav/grav.git"
cd grav
./bin/grav install
php -S localhost:8001 system/router.php
```

* http://localhost:8001


### admin plugin

* https://github.com/getgrav/grav-plugin-admin

```
bin/gpm install admin

bin/plugin login new-user
```

* http://localhost:8001/admin


### plugins

* https://getgrav.org/downloads/plugins#extras
    * [grav-plugin-tntsearch - README.md](https://github.com/trilbymedia/grav-plugin-tntsearch/blob/develop/README.md)

```sh
apt install -y \
 sqlite \
 php-pdo \
 php-sqlite3

bin/gpm install tntsearch

mkdir -p user/config/plugins

cp user/plugins/tntsearch/tntsearch.yaml \
   user/config/plugins/tntsearch.yaml
```

```sh:クエリの動作確認
# index作成
bin/plugin tntsearch index

bin/plugin tntsearch query ipsum
```

* http://localhost:8001/search

### mermaid

* https://github.com/DanielFlaum/grav-plugin-mermaid-diagrams

```
bin/gpm install mermaid-diagrams
```

```css:書き方
[mermaid]
graph TB
sq[Square shape] --> ci((Circle shape))

subgraph A subgraph
    od>Odd shape]-- Two line<br>edge comment --> ro
    di{Diamond <br/> line break} -.-> ro(Rounded<br>square<br>shape)
    di==>ro2(Rounded square shape)
end

classDef green fill:#9f6,stroke:#333,stroke-width:2px;
classDef orange fill:#f96,stroke:#333,stroke-width:4px;
class sq,e green
class di orange
[/mermaid]
```

![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/30516a9b-f237-8576-8b3d-3d1cfcff3e7c.png)

### マルチアカウント

* https://magnets.jp/web_design/8452/
    * /user/accounts/ユーザ名.yaml

```yaml:/user/accounts/ユーザ名.yaml
password: 'password'
email: 'youremail@mail.com'
fullname: 'Taro Yamada'
title: 'Editor'
access:
  admin:
    login: 'true'
    pages: 'true'
```


----


## wondercms

あとで試す。

* phpの curl, zip が必要

https://www.wondercms.com/demo/
CMake Error: Could not find CMAKE_ROOT !!! を回避

## 概要
cmake のアップデートの際に発生したエラーとその対処について記します。

## cmake アップデート
cmake を 3.5 から 3.18 にアップデートしたい。
こちらのサイト https://cmake.org/download/ からソースファイルをダウンロードして、ビルドしました。具体的には

```
$ wget https://cmake.org/files/v3.18/cmake-3.18.0.tar.gz
$ tar xvf cmake-3.18.0.tar.g
$ ./configure --prefix=[インストールする先]
$ make
$ make install
```

やっていることの説明：

- ./configure で Makefile を作成
-  make でソースコードをコンパイル
- make install で make で生成されたバイナリファイルを[インストールする先]に保存

## 発生したエラー
つぎのエラーが発生しました。

```
$ cmake --version
CMake Error: Could not find CMAKE_ROOT !!!
CMake has most likely not been installed correctly.
Modules directory not found in
/home/name/ubuntu16/share/cmake-3.5
cmake version 3.5.1

CMake suite maintained and supported by Kitware (kitware.com/cmake).
```
これは前のバージョンの cmake を消していないことによるものっぽい...。
Module directory がないと言われているディレクトリを覗いてみると

```
$ ls /home/name/ubuntu16/share/
/home/name/ubuntu16/share/cmake-3.18
```
なるほど、
新しくインストールしたものしかないのに、前のファイルを見ようとしているようです。
新しくインストールしたファイルを見よと教えてあげればよさそう。

## 対応
CMAKE_ROOT を明示的に示すために、.bash_profile に次を追記しました

```
export CMAKE_ROOT=/home/name/ubuntu16/share/cmake-3.18
```
この対処法であっているのかわかりませんが、インストールした cmake のバージョンに合わせて最後は変えれば良いかと思います。

```
$ source .bash_profile 
```
で .bash_profile を読み込むと

```
$ cmake --version
cmake version 3.18.0

CMake suite maintained and supported by Kitware (kitware.com/cmake).
```

これで無事アップデートできたことが確認できました。

## 参考
cmake とはなんぞやという疑問にはこの記事はわかりやすかったです
https://qiita.com/turenar/items/922a845cfc9350b0eece#fn2

cmake アップデートはこの記事を参考にしました
https://qiita.com/koara-local/items/9d01c6bb9dd93563b7c6
windowsでiverilog その59

概要

windowsでiverilogやってみた。
リダクション演算子の作法、調べてみた。

#リダクション演算子

１つの信号に対して各ビット単位で演算を行い、1ビットの結果を返す。
&a
全てのビットが1の場合のみ1。
~&a
全てのビットが1の場合のみ0。
|a
いずれかのビットが1であれば1。全てのビットが0の場合のみ0。
~|a
いずれかのビットが1であれば0。全てのビットが0の場合のみ1。
^a
奇数個の1がある場合は1。偶数個の1がある場合は0となる。

以上。
毎週月曜日に今週分の議事録を各Slackへ送る(GAS)



今回成し遂げること
---------------------

1. カレンダーから予定を検索し、
2. マスタードキュメントから雛形コピーし、
3. 新規ドキュメントを生成し、そのURLを返す。

コード
--------------

##1. Googleカレンダーから1週間分の定例予定を抽出

```javascript:カレンダーから予定を抽出する
CalendarApp.getDefaultCalendar().getEvents(now, end, {search: "検索したい文字列をかく"})[0];

```

補足：searchの他にもこんなoptionがあります。
start — 条件に一致する予定が複数ある場合に、何個目からスタートするかを指定（整数）
max — 条件に一致する予定が複数ある場合に、取得する予定の最大数を指定（整数）
author — 予定の作成者を指定（文字列）
search — 含まれる文字列を指定（文字列）
statusFilters[] — 参加状況のを指定（配列）


nowには今日、
endには1週間後を入れます。

```javascript:今日と1週間後
var now = new Date();
var end = new Date(now.getTime() + (7 * 24 * 60 * 60 * 1000));// 一週間後
```

nowとendを入れたら、eventを取り出します。

```javascript:カレンダーの内容を取り出す
 var events = CalendarApp.getEvents(startTime, endTime);
  for (var i in events) {
    var TITLE = events[i].getTitle(); //title
    var DATE = events[i].getDateFormatted(); //date
    var DURATION = events[i].getDuration(); //duration
  }

```

##2. マスタードキュメントから記事を新規生成

```javascript:今日と1週間後
function makeArticle(TITLE,DATE,DURATION) {

  //マスタードキュメントを定義
  var targetDoc = DocumentApp.openById("xxxxxxxxxxxxxxxxxxxxx");
  //IDを別に書き出して、以下のコードでもOK
  //var targetDoc = DriveApp.getFileById(fileId);

  //コピー先に名前をつけて作成。
  var docName = '[[TITLE]]' + '[[DATE]]' + '_議事録';
  var newdoc = targetDoc.makeCopy(docName + "_" + formattedDate);

  //タイトル、本文をカレンダーの内容に書き換える
  var title = newdoc.getName().replace('[[DATE]]',DATE).replace('[[TITLE]]',TITLE) ;
  var body = newdoc.getBody()
                .getText()
                .replace('[[DATE]]', DATE)
                .replace('[[DURATION]]', DURATION);
  return {
    title: title,
    body: body
  };
}

```

##3. 生成した記事を格納&URL取得

```javascript:格納とURL取得

  //ドキュメントを格納するフォルダを取得
  var targetFolder = DriveApp.getFolderById("xxxxxxxxxxxxxxxxxxxxx");

  //指定したフォルダに所属（移動）させる
  var docFile = DriveApp.getFileById(newdoc.getId());
  targetFolder.addFile(docFile);

  //作成したドキュメントURLを取得
  var documentUrl = newdoc.getUrl();

```

Slackへの送り方はこちらをご覧ください。
[＜Slack＞Slackに通知する関数を書いて、いつでも引用できるようにする(GAS)](https://qiita.com/Rihoritsuko/items/4e1d73989cb84021b664)



参考
[Google Apps Script (GAS) で毎週 30 分の雑務を自動化した話](https://qiita.com/tanabee/items/e51f107f1e95a42cc636)
[Google DocumentをGoogle Apps Scriptでコピーする方法](https://qiita.com/tepi/items/aadc2fcb399a555f5842)
android　webviewをchromeみたいに使う

#まずは全体のコードはこんなかんじです。

```kotlin:MainActivity.kt
package com.sample.android.webview

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.KeyEvent
import android.view.WindowManager
import android.webkit.WebView
import android.webkit.WebViewClient
import com.sample.android.webview.R

class MainActivity  : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val myWebView: WebView = findViewById(R.id.webview)

        // ポイント１，JavaScriptを有効化
        myWebView.getSettings().setJavaScriptEnabled(true);

        // Web Storage を有効化
        myWebView.getSettings().setDomStorageEnabled(true);

        // HTML5 Video support のため
        // Hardware acceleration on
        getWindow().setFlags(
            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
            WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
        );

        // ポイント２，ページナビゲーションの処理
        myWebView.webViewClient = WebViewClient()

        myWebView.loadUrl("https://yahoo.co.jp")
    }

    // ポイント３， ウェブページ履歴を操作する
    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        val myWebView: WebView = findViewById(R.id.webview)
        if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
            myWebView.goBack()
            return true
        }
        return super.onKeyDown(keyCode, event)
    }
}
```

##ポイント１、JavaScriptを有効にする
```kotlin
myWebView.getSettings().setJavaScriptEnabled(true);
```

##ポイント２、ページ ナビゲーションの処理
リンクを開いたときにブラウザが開かないようにします。

```kotlin
 myWebView.webViewClient = WebViewClient()
```
##ポイント、３ウェブページ履歴を操作する
WebView が URL の読み込みをオーバーライドすると、アクセスしたウェブページの履歴が自動的に蓄積されます。履歴を前後に移動するには、goBack() および goForward() を使用します。

```kotlin

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    // Check if the key event was the Back button and if there's history
    if (keyCode == KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {
        myWebView.goBack()
        return true
    }
    // If it wasn't the Back key or there's no web page history, bubble up to the default
    // system behavior (probably exit the activity)
    return super.onKeyDown(keyCode, event)
}
```

以上です。

参考にさせて頂いたサイト
https://developer.android.com/guide/webapps/webview?hl=ja
Rails 5  cloud9で誤って作成したマイグレーションファイルを削除する方法

記事の趣旨　※あくまで個人で開発されている方を対象としています


削除したマイグレーションファイルが生きている
編集してマイグレーションファイルを作成しなおした

対処

![299b83e2ab2fec0c25f32edba537db10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682652/1cce7ad4-71ab-6e8e-9af0-431e8ea62b20.png)

どうしよう。。。。強行手段

１．$ rails db:migrate:status

![a51f3909b9dfe0d66a0e5c910853ba46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682652/1d09f670-b35e-1eac-d235-3986ff63f0ef.png)

１.モデルに作成されたファイルを削除しましょう。(例です)
　
（例）complete.rb　　削除

2.不要なマイグレーションファイル削除

（例）20200729035325_create_complete.rb　削除


補足
削除はファイルを選択し削除してください

![658fb17c679b9dcd39054c5d26cf884a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682652/ef8e75db-7bf2-bc3c-ac8c-33a66b28d1f1.png)

３．再度作り直したマイグレーションファイルを作成

（例）$ rails g model Complete user:references task:references

４．不要なファイルを削除しリセット

$ rails db:migrate:reset

補足　もしそれでもできなければ１～４の手順の後でデータベースをリセットし再度リセット

$ rails db:reset 

$ rails db:migrate:reset


![1ff2a9671ba84f19a7f238ce246b20d0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682652/725bbe45-8489-4eca-9eef-4a41094e7380.png)

No-Fileは削除され必要なファイルが更新されました。

適切な方法は一度不要なファイルを落とし、確認した後に削除されることをお勧めします。
[AWS] Cognitoの基本まとめ

# Cognitoとは
一言で説明すると、ウェブアプリケーションやモバイルアプリケーションに、さくっと認証・認可、ユーザー管理の仕組みを構築できるためのサービス、ということになります。
その中で、

- ユーザープール
- IDプール
- Cognito Sync

などの代表的な機能について、まとめました。

## ユーザープール
公式のドキュメントには「何億人」とまで記載されていますが、それくらい巨大なユーザーを、セキュアに管理できる、フルマネージドサービスのユーザーディレクトリで、認証の機能を提供します。

ユーザーは、

- Google
- Facebook
- Amazon
- Apple

などのソーシャルIDプロバイダーやSAMLベースのIDプロバイダ経由でサインインすることもできます。
プロバイダ経由でサインインする場合でも、ユーザープールのディレクトリプロファイルにアクセスすることができます。
ディレクトリプロファイルには、

- ユーザー名
- 電話番号
- 住所
- タイムゾーン

等、標準的なOpenID Connectベースのプロファイルがサポートされています。

その他の主な機能としては、

- ユーザーがサインインするための組み込みのカスタマイズ可能なウェブUI
- 多要素認証（MFA）などのセキュリティ機能
    - 漏洩した認証情報のチェック
    - アカウントの乗っ取り保護
    - 電話とEメールによる検証
- カスタマイズされたワークフローとLambdaトリガーによるユーザー移行
- Eメールアドレスや電話番号によるサインイン認証
- パスワードポリシーの設定
- 利用デバイスの記憶

などがあります。

## IDプール
ユーザーの一意のIDを作成し、IDプロバイダーで連携させることができるようになります。
IDプールでは、一時的なAWS認証情報を取得することで、各種AWSサービスにアクセスすることが可能となります。
IDプールでは、以下のようなIDプロバイダがサポートされています。

- Login width Amazon
- Facebook
- Google
- Apple
- Twitter
- Digits
- Cognito（ユーザープール）
- Open ID Connect
- SAML ID
- 開発者が認証したID

## Cognito Sync
アプリケーション関連のユーザーデータのデバイス間の同期を有効にする機能です。
クライアントアプリは、データをローカルにキャッシュするため、デバイスがオフラインの状態でもデータへの読み書きが可能になります。
データは、デバイスがオンラインになっている時に同期が行われます。
この時、ユーザーが保持できるデータの最大サイズは20MBで、ユーザー情報ストア内のデータセットには、最大1MBのデータを保存できます。
データセット内に保存できるキーの数は1024個までです。

なお、現在はより高機能なAppSyncが提供されているため、今後使用を検討する場合は、AppSyncの使用を推奨されています。

## 対応言語
Cognitoは、AWS Mobile SDKに対応しています。AWS Mobile SDKでは、

- iOS
- Android
- Unity
- Kindle Fire

がサポートされています。
また、ユーザープールに関しては、JavaScriptによる、JavaScript AWS SDK for CognitoによるJavaScriptでの開発も可能です。
コントロールAPI、データAPIがそれぞれ用意されているため、各種言語からの呼び出しも可能です。

## 料金
リージョンごとで料金が異なりますが、ここでは東京リージョンを例にしたいと思います（2020年7月現在）。

### ユーザープール
ユーザープールの認証情報、または、ソーシャルIDプロバイダーで直接サインインするユーザ数によって決まります。

| 1ヶ月あたりのアクティブユーザー数 | 1ユーザーあたりの料金 |
| :---: | ---: |
| 〜50000 | 無料 |
| 50001〜10万 | 0.0055$(US) |
| 次の90万 | 0.0046$(US) |
| 次の900万 | 0.00325$(US) |
| 1000万超 | 0.0025$(US) |

SAMLまたはOpenID Connectionフェデレーションを使用してサインインする場合も、ユーザー数によって決まります。

| 1ヶ月あたりのアクティブユーザー数 | 1ユーザーあたりの料金 |
| :---: | ---: |
| 〜50 | 無料 |
| 50超 | 0.015$(US) |

さらに、監査モードを含む高度なセキュリティ機能を使用すると、上記に加えて、下記料金が加算されます。

| 1ヶ月あたりのアクティブユーザー数 | 1ユーザーあたりの料金 |
| :---: | ---: |
| 〜50000 | ０．０50$(US) |
| 次の5万 | 0.035$(US) |
| 次の90万 | 0.020$(US) |
| 次の900万 | 0.015$(US) |
| 1000万超 | 0.010$(US) |

### Cognito Sync
こちらもリージョンにより料金が異なりますので、東京リージョンを例にしたいと思います（2020年7月現在）。

#### 無料利用枠
- 最初の12ヶ月間
- 1ヶ月あたり10GBの同期容量
- 1ヶ月あたり100万回の同期オペレーション

無料利用枠外になる場合は、
- 1ヶ月1GBの同期容量あたり 0.19$(US)
- 1ヶ月に1万回の同期オペレーションごと 0.19$(US)

また、プッシュ同期を有効にしているとSNSによる通知が行われるため、別途SNSの料金が加算されます。

## リージョン
Cognitoは、以下のリージョンでサポートされています（2020年7月現在）。

- バージニア北部（us-east-1）
- オハイオ（us-east-2）
- オレゴン（us-west-2）
- ムンバイ（ap-south-1）
- ソウル（ap-northeast-2）
- シンガポール（ap-southeast-1）
- シドニー（ap-southeast-2）
- 東京（ap-northeast-1）
- カナダ（ca-central-1）
- フランクフルト（eu-central-1）
- アイルランド（eu-west-1）
- ロンドン（eu-west-2）

# まとめ
Cognitoは、モバイルアプリケーションだけでなく、ウェブアプリケーションにも使用できるので、一般的なウェブアプリケーションにも、非常に簡単に認証・認可の仕組みを導入することができます。
また、既存のアプリケーションへの導入も比較的簡単に行えます。
次回は、実際にコードを使ったサンプルアプリケーションを作成してみたいと思います。

- [[AWS] ReactアプリでCognitoユーザープール認証を行ってみる](https://qiita.com/herohit-tool/items/7a7e7bf3cbfa5b7ddff6)
Reactにおける配列処理(for文・mapメソッド)

# はじめに
現在Reactを用いて画面開発を行っているのですが、selectタグの中でoptionを使う際、以下のようにコンポーネントに直接記述してしまうと使い回しができなくなってしまうためpropsで値を渡してやりたいと考えました。

```sample.js
<select title="サンプル">
    <option disabled selected>選択してください</option>
    <option value="0">サンプル1</option>
    <option value="1">サンプル2</option>
    <option value="2">サンプル3</option>
    <option value="3">サンプル4</option>
</select>
```
しかし、optionタグ1つ1つをpropsとして値渡しすると数が多くなってしまうため、**propsで配列を受け取りその配列の要素をoptionタグ内に記述して表示してあげよう！**と考えました。

# ①for文での記述

```sample.js
list = []

for(let i=0;i<props.opt.length;i++){
    list.push(<option value={i}>{props.opt[i]}</option>
}

return(
    <select title="サンプル">
        {list}
    </select>
)
```
for文を用いて各配列の要素に変更を加え、それらを別の配列に格納することで実装いたしました。
ちなみに、
`opt=["test","sample",...]`
といった配列データが親コンポーネントから渡されています。

# ②mapでの記述
for文でも実装できましたが、JavaScriptのES6以降ではmapメソッドを用いることでスマートに配列処理を行うことができます。

```sample.js
var list = props.opt; //見やすくするため、受け取った値を変数に格納

const option = list.map((data,index)=><option value={index}>{data}</option>)

return(
    <select title="サンプル">
        {option}
    </select>
)
```
こちらのほうが自分は見やすくおしゃれだなと思いました。

## おまけ：mapメソッドのコールバック関数について
mapメソッドのコールバック関数の引数は**3つ**あります。

```sample.js
配列.map((value,index,array)=> )
```
value：配列の値
index：配列のインデックス番号
array：現在処理している配列

これらを覚えておくと、配列処理をさらにスマートに行えます！

# 最後に
ありふれた内容ですが、開発を行う際に引っかかったため自分用の備忘録として書かせていただきました。始めたばかりの初心者ですのでご指摘があればコメントお願いします。
JupyterHub でカスタムアクションを定義する（hook 機能）

JupyterHub の`hook`機能を使うと、ログイン時にユーザ毎のディレクトリを自動作成できたりする。

設定ファイル（`jupyterhub_config.py`）に関数として定義する。
hook には以下の種類がある。

- `c.JupyterHub.user_redirect_hook`
- `c.Spawner.auth_state_hook`
- `c.Spawner.post_stop_hook`
- `c.Spawner.pre_spawn_hook`
- `c.Authenticator.post_auth_hook`

※それぞれの詳細な説明は割愛（未調査）

公式リポジトリにサンプルスクリプトが紹介されている。

[jupyterhub/examples/bootstrap-script at master · jupyterhub/jupyterhub](https://github.com/jupyterhub/jupyterhub/tree/master/examples/bootstrap-script)

例えばユーザフォルダを自動作成するhookアクションは以下。

```python
# in jupyterhub_config.py  
import os
def create_dir_hook(spawner):
    username = spawner.user.name # get the username
    volume_path = os.path.join('/volumes/jupyterhub', username)
    if not os.path.exists(volume_path):
        # create a directory with umask 0755 
        # hub and container user must have the same UID to be writeable
        # still readable by other users on the system
        os.mkdir(volume_path, 0o755)
        # now do whatever you think your user needs
        # ...
        pass

# attach the hook function to the spawner
c.Spawner.pre_spawn_hook = create_dir_hook
```

※起動ユーザの所有権限になるため、合わせて chown / chmod もした方が良さそう。
Seleniumを使って(半)自動的に出退勤する

#はじめに
会社で利用している打刻システムが少し煩雑だったので自動で打刻するシステムをseleniumを利用して実装した。seleniumの使い方含めて解説していきたい。

社内でもブラウザ操作自動化がホットな話題だったのでその技術のキャッチアップにもなると考え、また一旦こういう技術を覚えてしまえば自動化脳になれるとも考えた。
要は具体的な方法を一度知ってしまえば、あれもこれも自動化してしまえという思考回路が生まれると考えた。技術の世界では本当にこれが大事だと思う。。。

#打刻システム
1 . ブラウザを立ち上げ、office365にログイン
2 . ログイン情報でOAuth連携?して打刻システムにログイン
3 . 打刻システムで数クリックして打刻完了

自分はしょっちゅうブラウザのcookieを消すため、2のログイン時にメールアドレスやパスワードを都度入れるのがかなり手間なので**自動的に打刻ボタンを押したい**

また、昨今のコロナ状況により、**リモートワークの開始・終了連絡のメール送信も自動化したい。**メールはoutlookだが、web版もあるのでseleniumで操作できるはず！

#ところでseleniumとは
簡単に説明するとブラウザの操作を自動化することができるツール。
webdriverというものからブラウザを起動、操作をおこなう。自動操作モードになるので実際のブラウザ操作と全く同じ挙動(cookieの扱い等)をするかは不明。

ブラウザ操作は提供されているAPIを利用することにより、操作したいウインドウやサイト内の要素を取得し、そこに対して「ボタンを押す」や「書き込む」などの操作をこれまたAPIで実行させる。
各種APIコマンド[こちら](https://www.seleniumqref.com/api/webdriver_gyaku.html)


なので、動かすまでのおおまかな流れとしては
1 . 利用したい言語をインストール(pythonとかjavaとか)
2 . seleniumをインストール
3 . 利用したいブラウザに対応したwebdriverインストール
4 . プログラム作成
5 . 実行

となる。
詳細は[こちら](https://mylife8.net/install-selenium-and-run-on-windows/)がわかりやすかった。

これをpythonにて利用し、打刻システムで出勤・退勤ボタンを押すところまでは実装できた。


#outlookで自動メール送信！
僕「すみません、なんかweb版のoutlookつながらないんですけど・・・」

ヘルプデスクさん「私用端末からもアクセスできる機能のため禁止となっております」

僕「ぴえん」

ということで「python outlook 自動化」で検索したところ案外簡単に実装できた。上で作成した打刻システムのpythonコードに下記を追加して完成。詳細はコメント参照。

```sendMail.py
# -*- coding: SJIS -*-
#↑日本語利用する場合はSJISで文字コードセット

#outlookを操作するためのwin32com.client
import win32com.client

#オブジェクト作成
outlook = win32com.client.Dispatch("Outlook.Application")

#オブジェクト登録
mail = outlook.CreateItem(0)

mail.to = 'test1@testmail.com' #宛先
mail.cc = 'test2@testmail.com' #cc
mail.bcc = 'test3@testmail.com' #bcc
mail.subject = '勤怠連絡' #件名
mail.bodyFormat = 3 #メール書式 1:テキスト 2:HTML 3:リッチテキスト
mail.body = '''おはようございます。

リモートワーク開始します。

Qiita-test
'''

mail.Send() #メール送信
```

#終わりに
これで出勤時、退勤時の貴重な1,2分が確保できた。
**定期実行にしてしまうと、
「あれ、あいつ今日休みなのに出勤メール送ってね？」
「退勤メール送った後に仕事してるやん」
ということになりかねない**ので一旦作成したpythonは出退勤時に手動実行、**なので半自動化(笑)。**
PC起動時や何かしらトリガーを設定できたらなぁと思いつつあまり良い考えが浮かばない。。。。

seleniumでできることやできないこと、他のツールでできることなどが少しわかったので自動化作業をするときの大きな助けになりそう^^
接続元IPを判定するRewriteCondでよくやる奴メモ（Proxy考慮）

# 結論
接続元のIPに応じて何かする RewriteCond で ReverseProxy ある場合も考慮した場合のよくやる設定メモ。

```apache
RewriteCond ,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For}, ,1\.1\.1\.1,
RewriteRule .* - [E=special_ip:1]
```

# 解説

- `%｛REMOTE_ADDR｝` に直接の接続元のIPが入ってくる。
- `%{HTTP:X-Forwarder-For}` には ReverseProxy が存在した場合の接続元IPが入ってくる。なければ空文字になる。
- 2つの変数に対して同じIPマッチを `RewriteCond %{変数} ^IP$ [OR]` みたいに `[OR]` で2回書く例を結構見るが、明らかに冗長で対象IPが増えてくると見通しが悪くなる上にIP許可は大抵複数列挙したい事が多いので1行に収めたい。
- あとそもそも `X-Forwarded-For` のことを書いていても複数のIPが入ってくることが考慮されてない設定例をよく目に見るのも気になってた。
- ここで2変数を1つの文字列 `,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For},` に纏めることにする。
    - RewriteCond の第1引数は実はただの文字列なので複数の変数を繋げられるし固定の文字も書ける
    - `%{REMOTE_ADDR}` は常に値が入る
    - `%{HTTP:X-Forwarded-For}` は以下の3種類のパターンがあり、必要な値は一番左に書かれた値である
        - `空文字` これはReverseProxyが無い場合
        - `1.1.1.1` など1つのIP。これはReverseProxyが1段あるケース
        - `1.1.1.1, 10.0.0.11` など2つ以上のIPが `, ` で連結された値。これはReverseProxyが多段の場合で、後段のReverseProxyに対する接続元は右に増えていくことになっている。
    - `,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For},` を考えると以下のようなパターンになる
        - `,1.1.1.1,,` ReverseProxyが無い（1.1.1.1->サーバ）
        - `,10.0.0.11,1.1.1.1,` ReverseProxyが1段ある（1.1.1.1->10.0.0.11->サーバ）
        - `,10.0.0.22,1.1.1.1, 10.0.0.11,` ReverseProxyが2段ある（1.1.1.1->10.0.0.11->10.0.0.22->サーバ）
        - `,10.0.0.33,1.1.1.1, 10.0.0.11, 10.0.0.22,` ReverseProxyが3段ある（1.1.1.1->10.0.0.11->10.0.0.22->10.0.0.33->サーバ）
- 2変数をカンマで連結して更に頭と尻尾にもカンマを付けておくことでReverseProxyの有無や数に関わらず、必要なIPは `,1.1.1.1,` でマッチ出来る事が値の例から分かる。正規表現の `^` と `$` の代わりにカンマを置いとけば部分ではなくちゃんと完全一致になるし、他のReverseProxyのIPと紛れる事もない。
- 複数のIPチェックを列挙するケースが多いと思うがその場合は↓こうなって2行ずつ書くよりは見通しが良くなると思う。

```apache
RewriteCond ,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For}, ,1\.1\.1\.1, [OR]
RewriteCond ,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For}, ,8\.8\.8\.8, [OR]
RewriteCond ,%{REMOTE_ADDR},%{HTTP:X-Forwarded-For}, ,8\.8\.4\.4,
RewriteRule .* - [E=special_ip:1]
```

- `RewriteRule` の第1引数を `.*` にしてるのは「何でもマッチする」の意味です。
    - 昔は `.` って書いてた時期もあったんだけど、httpd.conf に書くならそれでいいけど、`.htaccess` でもコピペで同じ設定が使われることを考慮すると `.htaccess` だとそのディレクトリ自体へのアクセスの場合に RewriteRule のマッチ対象のパス文字列は `空文字` になるので、すなわち `.` だとマッチしなくて意図に反するので `.*` でやってます。
- `RewriteRule` の第2引数は環境変数セットするだけとかの場合はなんにも使われないけど何か書く必要があるので何となく未定義的な意図で `-` を書いてます。なので値が `-` である事に意味はありません。
- `RewriteRule` の第３引数で環境変数をセットする場合は `[E=foo]` とするだけでもデフォルト値として `foo` には `1` がセットされるけど、そのデフォルト値の仕様を知らない人がこの設定を見た場合でも「マッチしたらfooに1が入るんだな」と直感的に分かりやすいよう `[E=foo:1]` と明記してます。

# 注意点
- ユーザのIPを `X-Forwarded-For` ではなく `X-Real-IP` に入れる文化のことも考慮した場合はRewriteCondの第1引数を `,%{REMOTE_ADDR},%{HTTP:X-Real-IP},%{HTTP:X-Forwarded-For},` とかにしといてやれば同じ理屈で上手く動く筈です。
- `X-Forwarded-For` や `X-Real-IP` などのヘッダはユーザが勝手に詐称することも可能なので、ReverseProxy は信頼できない接続元からこれらヘッダが送られてきた場合は捨てるようになっている事は前提条件です。その部分の詐称を考慮するのはバックエンドサーバの責務じゃないので。
PCA（主成分分析）をして累積率、累積寄与率を表示する

# 主成分分析
## 効果
主成分分析することで次元を減らせる。
## 手順
### 1. データ全体を使って適切な圧縮次元数を見極める


```python:pca
#データ全体にpca
from sklearn.decomposition import PCA
pca = PCA()
pca.fit(X)
```

```python:pca
#寄与率のグラフ
plt.bar([n for n in range(1, len(pca.explained_variance_ratio_)+1)], pca.explained_variance_ratio_)
```

```python:pca
#累積寄与率のグラフ
ev_ratio = pca.explained_variance_ratio_
ev_ratio = np.hstack([0,ev_ratio.cumsum()])
plt.plot(ev_ratio)
plt.show()
```
累積寄与率が0.7~0.9くらいの次元数を確認する。
### 2. trainとtestデータに次元圧縮

```python:pca
from sklearn.decomposition import PCA
pca = PCA(n_components = <任意の次元数>)
X_train_pca = pca.fit_transform(X_train)
X_test_pca = pca.transform(X_test)
```
【CSS】文字装飾

##文字の間隔をあける
```css
p {
    letter-spacing: 0.1em
}

（おすすめは0.05em~0.1em)
```
##文字に下線を引く
```css
P { 
  text-decoration: underline
}
```
色々な種類の下線がある
https://saruwakakun.com/html-css/reference/border

##取り消し線
```css
P { 
  text-decoration: line-through
}
```
##線で囲む
```css
P {
   border: solid 線の太さ 色 
}
```
borderの後に、線の種類・線の太さ・色を指定する
情報を生み出す考え方～経験主義と仮説思考・システム思考～

# 概要

エンジニアが解決する必要のある仕事上の問題は、当初からはっきり与えられていることは少ない。このため、「与えられた問題をどうやって解いたらいいかを考える」学力テストのような方法論で解決を試みても、実際にはなかなか解決することができない。

書籍「[エンジニアリング組織論への招待～不確実性に向き合う思考と組織のリファクタリング](https://gihyo.jp/book/2018/978-4-7741-9605-3)」では、そのような問題に対応する「情報を生み出す考え方」を説明している。

本記事では、書籍の内容を引用した上で、例題を通して、その実践例を示す。

# 情報を生み出す考え方

## 仕事と学力テストの3つの違い

||学力テスト|仕事|
|---|---|---|
|人数|1人|複数人|
|情報|問題に書いてある|必ずしもあるわけではない|
|答え|決まっている|決まっていない(問題を設定することからはじまる)|

学力テストの場合は、聞かれていることに答えればよい。また、与えられた情報を読み込めば、必ずどこかに解決への鍵がある。その鍵を発見することができれば、そこから解答を自分1人の力で導くことができる。私たちは、これまでの学習過程で、無意識のうちに問題をそのような方法で解く習慣が身についてきてしまっている。

しかし、エンジニアが直面する仕事上の問題の場合、当初問題だと思われたものが実際には誤っている（本当に解決すべき問題は別のところにある）ことがある。また、当初の情報内には解決への鍵が必ずしも存在しているわけではない。では、どのようにして問題を解決すべきなのだろうか。

## 経験主義と仮説思考

解決のための考え方は、経験主義と仮説思考である。

<dl>
  <dt>経験主義</dt>
  <dd>情報を入手するために、行動を起こして、その結果を観察し、そこから問題解決を行う考え方</dd>
  <dt>仮説思考</dt>
  <dd>限定された情報であっても、その情報から全体像を想定し、それを確かめることで少ない情報から問題解決に向かう思考様式</dd>
</dl>

「仕事と学力テストの3つの違い」に記載した通り、情報は必ずしも十分に存在するわけではない。このため、まずは情報の不足を補うための行動をする必要がある。

また、仮説を立てることで、すべての情報が揃っていなくても、早く問題を解決することができる。

# 例題

では、実際に具体的な例を挙げて考えてみよう。

> あなたは会社の情報システム部門に所属している。
> ある社員から「自席でふだん利用しているPCから、インターネットにつながらなくなった」という問い合わせがあった。
> さて、どのように解決すればよいか？

## 不足している情報は何か？

「インターネットにつながらなくなった」という文が指す問題は曖昧である。
受け取り手によって、そのイメージはバラバラになってしまう。

このため、まずはどのような情報が不足しているために、問題が曖昧になってしまっているのかを考えてみよう。

たとえば、下記のような例が挙げられるはず。自分で考えて、もっとたくさん挙げてみよう。


- いつから発生しているのか。発生する前には何をしたのか
- 周りの人も同じような問題が発生しているのか

不足している情報がわかったら、実際に行動を起こして情報を入手しよう。
たとえば、それらの不明点を問い合わせ元の社員に質問したり、実際に現場に行って自分で確かめたりできる。

## 新たに入手した情報から仮説を立てよう

行動した結果、下記のような情報を新しく手に入れることができた。

- 問題が発生しているのは、特定のWebサイトだけではない
- 最近、ソフトウェアを新しくインストールした
- 周りのPCでは問題が発生していない

その情報から、問題の原因に対する仮説を立ててみよう。
たとえば、下記のような仮説が立てられるはずである。

- 新しくインストールしたソフトウェアに問題があるのでは？
- 単にLANケーブルが抜けているだけでは？

仮説を立てることができれば、また、その仮説を行動によって確かめればよい。

## システム思考

このように、仕事上で直面する問題においては、学力テストと違って、一回で正解を導き出す必要はない。時間と資源の制約の中で、より正解に近づく一手を打ち続けることこそが重要である。

結果的には、そのほうが短時間で解決することができるだろう。

ほかの例: [仮説を検証するために、小さなコードを書いて結果を確かめよう](https://qiita.com/flyaway/items/9910cecc1ac8ed2f1b75)

# 参考書籍

本記事の内容は、[エンジニアリング組織論への招待～不確実性に向き合う思考と組織のリファクタリング](https://gihyo.jp/book/2018/978-4-7741-9605-3)の「1-3. 情報を生み出す考え方」を引用した上で、例を挙げての解説を試みたものです。

本書はきちんと順を追って話を運んでくれるので、新人エンジニアから組織の長まで、幅広いエンジニアに対しておすすめです。

[エンジニアリング組織論への招待：リファレンスガイド第１章/第２章](https://qiita.com/hirokidaichi/items/195d42ee056ea85a3150)も参考にどうぞ。
GitHub Actions で Android アプリを手動ビルドできるようにする

下記のようなyamlファイルをAndroidプロジェクトのリポジトリに置きます。

```yaml:.github/workflows/manual-build.yml

name: Manual build

on:
  workflow_dispatch:
    inputs:
      variant:
        description: 'Build variant'
        required: true
      ref:
        description: 'branch name|tag name|commit SHA'
        required: true
        default: master

jobs:
  build:
    name: Build
    runs-on: ubuntu-18.04
    steps:
      - name: Check out
        uses: actions/checkout@v2
        with:
          ref: ${{ github.event.inputs.ref }}
      - name: Set up JDK
        uses: actions/setup-java@v1
        with:
          java-version: 1.8
      - name: Restore gradle cache
        uses: actions/cache@v2
        with:
          path: ~/.gradle
          key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }}-${{ hashFiles('**/*.gradle.kts') }}
      - name: Run Android Lint
        run: ./gradlew lint${{ github.event.inputs.variant }}
      - name: Run unit test
        run: ./gradlew test${{ github.event.inputs.variant }}UnitTest
      - name: Build with Gradle
        run: |
          # 署名まわり。https://qiita.com/hkusu/items/cadb572c979c4d729567 を参照
          if [[ -n '${{ secrets.KEYSTORE_BASE64 }}' ]]; then
            echo '${{ secrets.KEYSTORE_BASE64 }}' | base64 -d > release.keystore
            export KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}'
            export KEY_ALIAS='${{ secrets.KEY_ALIAS }}'
            export KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
          fi
          ./gradlew assemble${{ github.event.inputs.variant }}
      - name: Get apk path
        id: apk-path
        run: |
          path=$(find **/build/outputs/apk -name '*.apk' -type f | head -1)
          echo "::set-output name=path::$path"
      - name: Upload apk file
        uses: actions/upload-artifact@v2
        with:
          name: apk
          path: ${{ steps.apk-path.outputs.path }}
```

そうすると、下図のように GitHub の画面からワークフローを実行してアプリがビルドできるようになります。

<img width="800" alt="スクリーンショット_2020-07-29_13_03_35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35733/747f183e-0dfe-8bbe-0a73-543d437b49fb.png">

apk ファイルは、下図のように結果画面からダウンロードできます。

<img width="800" alt="スクリーンショット_2020-07-29_13_06_24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35733/bab69a0e-37b9-bf2e-b276-70d4cf066faf.png">
競技プログラミング練習記 No.58【ABC105練習】

初回 -> [はじめに](https://qiita.com/easychoco/items/a9ae28aca97cfe47091f)

[ABC105](https://atcoder.jp/contests/abc105)を解きました。
4完です！
C問題は解説を見て解きました。

|問題|時間|アルゴリズム|実行時間
|:-:|:-:|:-:|:-:|
| A |  2min |-| 8ms|
| B |  4min |-| 6ms|
| C | - |余り| 5ms|
| D | - |累積和| 33ms|

# まとめ
先にまとめを載せます。これ自体は最後に書いてます。

今回はC問題が難しくて解けなかったです。悔しい。
とても勉強になりました。

負の値を割るときは切り捨てではなく切り上げになるので、進数表現をするときは注意です。

D問題は[Zero-Sum Ranges](https://atcoder.jp/contests/agc023/tasks/agc023_a)という有名な問題に似ているので、その考え方を使いました。

`std::map`は連想配列や`hashmap`と似た構造です。
`key`と`value`があって、`key`を自由に設定できます。
内部的には平衡二分探索木で実装されています。



## A - AtCoder Crackers
```c++
  int n, k;
  cin >> n >> k;
  cout << (n % k > 0 ? 1 : 0);
```
均等に分けようとしたときの最大の差は`1`になりますね。
差が`2`あるときは、多く分けられた人が少ない人に1つ渡せば差がなくなります。
また、`N`が`K`で割り切れるときは差は`0`になります。

## B - Cakes and Donuts
``` c++
  int n;
  cin >> n;
  for(int i = 0; n - i * 7 >= 0; ++i)
  {
    if ((n - i * 7) % 4 == 0)
    {
      cout << "Yes";
      return;
    }
  }
  cout << "No";
```
`7`の個数を変えながら、条件を満たす`4`の個数があるかを探索しました。
つるかめ算みたいに数学的に解けると思うのですが、思いつかなかったので愚直に。

## C - Base -2 Number

``` c++
  int n;
  cin >> n;
  string ans = ((n == 0) ? "0" : "");
  while(n != 0)
  {
    ans += (n % 2 == 0 ? '0' : '1');
    n -= (n % 2 == 0 ? 0 : 1);
    n /= -2;
  }
  reverse(ans.begin(), ans.end());
  cout << ans;
```
難しかった。思い込みにやられました。

基本的な考え方は10進数を`N`進数に直すときと同じです。
つまり、`N`で割りながら余りを記憶するという処理です。

私は`N`進数に直すときは割り算が切り捨てられることを利用してひたすら`N`で割り続けるのですが、今回はそれが使えません。
というのも、負の値を割ったときは切り捨てではなく切り上げになるためです。

例えば、`-9`を`2`で割ったときは切り捨てられて`-5`になってほしいのですが、プログラムでは`-4`になります。
これは当然といえば当然なのですが、この問題の場合はそれだと困ります。

これを防ぐためにはあまりを引く必要があります。
この部分ですね。
`n -= (n % 2 == 0 ? 0 : 1);`
こうすることで桁がそろい、想定通りの結果になります。

## D - Candy Distribution
``` c++
  ll n, m;
  cin >> n >> m;
  vector<ll> a(n);
  for(ll i = 0; i < n; ++i) cin >> a[i];
  vector<ll> sum(a);
  for(ll i = 1; i < n; ++i) sum[i] += sum[i - 1];
 
  map<ll, ll> mp;
  for(const auto& v : sum) mp[v % m]++;

  ll ans = mp[0];
  for(const auto& m : mp)
  {
    ll v = m.second;
    ans += v * (v - 1) / 2;
  }
  cout << ans;
```
C問題に比べると簡単でした。(※個人差があります。)
[Zero-Sum Ranges](https://atcoder.jp/contests/agc023/tasks/agc023_a)という有名な問題に似ているので、その考え方を使いました。

部分和が`M`の倍数になってほしいので、部分和を考えます。
部分和を求めるには累積和が使えますね。
ただ、部分和を求める解き方だと$O(N^2)$となり、今回は制約より時間内に解けません。

ここで知りたいのは部分和が`M`の倍数かどうかです。
累積和の差が部分和なので、累積和の差が`M`の倍数なことと部分和が`M`の倍数なことは同値です。

倍数なのでMODを考えます。
MODの世界では加算と減算が自由にできます。
つまり、累積和にMODを適用することと部分和にMODを適用することは同じですね。
この時、部分和が`M`の倍数ということはMODを適用した場合には部分和が`0`になります。
言い換えると、累積和の値が同じになるということだとわかります。

以上より、累積和にMODを適用したとき、値が同じ2数を選んで作る部分和は`M`の倍数なことがわかります。
あとはその組み合わせを数え上げれば答えがわかります。

ここまでわかれば実装です。
今回は累積和のMODが取りうる範囲がとても広いので、配列は使えません。
そのかわりに`std::map`を使いました。
`std::map`は連想配列や`hashmap`と似たデータ構造ですね。
`key`と`value`があって、`key`を自由に設定できます。
内部的には平衡二分探索木で実装されています。



以上です。ありがとうございました。
Kotlinから使えないJavaコード（社内勉強会用）

- 概要
    - 業務でハマった問題について発表します
    - 実際のところ私もよく分ってないので、ぜひ分かる方はコメント下さい
- 前提
    - Android Kotlin/JVMで、Java製のLibraryをKotlinから利用

---

# 実際にハマったコード

```java:Desk.java
package library_package;

abstract class BaseDesk {
    public enum Color { BLUE, ORANGE }
}

public class Desk extends BaseDesk {
    private Color color;

    public Desk(Color color) {
        this.color = color;
    }
}
```

こういうJava実装を、Javaから使うのは、当然うまくいく。

```java:java
new Desk(Desk.Color.BLUE);
```

Kotlinから使うと、Compile Errorになる。

```kotlin:kotlin
Desk(Desk.Color.BLUE) // Unresolved reference: Color
```

```kotlin:kotlin
import library_package.BaseDesk // Cannot access 'BaseDesk': it is package-private in 'library_package'

Desk(BaseDesk.Color.BLUE) // Cannot access 'Color': it is public in 'BaseDesk'
```

---

# どう回避したか

この問題を解決するために（だろうか？） `getColorEnum()` という static method が用意されていた。

```java:Desk.java
package library_package;

abstract class BaseDesk {
    public enum Color { BLUE, ORANGE }

    public static Color getColorEnum(String name) {
        return Color.valueOf(name);
    }
}
```

```kotlin:kotlin
@Suppress("INACCESSIBLE_TYPE") // suppress warning は必要だが
Desk(Desk.getColorEnum("BLUE")) // DeskコンストラクタにColorインスタンスを入れることが出来た
```

---

# なぜエラーになったり／ならなかったり？

どういうケースでエラーとなるのか見てみると...

```java:Foo.java
class BaseFoo {
    public static int i = 0;

    public static int inc() {
        return ++i;
    }

    public static class FooFoo {
        public static int j = 0;
    }
}

public class Foo extends BaseFoo {}
```

```java:java
int foo_i = Foo.i;
int foo_inc = Foo.inc();
int foo_foo_j = Foo.FooFoo.j;
```

```kotlin:kotlin
val foo_i = Foo.i
val foo_inc = Foo.inc()
val foo_foo_j: Int = Foo.FooFoo.j // Unresolved reference: FooFoo
```

Kotlinでは、super class の static inner class にアクセスできないようだ。
根拠となる言語仕様を軽く調べてみたが、わからなかった。

---

# なぜメソッドの戻り値としては使えたのか？

Javaは、もともと不可視なクラスを戻り値に定義できるようだ。

```java
public class Bar {
    public static InternalBar getBar() {
        return new InternalBar();
    }

    private static class InternalBar extends Bar {}
}

Bar bar = Bar.getBar(); // OK
```

Kotlinでは、こんな事はできない。

```kotlin
open class Baz {
    companion object {
        fun getInternalBaz(): InternalBaz { // 'public' function exposes its 'private' return type InternalBaz
            return InternalBaz()
        }

        fun getBaz(): Baz { // OK
            return InternalBaz()
        }
    }
    private class InternalBaz : Baz()
}
```

---

# INACCESSIBLE_TYPE warning とは？

参照できない場合にエラーとするKotlin Compilerの挙動が、前述のようなJavaの仕様と上手く合致せずにコンパイルできない問題があり、いまはCompile ErrorではなくWarningにしているが、今後適切に、本当にアクセス不可な場合だけコンパイルエラーとするように再変更する予定、ということらしい。

https://youtrack.jetbrains.com/issue/KT-11398 を参照。

---

# もし Library 側に getColorEnum() が用意されてなかったら？

自分でJavaでUtility Methodを作ればよい。

```java:DeskColorResolver.java
import library_package.Desk;

public class DeskColorResolver {
    public static Desk.Color getColor(String name) {
        return Desk.Color.valueOf(name);
    }
}
```


```kotlin:kotlin
val desk = Desk(DeskColorResolver.getColorEnum("BLUE"))
```

と書ける。
本当にこれが正解だろうか...?

---

# thanks!
WRFコンパイル時に出た失敗と解決法一覧

#WRF本体のコンパイル
##configure時
### configureが上手く通らない 
####WRFv4.2.1 on REDHAD（スパコン）
#####参考URL
WRF setup memo (hysk) http://www.hysk.sakura.ne.jp/meteorol/WRF_setup

毎回早崎さんのHPを見てしまう。

```:./configureすると
------------------------------------------------------------------------
Settings listed above are written to configure.wrf.
If you wish to change settings, please edit that file.
If you wish to change the default options, edit the file:
     arch/configure.defaults
NetCDF users note:
 This installation of NetCDF supports large file support.  To DISABLE large file
 support in NetCDF, set the environment variable WRFIO_NCD_NO_LARGE_FILE_SUPPORT
 to 1 and run configure again. Set to any other value to avoid this message.
  

Testing for NetCDF, C and Fortran compiler

  One of compilers testing failed!
  Please check your compiler
```

serialでもdmでもsmでもdm+smでも通らない。
MacOSだと引っかかることはないので、どうやらOSによって違うのかもしれないが、日本語環境のままだとこのfailedから抜け出せない。

```:terminal
$ echo $LANG
ja_JP.UTF-8
$ LANG=C
$ echo $LANG
C
```

echoして日本語になっていたら、Cにしよう。
その後configureをし直す。

```:./configure
------------------------------------------------------------------------
Settings listed above are written to configure.wrf.
If you wish to change settings, please edit that file.
If you wish to change the default options, edit the file:
     arch/configure.defaults
NetCDF users note:
 This installation of NetCDF supports large file support.  To DISABLE large file
 support in NetCDF, set the environment variable WRFIO_NCD_NO_LARGE_FILE_SUPPORT
 to 1 and run configure again. Set to any other value to avoid this message.
  

Testing for NetCDF, C and Fortran compiler

This installation of NetCDF is 64-bit
                 C compiler is 64-bit
           Fortran compiler is 64-bit
              It will build in 64-bit

standard location of RPC
*****************************************************************************
This build of WRF will use classic (non-compressed) NETCDF format
*****************************************************************************
```

上手く行った。うん。良かった。
Seleniumでchromedriverのエラーが出るとき

Seleniumでクローリング環境を構築するとき、下記のようなエラーが出ました。Mac環境です。

```
selenium.common.exceptions.WebDriverException: Message: ‘chromedriver’ executable needs to be in PATH. Please see https://sites.google.com/a/chromium.org/chromedriver/home
```

パスが通ってないよ、ということらしい。
chromedriverが保存されている場所を指定してみる。

```
webdriver.Chrome(executable_path='Chromedriverがあるパス')
```

JupyterLab 上では動いたけど、`.py`にしてテストしてみたらダメでした。
で
[chromedriver-binary](https://pypi.org/project/chromedriver-binary/)をインストールする。

```
pip install chromedriver_binary==chromedriverのバージョン
```

JupyterLab上では接頭に「!」をつけてこのように↓

```
!pip install chromedriver_binary==chromedriverのバージョン
```
確認してみる。

```
from selenium import webdriver
import chromedriver_binary
driver = webdriver.Chrome()
```
で、Chromeが立ち上がればとりあえずOK。

chromedriverは、現在使っているChromeのbuildに応じたものをインストールします。
例えばChromeが`84.0.4147.105`であれば`ChromeDriver 84.0.4147.30`という感じ。

buildっていうかメジャーバージョンが違うと、
`SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 85`
みたいなエラーが出て動作しません。
Lambda(node.js)とJavaScriptのタイムゾーン

Lambda(node.js)で日付の計算をしたら思ったより面倒だったのでまとめる。

# やりたかったこと

日本標準時(JST)で先週/先月の範囲を、世界標準時(UTC)で取得したい。
例えば、今日が 2020-07-29 とすると先週の範囲は日曜日始まりでこんな感じ。

| タイムゾーン | 開始 | 終了 |
|:-:|:--|:--|
| JST | 2020-07-19T00:00:00+0900 | 2020-07-26T00:00:00+0900 |
| UTC | 2020-07-18T15:00:00Z | 2020-07-25T15:00:00Z |

# やりたくなかったこと

大した処理じゃないので余計なライブラリは使いたくない。

# 結論

- タイムゾーンの設定はそのままにプログラム内で対応する
- タイムゾーンオフセット値からJSTを自力で設定する
- UTCへの変換も自力でやる

# それぞれのタイムゾーン

## Lambdaのタイムゾーン

UTCに固定されている模様。

ググるとTZ環境変数を設定すればいいという記事が大量にヒットする。
ただ、TZ環境変数はAWSに予約されているので、それを変更するのはよろしくなさそう。
[Lambda のタイムゾーンを環境変数TZで指定してはいけないっていう話 | Serverworks ENGINEER  BLOG](http://blog.serverworks.co.jp/tech/2019/10/30/lambda-timezone-2/)

他の利用しているAWSサービスでUTCで動作するものもあったので、プログラム内で対応することにした。

## JavaScriptのタイムゾーン

JavaScriptのDateクラスにはタイムゾーンを設定する方法はないようだった。
ただ `getTimezoneOffset` で分単位の時差は取得できた。JSTは+9時間なので、なぜか-540分が返ってくる。取得した値を+しとけばUTCに変更できそう。

`toLocaleString` でタイムゾーン指定の日付文字列を取得して、新しくDateオブジェクトを作る方法もあるようだが、Lambdaでは動作しないのとDateクラスのコンストラクタに日付文字列を渡すのは非推奨だったのでやめた。

最終的にJavaScriptのタイムゾーンは無視して `getTimezoneOffset` を使って自力で日時を計算することにした。

## オレのMacのタイムゾーン

JSTだった。
AWS SAMを使ってローカルで開発していると、ローカルではJSTでLambda上ではUTCになる。
タイムゾーンを変更してテストしたい場合は、TZ環境変数を設定することで変更できる。

# 実装

## 日本標準時で現在日時を取得

考慮するタイムゾーンはJSTかUTCの2つという前提で、タイムゾーン設定を無視して日本標準時での現在日時を取得することにした。

```js
const jstOffset = 9 * 60;
const now = new Date();
const offset = now.getTimezoneOffset() + jstOffset;
now.setTime(date.getTime() + offset * 60 * 1000);
```

## 先週の開始/終了日時を取得

日本標準時で先週の日曜日を取得。

```js
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
start.setDate(start.getDate() - 7 - start.getDay());
```

同様に今週の日曜日を取得。

```js
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate());
end.setDate(end.getDate() - end.getDay());
```

## 先月の開始/終了日時を取得

先月1日を取得。

```js
const start = new Date(now.getFullYear(), now.getMonth() - 1, 1);
```

今月1日を取得。

```js
const end = new Date(now.getFullYear(), now.getMonth(), 1);
```

## JST→UTCの変換

それぞれの日付をUTCに変換。
実際はタイムゾーンを変換してないので `getUTCFULLYear` などは使えない。

```js
const jstOffset = 9 * 60;
date.setTime(date.getTime() - jstOffset * 60 * 1000);

date.getFullYear();
date.getMonth() + 1;
date.getDate();
date.getHours();
```