OmniAICreator/Qiita-1.07M huggingface dataset:
【Vagrant】【Mac×CentOS7】ホストOSとゲストOS間での共有フォルダ設定

## 環境
|OS - software|version|
|---|---|
|Mac(ホスト)|High Sierra 10.13.6|
|CentOS(ゲスト)|7.5.1804|
|vagrant|2.1.2|
|vagrant-vbguest |0.15.2|

## vagrant-vbguestインストール

ホストOS(Mac)とゲストOS(CentOS)間でファイルを共有する為に、`vagrant-vbguest`プラグインを入れます。

`vagrant-vbguest`は、
VirtualBox と box にインストールされている Guest Additions のバージョンが違う場合に、VirtualBox のバージョンに合わせて最適化してくれるプラグインです。

vagrantのプラグインのインストールは、
vagrant plugin install [プラグイン名]で行います。

```
$ vagrant plugin install vagrant-vbguest
```
## Guest Additionsのアップデート
まずは、`vagrant status`でゲストOSの状態を確認し、起動していなければ、`vagrant up`で立ち上げてからアップデートを実行してください。

ホストOS側で、以下コマンドを実行します。

```
$ vagrant vbguest
```

#### ステータスを確認

```
$ vagrant vbguest --status
```
マッチしている時：
GuestAdditions 5.0.16 running --- OK.


## フォルダの同期設定とGuest Additionsの更新無効設定（Vagrantfile編集）

#### 同期設定
Vagrantfileは、`config.vm.synced_folder`を以下のように追加します。

```
Vagrant.configure("2") do |config|
~~省略~~
  config.vm.synced_folder "/Users/ユーザー名/work", "/home/vagrant/vagrant_data",create:"true"
end
```

- ホスト側のフォルダ：/Users/ユーザー名/work
- ゲスト側のフォルダ：/home/vagrant/vagrant_data
- オプション：「create:"true」でフォルダが存在していない場合に作成


#### Guest Additionsの更新無効設定

続けて、グローバルのVagrantfile（~/.vagrant.d/Vagrantfile）に更新無効の設定を行います。

※デフォルトの設定では、`vagrant up`の際にGuest Additionsが自動的にアップデートされます。起動時にゲストOSだけアップデートされるとバージョン不一致でエラーが発生するので、更新は無効にしておきます。


```
Vagrant.configure("2") do |config|
~~省略~~
  # 起動時のGuestAdditions自動アップデート設定
  config.vbguest.auto_update = false
  # isoファイルをダウンロードしない
  config.vbguest.no_remote = true
end
```

## 動作確認
今回行った共有設定ができているか確認します。

ホスト側：/Users/ユーザー名/work
ゲスト側：/home/vagrant/vagrant_data

```
$ # ゲストOSの再起動とステータス確認
$ vagrant reload && vagrant status
$
$ # runningが出ていることを確認し、ssh
$ vagrant ssh
```

#### ゲスト側で作成したものがホスト側に反映されるか
```
[vagrant]$ pwd
[vagrant]$ /home/vagrant
[vagrant]$ cd vagrant_data
[vagrant]$ touch test_file.txt
[vagrant]$ mkdir test_dir
[vagrant]$ ls -l
```
ホスト側の/Users/ユーザー名/work内に「test_file.txt」と「test_dir」が作成されていればOK

#### ホスト側で削除したものがゲスト側に反映されるか
```
$ cd /Users/ユーザー名/work
$ rm test_file.txt
$ rm -r test_dir
$ ls -l
```

ゲスト側の/home/vagrant/vagrant_dataに存在していた「test_file.txt」と「test_dir」が無くなっていれば、OK
pythonで並行処理を実行させて、それぞれの結果の値を取得する

普段はよくDjangoを使っているものの、Webサーバの中で並行処理させることも早々ないんですが。
バッチ処理だったりなんだったりで、たまーに並行処理が必要になってくることもあります。
そんな時のために備忘録もかねて。

# Threadクラスの拡張！

```python
from threading import Thread

class ThreadWithReturnValue(Thread):
    def __init__(
            self,
            group=None,
            target=None,
            name=None,
            args=(),
            kwargs=None,
            verbose=None
    ):
        Thread.__init__(self, group, target, name, args, kwargs, verbose)
        self._return = None

    def run(self):
        if self._Thread__target is not None:
            self._return = self._Thread__target(
                *self._Thread__args,
                **self._Thread__kwargs
            )

    def join(self, timeout=None):
        Thread.join(self, timeout)
        return self._return
```

というクラスを用意してあげます。
privateな変数を触りまくっているので、少しお行儀の悪いソースではあるんですが。
run関数を上書きして、そこで実行結果を取るようにしてあげます。

# Threadを実行する関数の用意！

```python
def run_thread(exec_func_list):
    thread_list = []
    for exec_func in exec_func_list:
        t = ThreadWithReturnValue(target=exec_func)
        t.start()
        thread_list.append(t)

    return map(lambda x: x.join(), thread_list)
```

非同期で実行したい関数を用意して、この関数の中に入れてあげます。
ThreadWithReturnValueのクラスを作って起動した後に、実行結果をjoinで待っているので、渡した配列の順序通りに結果が帰ってくる想定です！

# いざ実行

```python
from time import sleep


def func1():
    sleep(1)
    return 1


def func2():
    sleep(2)
    return 2


def func3():
    sleep(3)
    return 3


def func4():
    sleep(4)
    return 4


print run_thread([func1, func2, func3, func4])
print run_thread([func4, func3, func2, func1])
```

```terminal
$ python hoge.py 
[1, 2, 3, 4]
[4, 3, 2, 1]
```

ちゃんと帰ってきました:joy_cat:
実際には、各関数の中で複雑な処理を実行させてあげれば！

# 最後に
[株式会社ネコカリ](https://www.nekokari.co.jp/)では猫の手も借りたい🔥炎上中🔥なお仕事を募集しています！
一緒に働くメンバーも募集していますので、よかったら是非！
Cognos Analytics ユーザー認証情報を含むレポート呼び出しURL

#Cognos Analyticsでのユーザー認証を含むレポート呼び出しURL

CognosでレポートをURLで呼び出す際に、URLに「ユーザーID」や「パスワード」を埋め込む事で、認証とレポート呼び出しを一気に行う事が可能です。

Cognos BIでは割と簡単だったのですが、Cognos Analyticsでは以下の様な手順が必要ですので、投稿しておきます。

#ポータル画面を呼び出すだけのURL
まず始めに、Cognos Analyticsのポータル画面を呼び出すだけであれば、以下のURLでできます。
　CognosHostnameというCognosノード
　ApacheDSというネームスペース
　cogadminというユーザー
　passwordというパスワードを指定しています。

```html
http://CognosHostname:9300/bi/v1/disp?h_CAM_action=logonAs&CAMNamespace=ApacheDS&CAMUsername=cogadmin&CAMPassword=password
```

#ユーザーIDとパスワードを指定して、レポートを呼び出す場合です
初めに、以下のTechnoteを参照して、ConfigSetterでGlass.urlLoginParametersの設定を行います。
**ConfigSetterユーティリティーを使用してCognos Analyticsの設定を行なう方法**
http://www-01.ibm.com/support/docview.wss?uid=swg22014072

*ConfigSetterの使い方*
1. ブラウザーからCognos Anlyticsにシステム管理者でログオンします。
2. 同じブラウザーから、下記にアクセスします。
http://<サーバー名>:<ポート>/bi/utils/ConfigSetter.html
3. 設定したいプロパティのNameとValueを入力します。
4. Submitをクリックします。

Name: Glass.urlLoginParameters
Value: CAMUsername, CAMPassword,

**Cognos Analytics 11.1.5での設定（2020/1/14追記）**
Cognos Analytics 11.1.5で設定しようとしたところ、上記のURLではなく、「管理」→「構成」→「システム」→「セキュリティー」→「URL内に指定できるログイン・パラメーター」に「CAMUsername, CAMPassword,」を記載し「適用」する事でできました。
<img width="407" alt="001.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147246/9e034310-17e7-8eac-4029-25e850e6c51b.png">


設定は10秒で再起動不要で反映されるので、反映後、以下のURLで認証&レポート呼び出し可能です。
　CognosHostnameというCognosノード
　ApacheDSというネームスペース
　cogadminというユーザー
　passwordというパスワード
　共有コンテンツ > TESTフォルダ > URLReportというレポートを呼び出しています。

```html
http://CognosHostname:9300/bi/v1/disp?h_CAM_action=logonAs&CAMNamespace=ApacheDS&CAMUsername=cogadmin&CAMPassword=password&pathRef=.public_folders%2FTEST%2FURLReport&format=HTML&Download=false&prompt=false
```

環境は、Cognos Analytics 11.0.11でテストしました。
これを使って、外部アプリケーションからのCognosレポート呼び出しとか、レスポンス応答監視ができる様になると思いますのでご活用下さい。
restTemplate template

```java
        // create restTemplate
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        InetSocketAddress address = new InetSocketAddress(proxySettings.getHost(), proxySettings.getPort());
        Proxy proxy = new Proxy(Proxy.Type.HTTP, address);
        factory.setProxy(proxy);
        RestTemplate restTemplate = restTemplateBuilder.requestFactory(factory).build();

        // set headers
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("User-Agent", "hogeclient");
        httpHeaders.add("Content-Type", "application/json; charset=utf-8");

        // set body as JSON
        ObjectMapper mapper = new ObjectMapper();
        ObjectNode objectNode = mapper.createObjectNode();
        objectNode.put("key", body);

        // set uri
        try {
            uri = new URI("http://hoge.io/");
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        // create requestEntity
        RequestEntity<?> requestEntity = new RequestEntity<>(httpHeaders, HttpMethod.GET, uri);

        // request
        ResponseEntity<?> responseEntity = restTemplate.exchange(requestEntity, String.class);
```

# 参考
* https://github.com/FasterXML/jackson-databind
* http://blog.kymmt.com/entry/jackson
sklearnの交差検証の種類とその動作

sklearnで交差検証をする時に使う`KFold`，`StratifiedKFold`，`ShuffleSplit`のそれぞれの動作について簡単にまとめ

## KFold（K-分割交差検証）
#### 概要
- データをk個に分け，n個を訓練用，k-n個をテスト用として使う．
- 分けられたn個のデータがテスト用として必ず1回使われるようにn回検定する．

#### オプション(引数)
- n_split：データの分割数．つまりk．検定はここで指定した数値の回数おこなわれる．
- shuffle：Trueなら連続する数字でグループ分けせず，ランダムにデータを選択する．
- random_state：乱数のシードを指定できる．

#### 例
```python
import numpy as np
from sklearn.model_selection import KFold

x = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4]])
y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 1, 1])

kf = KFold(n_splits=5)
for train_index, test_index in kf.split(x, y):
    print("train_index:", train_index, "test_index:", test_index)

# output
# train_index: [2 3 4 5 6 7 8 9] test_index: [0 1]
# train_index: [0 1 4 5 6 7 8 9] test_index: [2 3]
# train_index: [0 1 2 3 6 7 8 9] test_index: [4 5]
# train_index: [0 1 2 3 4 5 8 9] test_index: [6 7]
# train_index: [0 1 2 3 4 5 6 7] test_index: [8 9]
```

## StratifiedKFold（層状K分割）
#### 概要
- 分布に大きな不均衡がある場合に用いるKFold．
- 分布の比率を維持したままデータを訓練用とテスト用に分割する．

#### オプション(引数)
- KFoldと同じ．
- n_splitがデータ数が最も少ないクラスのデータ数よりも多いと怒られる．

#### 例
```python
import numpy as np
from sklearn.model_selection import StratifiedKFold

x = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4]])
y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 1, 1])

skf = StratifiedKFold(n_splits=2)
for train_index, test_index in skf.split(x, y):
    print("train_index:", train_index, "test_index:", test_index)

# output
# train_index: [1 6 7 8 9] test_index: [0 2 3 4 5]
# train_index: [0 2 3 4 5] test_index: [1 6 7 8 9]
```

## ShuffleSplit（ランダム置換相互検証）
#### 概要
- 独立した訓練用・テスト用のデータ分割セットを指定した数だけ生成する．
- データを最初にシャッフルしてから，訓練用とテスト用にデータを分割する．

#### オプション(引数)
- n_splits：生成する分割セット数
- test_size：テストに使うデータの割合（0~1の間で指定）
- random_state：シャッフルする時の乱数のシード

#### 例
```python
import numpy as np
from sklearn.model_selection import ShuffleSplit

x = np.array([[1, 2], [3, 4], [1, 2], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4], [3, 4]])
y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 1, 1])

ss = ShuffleSplit(n_splits=3, test_size=0.25, random_state=0)
for train_index, test_index in ss.split(x, y):
    print("train_index:", train_index, "test_index:", test_index)

# output
# train_index: [9 1 6 7 3 0 5] test_index: [2 8 4]
# train_index: [2 9 8 0 6 7 4] test_index: [3 5 1]
# train_index: [4 5 1 0 6 9 7] test_index: [2 3 8]
```
login_requiredのajax版


認証の必要なAPIエンドポイントを作りたいときに、`@login_required` 使うと、APIサーバとしては不都合があった

## `login_required`を使った場合

認証NGのとき

- 302 Found
- リダイレクト先のパスへのGETを発行
    - レスポンスデータには、`<doctype......` なるhtmlが返される

という動きをするので、APIの利用者側からは使いにくい。

- https://docs.djangoproject.com/ja/2.1/topics/auth/default/#django.contrib.auth.decorators.login_required



API利用者としては

- 認証NGだったら "401 Unauthorized"

という動きをしてほしい

## コピペ用コード

```py
from json import dumps
from functools import wraps
import django.http
from django.http.response import HttpResponse

def ajax_login_required(view_func):
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if request.user.is_authenticated:
            return view_func(request, *args, **kwargs)
        res = {}
        res["message"] =  "Unauthorized"
        jsonstring = dumps(res, ensure_ascii=False)
        response = HttpResponse(jsonstring, content_type='application/json; charset=UTF-8', status=401)
        return response
    return wrapper
```

- ほぼ[stackoverflowの回答](https://stackoverflow.com/a/49051524)をベース
- 自作するよりメンテされてるプラグインを使いたいが、見つけられてない
kustomizeでsecretGenerator内でcatを使うときに改行コードを気にしたくない

# セキュリティ上の懸念からsecretGenerator.commandsは廃止されました


# これらの情報はもう使えません。

https://github.com/kubernetes-sigs/kustomize/commit/2fa4a3458924de6c3a1613fb0c69b2f658e6b3c7#diff-56d2b08a91b2ee0b8bd542dd9eb8299e


- - -

`secretGenerator` は便利な機能だけど
環境変数で使う場合、相変わらず末尾の改行コードを意識しなくてはならなくて
k8sのsecretを知らない人がハマりやすい

今まではこんな感じに準備してた

~~~bash
$ echo -n "sensitive_text" > secret_file
~~~

# 末尾の改行コードがあってもなくても動かす

`xargs echo -n` にpipeで繋ぐことによって末尾の改行コードがあってもなくても動く。
またkustomizeが実行するdefault commandは`["sh", "-c"]` のため、
pipeの途中で失敗しても最終コマンドが成功すると、成功とみなされてしまうためpipefailを指定したbashで実行する。

kustomization.yaml

~~~yaml
generatorOptions:
  shell: ["bash","-o", "pipefail", "-c"] 
secretGenerator:
- name: your-secret
  type: Opaque
  commands:
    SECRET_TOKEN: "cat ./secret_file | xargs echo -n"
~~~
CentOSで「NetworkManager」から「network」に変更する

ネットワーク管理方式を「NetworkManager」から「network」に戻す際にはまったのでメモ

## 環境情報
```
OS：CentOS7.3
IP：DHCP
```

## はまったこと
下記コマンドでNetworkManagerを停止し、networkを起動しようとした。

```
# systemctl stop NetworkManager
# systemctl disable NetworkManager
# systemctl start network
```

しかし、networkを起動する時にエラーが立ち上がらない事象が発生。

```
# systemctl status network
● network.service - LSB: Bring up/down networking
   Loaded: loaded (/etc/rc.d/init.d/network; bad; vendor preset: disabled)
   Active: failed (Result: exit-code) since 火 2018-09-04 15:34:59 JST; 4s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 1556 ExecStart=/etc/rc.d/init.d/network start (code=exited, status=1/FAILURE)

 9月 04 15:34:59 ホスト名 network[1556]: RTNETLINK answers: File exists
 9月 04 15:34:59 ホスト名 network[1556]: RTNETLINK answers: File exists
 9月 04 15:34:59 ホスト名 network[1556]: RTNETLINK answers: File exists
 9月 04 15:34:59 ホスト名 network[1556]: RTNETLINK answers: File exists
 9月 04 15:34:59 ホスト名 network[1556]: RTNETLINK answers: File exists
 9月 04 15:34:59 ホスト名 network[1556]: RTNETLINK answers: File exists
 9月 04 15:34:59 ホスト名 systemd[1]: network.service: control process exited, code=exited status=1
 9月 04 15:34:59 ホスト名 systemd[1]: Failed to start LSB: Bring up/down networking.
 9月 04 15:34:59 ホスト名 systemd[1]: Unit network.service entered failed state.
 9月 04 15:34:59 ホスト名 systemd[1]: network.service failed.
```

サーバを再起動するとエラーなどなかったかのように「network」は立ち上あがっているので
netork周りの設定はおかしくないとはずなのにどうして失敗するのだろうか。
サーバを再起動すれば直るということはプロセスが悪さをしてる？

## 原因
見つけました！

```
# ps aux | grep NetworkManager
root        572  0.0  1.5 112876 15856 ?        S    15:20   0:00 /sbin/dhclient -d -q -sf /usr/libexec/nm-dhcp-helper -pf /var/run/dhclient-eth0.pid -lf /var/lib/NetworkManager/dhclient-418ddc1b-baa9-3a6c-9c2d-27737ad89161-eth0.lease -cf /var/lib/NetworkManager/dhclient-eth0.conf eth0
root        577  0.0  1.5 112876 15864 ?        S    15:20   0:00 /sbin/dhclient -d -q -sf /usr/libexec/nm-dhcp-helper -pf /var/run/dhclient-eth1.pid -lf /var/lib/NetworkManager/dhclient-241ed966-e1c7-4d5c-a6a0-8a6dba457277-eth1.lease -cf /var/lib/NetworkManager/dhclient-eth1.conf eth1
root       1714  0.0  0.0 112660   976 pts/0    R+   15:38   0:00 grep --color=auto NetworkManager
```

今回IPアドレスをDHCPで管理していた為、「NetworkManager」が「dhclient」を動かしていたのですが
「NetworkManager」を停止しても「dhclient」自体は消えずに残っていたようです。
このプロセスをkillしたところ、無事「network」を起動できました！

## 今回の反省
messageログを再度見てみるとnetwork起動時に下記メッセージが！
これを見れば一発で原因が確定していましたね。
ちゃんとログを見ていればもっとはやく解決できただろうに…

```
dhclient(577) is already running - exiting.
```
springのtaglib:formのメモ

# Springのjspのformタグについての簡易メモ

## 初期設定（？）
使用するjspファイルのに下記を追加

`<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form %>`

追加したファイルの冒頭部分が以下

```
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form %>
```

## 基本の形

formタグの基本の形が以下の表記
###　ページの表示
```java:HogeForm.java
// 省略

public class HogeForm {
	private String hoge;
	
	// ゲッター、セッター省略
}
```

```java:HogeController.java
// 基本的な表記はある程度省略
public String hogeForm(Model model) {
	HogeForm form = new HogeForm();
	// フォームに表示したい初期値を設定
	form.setHoge("foo");
	return "page";
}
```

```html:page.jsp
<form:form modelAttribute="checkForm">
    <form:input path="hoge" />
    <input type="submit" />
</form:form>
```

### フォームの受け取り

```java:HogeController.java
public Stiring hogeForm(@ModelAttribute HogeForm form, Model model) {
	// 受け取った値からmessageにセット
	model.setAttribute("message", form.getHoge());
	return "result";
}
```

```html:result.jsp
<!-- 基本を省略 -->
<!-- messageを表示 -->
<p>${ message }</p>
	
```

以上が基本の基本の形

## 使用可能なタグの説明

`form` ー `<form></form>`を作成する。

 - デフォルト(指定なし)でPOSTメソッド
 - デフォルト(指定なし)で`action=""`がGETで飛んできたサーブレットを指定
 
`input` ー　デフォルトで

## 気が向いたらつづきをかく

## 参考サイト

[Spring MVCのformタグを簡単にまとめました](http://kuwalab.hatenablog.jp/entry/20130118/p1)
[Spring4勉強会 第一回(第二回)](https://qiita.com/yu_eguchi/items/9a56e6c6281c1f0435ff)
アソシエーションについて

#アソシエーションとは
Ruby on RailsのActive Recordでテーブル間（モデル）の関連付けを行うこと



##メソッドの種類
belongs_to...（「1対1」のつながりを設定する）
has_one...（「1対1」のつながりを設定する。belongs_toとの違いは、他方のモデルのインスタンスをまるごと含んでいること）
has_many...（「１対多」のつながりを設定する場合によく使われる。）
has_many :through...（「多対多」のつながりを設定する場合によく使われる。この関連付けは、2つのモデルの間に「第3のモデル」(結合モデル)が介在する点が特徴）
has_one :through...（他方のモデルに対して「1対1」のつながりを設定。この関連付けは、2つのモデルの間に「第3のモデル」(結合モデル)が介在する点が特徴）
has_and_belongs_to_man...（他方のモデルと「多対多」のつながりを作成する。through:を指定した場合と異なり、第3のモデル(結合モデル)が介在しない）


#ex.)twitterのような投稿アプリの時
<img width="618" alt="スクリーンショット 2018-09-04 15.01.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/265805/ea6ff698-52d2-24f9-86e4-bd7af81a11bc.png">

User一人に対してPostsにはUserが投稿した複数の情報が入っている。
UserとPostsを連携させるために使うメソッドが**belongs_toメソッド**と**has_manyメソッド**だ。
 
英文で書くと、、、
user has many posts (userはpostsを所有する） 
posts belongs to user (postsはuserに属する） 

なので**Userモデル**には

```
has_many :posts, dependent: :destroy 
```

**Postsモデル**には

```
belongs_to :user
```

を記入し連携させる








#dependentオプションとは
モデルの親レコードを削除するときに子のレコードを削除するかどうかを決めるオプション。


##種類

オプション	説明
:destroy	親レコードと一緒に子レコードを削除。子レコードのコールバックも実行
:delete_all	親レコードと一緒に子レコードを削除。SQL直接実行なのでコールバックなし
:nullify	子レコードの外部キーをNULL更新する
:restrict_with_exception	子レコードがある場合はActiveRecord::DeleteRestrictionErrorを発生
:restrict_with_error	子レコードがある場合は削除が失敗し、親レコードにエラー情報を付与
wekanのdockerコンテナ作成

# はじめに
どうやら思っている以上に有名なWekanは導入について多くの記事があります。しかしつい最近（2018/08/30）に会社のサーバーにいれてみようとしたところうまくいかなかったのでいろいろした結果、たぶんやり方が変わった気がするので記事にしてみることにしました。

## 実際にあったこと
* まずproxyに引っかかってた気がする
* しかしWekanのgithubを見ると下記が書いてある。

> Currently there are two dockerhub builds for wekan. One at mquandalle dockerhub and another at wekanteam dockerhub.

　　broken……壊れてるんですか？
　　まあ、そんなわけでよくわからないがquayの方を使ってみることに。

# やること
## 概略
* docker / docker-composeの環境を用意する  
使った環境は下記。いろいろあって会社と自室の両方で試すことになったので記録。LinuxはどちらもCentos7。

環境 | docker | docker-compose
---|---|---
自宅 | 1.13.1 | 1.13.0 
会社 | 18.03.1 | 1.22.0

* Wekanのソースを持ってくる

 > git clone https://github.com/wekan/wekan.git

　ひょっとしたらソース全部はいらない気がするが私はバカなので知らない。

* Dockerfileとdocker-compose.ymlを書き換える

* docker-compose up -d
上の２つはcloneしてきたフォルダの中でやる。


## Dockerfileの編集（必要な時は） 
* プロキシを突破のためにDockerfileにENVでhttp_proxyとか書いてあげる。  
しかしこれで作ったコンテナはプロキシを環境変数に覚えてしまうので、気になるなら後で何とかして消してあげる必要がある。

## docker-compose.ymlの編集（必要な時は）
* ポートフォワーディングの変更  
```port:```のところに書いてあるので直す。デフォルトは80:8080

* URLの変更 
先頭が```- ROOT_URL=```になっている行があるので、そこを適当に書き直す。
デフォルトはlocalhostになってる。
この並びにいっぱい設定の変数があるが放置してても動くっぽい。

* mongodbのフォルダ(volume)の変更
デフォルトでもホストのどこかに作るんだろうけど、よくわからないしわかる場所に変更する。

```docker-compose.yml
volumes:
  wekan-db:
    driver_opts:
      type: none
      device: /home2/Dagon/docker_work/wekan/db
      o: bind
```
　　みたいな感じにしました。


# 参考
* https://github.com/wekan/wekan/wiki/Docker
* [トップレベルにvolumes:を置いてボリュームの定義する(2)](https://qiita.com/reflet/items/5c0a488494c7d6904084#%E3%83%88%E3%83%83%E3%83%97%E3%83%AC%E3%83%99%E3%83%AB%E3%81%ABvolumes%E3%82%92%E7%BD%AE%E3%81%84%E3%81%A6%E3%83%9C%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%A0%E3%81%AE%E5%AE%9A%E7%BE%A9%E3%81%99%E3%82%8B2)
AWS CloudWatch Logsでログを収集する

複数台のサーバからログを収集するのにCloudWatchを利用してみました。 
Fluentdも検討しましたが、ただ収集するだけでいいていうのと溜まったものは定期的に退避させたい、インフラがAWSに集約されているのでこちらを使ってみました。

## 実行環境
```
$ cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
```

```
$ uname -a
Linux ip-**-*-*-**.ap-northeast-1.compute.internal 3.10.0-862.3.2.el7.x86_64
```

## IAMの登録
### ポリシーの作成

1. IAMコンソールを開く
2. ナビゲーションから「Policies」を選択
3. 「Create policy」をクリック
4. サービスに「CloudWatch Logs」を選択
5. JSONタブを選択し、下記JSONを適宜編集して貼り付け、「Review policy」を押下
6. Name, Descriptionを設定して「Create policy」を押下

```json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:DescribeLogStreams",
                "logs:GetLogEvents",
                "logs:PutRetentionPolicy",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:ap-northeast-1:{ aws_id }:log-group:*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:GetLogEvents",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:ap-northeast-1:{ aws_id }:log-group:/*:log-stream:*"
        },
        {
            "Effect": "Allow",
            "Action": "logs:CreateLogGroup",
            "Resource": "arn:aws:logs:ap-northeast-1:{ aws_id }:*"
        }
    ]
}
```

### グループの作成
1. ナビゲーションから「Groups」を選択
2. 「Create new group」を押下
3. グループ名を適宜設定
4. 先程作成したポリシーをアタッチ

### ユーザーの作成
1. ナビゲーションから「Users」を選択
2. 「Add user」を押下、名前を適宜設定、「Programmatic access」にチェックを入れ次へ
3. グループに先程作成したグループを追加
4. 作成すると「Access key ID」と「Secret access key」が生成されるのでメモしておく

## CloudWatch Logs Agentのインストール
### セットアップスクリプトの取得

```bash

curl https://s3.amazonaws.com/aws-cloudwatch/downloads/latest/awslogs-agent-setup.py -O
```

### 実行

```bash
python ./awslogs-agent-setup.py --region ap-northeast-1
```
1. AWS Access Key ID、AWS Secret Access Keyに先程ユーザーを作成したときに生成されたものを入力
2. Default regionはご自由に (今回はap-northeast-1を使用しています)
3. Default output formatは空でOK
4. Path of log file to uploadは対象となるログファイルのパス
5. Destination Log Group nameはCloudWatch Logsで使用したいロググループ名。ログのファイルパスとかでOK
6. Choose Log Stream nameはなんでもいいけどEC2 instance id or ホスト名にしておくのが無難。
7. Log Event timestamp formatは対象ログのデートフォーマットに合わせる
8. initial position of uploadは過去のログも取り込む場合はFrom start of file.今から先のだけ取り込みたい場合はFrom end of file.
9. 他のログも設定したい場合は「Y」で4から繰り返し
10. /var/awslogs/etc/awslogs.conf を直接編集してもOK

### awslogsの起動

```bash
systemctl start awslogs
systemctl enable awslogs
```

## 確認
1. CloudWatchコンソールにアクセスし、ナビゲーションから「Logs」を選択
2. 設定したロググループが生成され、下層に設定したログストリームが生成され、下層にログが集積されていれば成功

## 次回
定期的にS3にアーカイブしていきます。
cordova-android@7.0.0でcordova build androidしたらUnhandledPromiseRejectionWarning

# 問題

cordova-android 7.0.0で下記のようにresource fileを追加すると、

```xml
<resource-file src="MyStrings.xml" target="res/values/MyStrings.xml" />
```

`cordova build android`で下記のエラーが出る：

```
cp: copyFileSync: could not write to dest file (code=ENOENT):/***/platforms/android/res/xml/config.xml

Parsing /***/platforms/android/res/xml/config.xml failed
(node:68280) UnhandledPromiseRejectionWarning: Error: ENOENT: no such file or directory, open '/***/platforms/android/res/xml/config.xml'
```

# 原因

cordova-android 7.0.0からプロジェクトのディレクトリー構造が変更された。しかし、`platform/android/`直下に`res`というディレクトリーがあると、「Eclipseを使っている」と誤認識されるので、いろいろとおかしくなる。[^1]

# 解決方法

1. resource-fileを`app/src/main/res`以下に追加する。
2. cordova-common@2.1.0から、`config.xml`でも`<config-file>`をresource fileに対して記述できるので、これを使う。


[^1]: [このPR](https://github.com/apache/cordova-android/pull/437)で修正されているみたいだけど、まだリリースされていない。
gethコマンド

# 事前準備
・Download geth from https://ethereum.github.io/go-ethereum/downloads/

```
mkdir ~/work/geth
cp /path/to/download/geth ~/work/geth/
echo "export PATH=~/work/geth:$PATH" >> ~/.bash_profile && source ~/.bash_profile
mkdir ~/work/geth/private_net
vim genesis.json
cd ~/work/geth/private_net
```
```genesis.json
{
    "config":{
        "chainId": 33,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
    "nounce": "0x0000000000000033",
    "timesatamp": "0x0",
    "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "gasLimit": "0x8000000",
    "difficulty": "0x100",
    "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "coinbase": "0x3333333333333333333333333333333333333333",
    "alloc": {}
}
```

# プライベートネットワーク初期化
`geth --datadir ./ -init ./genesis.json`

# プライベートネットワーク起動
`geth --networkid "10" --nodiscover --datadir ~/work/geth/private_net --rpc --rpcaddr "localhost" --rpcport "8545" --rpccorsdomain "*" --rpcapi "eth,net,web3,personal" --targetgaslimit "20000000" console 2>> ~/work/geth/private_net/error.log`

# アカウント作成
`> personal.newAccount("password")`

# アカウントの確認
`> eth.accounts`
`> eth.accounts[0]`

# コインベースアカウントの確認
`> eth.coinbase`

# コインベースアカウントの変更
`> miner.setEtherbase(eth.accounts[1])`

# ブロック内容の確認
`> eth.getBlock(0)`

# マイニングの開始
`> miner.start(1)`
＊()内はマイニングするスレッド数

# マイニングの停止
`> miner.stop()`

# マイニング中かどうかの確認
`> eth.mining`

# ブロック数の確認
`> eth.blockNumber`

# コインベース残高の確認(単位wei)
`> eth.getBalance(eth.accounts[0])`

# コインベース残高の確認(単位ether)
`> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether")`

# アカウントのロック解除
`> personal.unlockAccount(eth.accounts[0])`

# etherの送金
`> eth.sendTransaction({from: eth.accounts[0], to: eth3.accounts[2], value:web.toWei(5, "ether")})`

# トランザクションの確認
`> eth.getTransaction("hash値")`

# トランザクションレシートの確認
`> eth.getTransactionReceipt("hash値")`
C＃を使用してWord文書に脚注の脚注を追加する

脚注と文末脚注は、文書に説明を追加するためにワード文書でよく使用されます。
脚注は通常、現在のページの下部またはテキストの下にあり、ドキュメント内の何かのコメントとして使用されます。注釈は通常、文書の最後にあり、引用のソースをリストするなどです。この資料では、主にC＃を使用してWord文書に脚注を追加する方法について説明します。
【C#】


```ruby:Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using System.Drawing;
namespace Word_Footnote_Endnote
{
    class Program
    {
        static void Main(string[] args)
        {
            //新しい単語文書オブジェクトを作成し、脚注を注釈に追加する必要がある単語文書を読み込みます
            Document document = new Document();
            document.LoadFromFile("サンプル.docx", FileFormat.Docx2010);
            //最初の段落を取得する
            Paragraph paragraph = document.Sections[0].Paragraphs[0];
            //脚注を追加
            Footnote footnote = paragraph.AppendFootnote(FootnoteType.Footnote);
            //第1段では、文字列「井戸」を検索して、脚注を追加します。
            DocumentObject obj = null;
            for (int i = 0; i < paragraph.ChildObjects.Count; i++)
            {
                obj = paragraph.ChildObjects[i];
                if (obj.DocumentObjectType == DocumentObjectType.TextRange)
                {
                    TextRange textRange = obj as TextRange;
                    if (textRange.Text == "井戸")
                    {
                        //脚注を添えた文字列を太いフォーマットに設定する
                        textRange.CharacterFormat.Bold = true;
                        //脚注を差し込む
                        paragraph.ChildObjects.Insert(i + 1, footnote);
                        break;
                    }
                }
            }

            //脚注を添付してフォントフォーマットを設定する            
            TextRange text = footnote.TextBody.AddParagraph().AppendText("狐と山羊脚注");
            text.CharacterFormat.FontName = "MS Gothic";
            text.CharacterFormat.FontSize = 10;
            text.CharacterFormat.TextColor = Color.DarkGray;
            footnote.MarkerCharacterFormat.FontName = "Calibri";
            footnote.MarkerCharacterFormat.FontSize = 12;
            footnote.MarkerCharacterFormat.Bold = true;
            footnote.MarkerCharacterFormat.TextColor = Color.DarkGreen;
            //第3の段落をとる
            Paragraph paragraph2 = document.Sections[0].Paragraphs[2];
            //尾注とフォーマットを設定します
            Footnote endnote = paragraph2.AppendFootnote(FootnoteType.Endnote);
            TextRange text2 = endnote.TextBody.AddParagraph().AppendText("狐と山羊注釈");
            text2.CharacterFormat.FontName = "Arial Black";
            text2.CharacterFormat.FontSize = 10;
            text2.CharacterFormat.TextColor = Color.DarkGray;
            endnote.MarkerCharacterFormat.FontName = "Calibri";
            endnote.MarkerCharacterFormat.FontSize = 12;
            endnote.MarkerCharacterFormat.Bold = true;
            endnote.MarkerCharacterFormat.TextColor = Color.DarkGreen;
            //ファイルを保存する
            document.SaveToFile("脚注を追加する.docx", FileFormat.Docx2010);
        }
    }
}

```

エフェクトチャートは以下の通りです
![1.jpg](https://qiita-image-store.s3.amazonaws.com/0/278784/81463b9b-973f-99d3-d92a-5526d46790d9.jpeg)
複数の Arduino の状態を複数のシリアル出力にて確認する方法

# はじめに
複数の Arduino から構成されるインスタレーション作品の場合、それぞれの Arduino が正常に動作しているのか＝製作者の意図したとおりに動いているのかを確認しながら製作（開発）を進める必要があります。

本稿では開発ホストとして Raspberry Pi 3 を使用しますが、残念なことにラズパイ用の Arduino IDE では、シリアルコンソールを複数開くことができません。そこで本文書では、picocom というシリアルターミナルソフトを用いた、複数の Arduino からのシリアル出力を確認する方法について説明します。

# 複数の Arduino を ArduinoIDE より開発する
Raspberry Pi に複数の Arduino を接続すると、接続ごとにシリアルポートが増えていきます。これは、Arduino IDE の「ツール」→「シリアルポート」にて確認できます（シリアルモニタを開いていると全てのポートが表示されないので、その場合は閉じて下さい）。

私の環境では、シリアルポートは、/dev/ttyACM0 と /dev/tyACM1 という名前になっていました。ラズパイに接続したまま、ラズパイを起動すると、どのシリアルポートに、どの Arduino が接続されているのかは分かりません。その場合は、Arduino と接続されている USB ケーブルを一度抜いて、ひとつづつ差し込んでポート名を確認するようにして下さい。

複数の Arduino に異なるプログラムを書き込む場合は、このシリアルポートを切り替えて、コンパイル＆転送していくこととなります。

# 別の手段でシリアルをモニタする
Arduino IDE によるシリアルモニタは複数開いておくことができないことは冒頭で述べたとおりです。しかし、USB デバイスによる仮想的なシリアルポートとして機能しているようですので、ここでは picocom という小さなシリアルモニタを用いて、IDE ではモニタできないシリアルポートの様子を観察します。

## picocom
picocom は小型のシリアルモニタプログラムです。今回はこれを用いて IDE のシリアルモニタでは表示されていない、その他のシリアルポートをモニタします。

## picocom のインストール
picocom は apt-get を使って、

    sudo apt-get install picocom

でインストールできます（簡単です）。

## picocom の使い方
picocom シリアルポート名、とすれば該当のシリアルポートがモニタできるのですが、そのままでは Arduino で出力した "\n" が正しく改行されません。これは LF として picocom に入力された文字を CR LF として解釈すれば、IDE のシリアルモニタと同じ様に表示されるので、--imap オプションを用いて変換します（変換用のオプションは LF を CRLF にするので、lfcrlf です）。 

あと、picocom を起動してみると分かるのですが、デフォルトの通信速度は 9600 bps です。なので、Arduino 側のシリアルポートもその設定にしておくと便利でしょう。

実際のコマンドは以下のように書きます。ここでは、/dev/ttyACM1 をモニタしています：

    pococom --imap lfcrlf /dev/ttyACM1

コンソールベースとなりますが、これで他の Arduino の出力も確認できます。もちろん、ターミナルウィンドウを複数開いて、別のポートを同時にモニタしても良いですし、別のタブにて picocom を走らせて、タブ切り替えによって確認することもできます。このあたりは、開発スタイルに応じて好きな方を選べば良いと思います。

picocom の終了方法ですが、<b>Ctrl-A Ctrl-X です</b>。Ctrl-C とか Ctrl-D とかでは終了しませんので、ご注意を。

# まとめ
本稿では、複数の Arduino へ、異なるスケッチの書き込み方法を説明しました。次に、picocom を用いた Arduino IDE に附属のシリアルモニタを用いない、別のシリアルポートをモニタリングする方法について述べました。ここで述べた方法は、複数の Arduino から構成されるシステムの、最低限の開発環境整備だとは思いますが、動作確認程度はこれでなんとかなる場合も多いと思います。インスタレーションシステム作りに少しでも役に立てば幸いです（もっと他の便利な方法を知っている方は教えてください。現在、絶賛作品制作中なので助かります。よろしくおねがいします）。
WindowsUpdte のURL

# WindowsUpdte で確認しに行くURLの調べ方。

*コマンドプロンプトを立ち上げる。
*以下コマンドを実行する。

```
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\au
```
Windows10 快適にするレジストリの登録

# レジストリの変更

* コマンドプロンプトを管理者として起動する。
* 以下コマンドをregの拡張子で保存する。
* コマンドプロンプトから作成した拡張子のファイルを実行する。

```

rem Windows10でスリープからの復帰時にロック画面にしない。
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\Personalization]
"NoLockScreen"=dword:00000001

rem シャットダウンモードを完全シャットダウンにする。
rem これ設定していないと、シャットダウンが実質スリープなのでバッテリー消耗が激しい
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Power]
"HiberbootEnabled"=dword:00000000

rem WindowsUpdateを複数の場所から更新する これをＯＮにすると、WINDOWS Updateを共有をしてしまう。
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\DeliveryOptimization\Config]
"DODownloadMode"=dword:00000000

rem ------------------------------------------------------

rem Explorer 規定値ボタンの設定
rem フォルダーのヒントにサイズ情報を表示する重くなるので false
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\ShowInfoTip]
"DefaultValue"=dword:00000000

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\FolderSizeTip]
"DefaultValue"=dword:00000000


rem 拡張子を表示する。
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\HideFileExt]
"DefaultValue"=dword:00000000

rem メニューをいつも表示する
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\AlwaysShowMenus]
"DefaultValue"=dword:00000001

rem メニュー展開する
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\NavPane\AutoExpand]
"DefaultValue"=dword:00000001

rem ------------------------------------------------------
rem Explorer 規定値ボタンの設定
rem フォルダーのヒントにサイズ情報を表示する重くなるので false
[HKEY_USERS\.DEFAULT\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\ShowInfoTip]
"DefaultValue"=dword:00000000

[HKEY_USERS\.DEFAULT\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\FolderSizeTip]
"DefaultValue"=dword:00000000

rem 拡張子を表示する。
[HKEY_USERS\.DEFAULT\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder]
"HideFileExt"=dword:00000000

[HKEY_USERS\.DEFAULT\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced]
"HideFileExt"=dword:00000000

rem メニューをいつも表示する
[HKEY_USERS\.DEFAULT\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\Folder\AlwaysShowMenus]
"DefaultValue"=dword:00000001

rem メニュー展開する
[HKEY_USERS\.DEFAULT\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced\NavPane\AutoExpand]
"DefaultValue"=dword:00000001

rem IEの設定

rem URLにID/passwordの埋め込みを許可する。
[HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_HTTP_USERNAME_PASSWORD_DISABLE]
"iexplore.exe"=dword:00000000

```

※実行は自己責任でお願いします。
Windows10 快適にする不要なタスクスケジューラの停止

# 不要なタスクスケジューラの停止
*コマンドプロンプトを管理者として起動する。
*以下のコマンドを実行する。

```
rem 不要 Adobe製品は他システムで更新管理しているので
schtasks /change /Disable /TN "Adobe Acrobat Update Task"

rem 不要 MediaPlayerの共有サービスは利用しないので
rem schtasks /change /Disable /TN "\Microsoft\Windows\Windows Media Sharing\UpdateLibrary"

rem 不要 カスタマー エクスペリエンス向上プログラムに参加しないので。
schtasks /change /Disable /TN "\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser"
schtasks /change /Disable /TN "\Microsoft\Windows\Application Experience\ProgramDataUpdater"
schtasks /change /Disable /TN "\Microsoft\Windows\Application Experience\StartupAppTask"
schtasks /change /Disable /TN "\Microsoft\Windows\Application Experience\StartupAppTask"
schtasks /change /Disable /TN "\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser"
schtasks /change /Disable /TN "\Microsoft\Windows\Autochk\Proxy"
schtasks /change /Disable /TN "\Microsoft\Windows\Customer Experience Improvement Program\Consolidator"
schtasks /change /Disable /TN "\Microsoft\Windows\Customer Experience Improvement Program\KernelCeipTask"
schtasks /change /Disable /TN "\Microsoft\Windows\Customer Experience Improvement Program\UsbCeip"
schtasks /change /Disable /TN "\Microsoft\Windows\CloudExperienceHost\CreateObjectTask"

rem 不要（Windows システム評価ツール）評価されても仕方が無い。
schtasks /change /Disable /TN "\Microsoft\Windows\Maintenance\WinSAT"

rem 不要 Xboxゲームは利用しないので
schtasks /change /Disable /TN "\Microsoft\XblGameSave\XblGameSaveTask"
schtasks /change /Disable /TN "\Microsoft\XblGameSave\XblGameSaveTaskLogon"

rem 不要 タスクによる CPU 高負荷削減
schtasks /change /Disable /TN "\Microsoft\Windows\Diagnosis\Scheduled"
schtasks /change /Disable /TN "\Microsoft\Windows\DiskFootprint\Diagnostics"

rem 不要 ディスクとシステムに関する全般的な情報がWindowsディスク診断ツールによってMicrosoftに報告するタスク。 
schtasks /change /Disable /TN "\Microsoft\Windows\DiskDiagnostic\Microsoft-Windows-DiskDiagnosticDataCollector"

rem 不要 自動報告する必要ない
schtasks /change /Disable /TN "\Microsoft\Windows\Windows Error Reporting\QueueReporting"

rem 不要 SIUF 文字列をアップデート
schtasks /change /Disable /TN "\Microsoft\Windows\Feedback\Siuf\DmClient"

rem 不要 位置情報通知のタスク
schtasks /change /Disable /TN "\Microsoft\Windows\Location\Location"
schtasks /change /Disable /TN "\Microsoft\Windows\Location\WindowsActionDialog"

rem さまざまな地図関連のトーストはしない。 オフライン用にダウンロードした地図の更新データをチェックはしない
schtasks /change /Disable /TN "\Microsoft\Windows\Maps\MapsToastTask"
schtasks /change /Disable /TN "\Microsoft\Windows\Maps\MapsUpdateTask"

rem Google 関連のタスクは移動しない。
schtasks /change /Disable /TN "GoogleUpdateTaskMachineCore"
schtasks /change /Disable /TN "GoogleUpdateTaskMachineUA"

```


※実行は自己責任でお願いします。
google-home-notifier覚え書き

Qiitaには[google-home-notifier](https://github.com/noelportugal/google-home-notifier)についての情報がたくさんあります。おかげで[時報](https://qiita.com/udon242/items/fef44cec7b243f93151b)や[gmailの着信通知](https://qiita.com/tetotetote/items/4d6ac866b716695fa4c9)がとても便利に使えています。

しかしいろいろ試行錯誤して気づいたこともあるので、メモしておきます。

# Raspberry PiのIPアドレス、Google HomeのIPアドレス

google-home-notifier.jsにIPアドレスとポート番号のデフォルト値が書かれています。

```javascript
const serverPort = 8091; // default port

var deviceName = 'Google Home';
var ip = '192.168.1.20'; // default IP
```

このipはGoogle HomeのIPアドレス、serverPortはRaspberry Piで動くgoogle-home-notifierが使うポート番号を指定します。最初、serverPortはGoogle Homeのポート番号だと思ったのですが、Google Homeが開けているポート番号は固定で8009です。

# google-home-notifierのサービス化
[Google Homeで時報を知らせる](https://qiita.com/udon242/items/fef44cec7b243f93151b)に従ってgoogle-home-notifierをサービス化すると、"sudo systemctl stop googlehomenotifier"後にngrokのプロセスが残ってしまい、次のstartが失敗します。[google-home-notifierをsystemdで常駐させる](https://qiita.com/q1701/items/a9907c6ded2a2e7fe382)に書かれているとおり、KillModeはcontrol-groupにするとよいようです。

# IPアドレス、言語の指定

google-home-notifierのexample.jsを見ると、ipアドレスや言語をパラメータとして指定できるようにしようとした形跡があります。

```javascript
  if (req.query.ip) {
     ip = req.query.ip;
  }

  var language = 'pl'; // default language code
  if (req.query.language) {
    language;
  }

  googlehome.ip(ip, language);
  googlehome.device(deviceName,language);
```

実際には、postのコードでreq.queryを参照しているのは無意味ですし、getのコードでもreq.query.languageがあるかどうかチェックしたあとでlanguageの値を更新していないので無条件にデフォルト値が使われます。だからこれを使う場合には直接コードを書き換えるしかありません。

[私のfork](https://github.com/kazhik/google-home-notifier)では修正済みです。

# Google Homeを探す処理

google-home-notifier.jsには、googlehome.ip()でGoogle HomeのIPアドレスが指定されなかった場合にはgooglehome.device()で指定された名前のデバイスがあるかどうかを探す、という処理が書かれています。しかし未完成なのでまともに動きません。


[私のfork](https://github.com/kazhik/google-home-notifier)で修正済み。

# ngrokのauthToken

ngrokの公開URLはアカウントを登録しないとすぐに消えてしまいます。[Maruhack.comの記事](https://maruhack.com/google-home-notifier-raspberry-pi-2651#ngrokURL)に従ってトークンを生成し、google-home-notifier.jsを修正してngrok接続時に設定ファイルからトークンを読み込むようにします。

[私のfork](https://github.com/kazhik/google-home-notifier)で修正済み。
Windows10 でPhoto Viewerを利用できるようにする。

# photo viewerを利用できるようにする方法

以下のレジストリ設定を行い再起動する。

```

rem 
[HKEY_CLASSES_ROOT\Applications\photoviewer.dll]

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell]

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\open]
"MuiVerb"="@photoviewer.dll,-3043"

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\open\command]
 @=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,72,00,75,00,\
6e,00,64,00,6c,00,6c,00,33,00,32,00,2e,00,65,00,78,00,65,00,20,00,22,00,25,\
00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,46,00,69,00,6c,00,65,00,73,00,\
25,00,5c,00,57,00,69,00,6e,00,64,00,6f,00,77,00,73,00,20,00,50,00,68,00,6f,\
00,74,00,6f,00,20,00,56,00,69,00,65,00,77,00,65,00,72,00,5c,00,50,00,68,00,\
6f,00,74,00,6f,00,56,00,69,00,65,00,77,00,65,00,72,00,2e,00,64,00,6c,00,6c,\
00,22,00,2c,00,20,00,49,00,6d,00,61,00,67,00,65,00,56,00,69,00,65,00,77,00,\
5f,00,46,00,75,00,6c,00,6c,00,73,00,63,00,72,00,65,00,65,00,6e,00,20,00,25,\
00,31,00,00,00

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\open\DropTarget]
"Clsid"="{FFE2A43C-56B9-4bf5-9A79-CC6D4285608A}"

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\print]

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\print\command]
 @=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,72,00,75,00,\
6e,00,64,00,6c,00,6c,00,33,00,32,00,2e,00,65,00,78,00,65,00,20,00,22,00,25,\
00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,46,00,69,00,6c,00,65,00,73,00,\
25,00,5c,00,57,00,69,00,6e,00,64,00,6f,00,77,00,73,00,20,00,50,00,68,00,6f,\
00,74,00,6f,00,20,00,56,00,69,00,65,00,77,00,65,00,72,00,5c,00,50,00,68,00,\
6f,00,74,00,6f,00,56,00,69,00,65,00,77,00,65,00,72,00,2e,00,64,00,6c,00,6c,\
00,22,00,2c,00,20,00,49,00,6d,00,61,00,67,00,65,00,56,00,69,00,65,00,77,00,\
5f,00,46,00,75,00,6c,00,6c,00,73,00,63,00,72,00,65,00,65,00,6e,00,20,00,25,\
00,31,00,00,00

[HKEY_CLASSES_ROOT\Applications\photoviewer.dll\shell\print\DropTarget]
"Clsid"="{60fd46de-f830-4894-a628-6fa81bc0190d}"

```
H2 と MySQL で DDL を共有する

ScalikeJDBC を使ったプロジェクトで、本番のデータベースは MySQL だけど、テストは手軽さを優先して H2 を選択して開発を進めています。

データベースのマイグレーションを Flyway で行っており、DDL は MySQL / H2 共有で使っていて、ほとんどのケースで問題なく動いていたのですが、一部 SQL ステートメントエラーになるケースがあったのでメモ。

## 経緯

マイグレーションファイルを作成していて、テーブルのユニーク制約を削除したかったので以下のような DDL を作成した。

```sql
ALTER TABLE articles DROP INDEX articles_title;
```

MySQL だと動くんですが、H2 では　`インデックス "ARTICLES_TITLE" が見つかりません` というエラーになります。

H2 の設定で `MODE=MySQL` としてもだめでした。
（そもそも構文に互換性を持たせるためのものではなかった。）

## 原因

どうも `1.4.196` までは `ALTER TABLE <table_name> DROP CONSTRAINT <constraint_name>` とする必要があったようです。
→ [Issue #570](https://github.com/h2database/h2database/issues/570)

調べてみると `1.4.197` では上記 Issue が取り込まれ `DROP INDEX` でもユニーク制約が削除できるようになっていました🙏

めでたしめでたし。

## 参考

[MySQL と H2 の両方で動作するデータ定義言語 (DDL) のパターン集](https://qiita.com/takuya0301/items/578fc9578c05e2e2e5e7)
ffmpegでmovからmp4に変換する

#はじめに

ffmpegでmovからiOS11で再生できるmp4に変換する方法のメモです。

# .h265をインストールする

```sh

$ brew reinstall ffmpeg --with-fdk-aac --with-freetype --with-libass --with-libvpx  --with-sdl2 --with-x265
```

# 変換コマンド

mymov.movからh265の同名のmp4を書き出す。

```sh
$ ffmpeg -i mymov.mov -vcodec libx265 mymov.mp4
```

quicktimeで再生できる最高品質のmp4を書き出す

```sh

$ ffmpeg -i mymov.mov -crf 1 -pix_fmt yuv420p mymov.mp4
```
海外の人名のシステムでの取り扱いについて

#結論
海外の人名をシステムで取り扱う場合、余裕を持った項目、桁数に設計したほうがいいよ！
という話。

#説明
日本人の想定で姓名だけのを想定したUIに海外の人名をシステム的に取り込む場合、
複数の単語が1項目につめこまれるので桁数不足になる可能性がある。

##海外の人名の構成
ファーストネーム、ミドルネーム、ラストネームの構成。
ただし、ミドルネームに決まりはなく、複数単語で構成されることもある。
例 George Herbert Walker Bush

さらに、親子で同名の場合、ジュニアが追加される場合があるので5単語以上の人名もありうる


# 参考文献
http://youshouki-eigo.hatenadiary.jp/entry/%E3%82%B8%E3%83%A5%E3%83%8B%E3%82%A2%E3%80%81%E3%83%9F%E3%83%89%E3%83%AB%E3%83%8D%E3%83%BC%E3%83%A0%E3%81%A8%E3%81%AF
【Python】指定した期間の連続した日付をだす

分析的な業務をやっていると日付毎に集計したいというニーズがあるのですが、そのときに掲題の処理が必要になったのでメモ。

## ほしい結果

```
2018-01-01
2018-01-02
2018-01-03
2018-01-04
2018-01-05
2018-01-06
2018-01-07
2018-01-08
2018-01-09
2018-01-10
```

こういうの。

## コード

```python

from datetime import datetime
from datetime import timedelta

start = datetime.strptime('2018-09-01', '%Y-%m-%d')
end   = datetime.strptime('2018-09-05', '%Y-%m-%d')

def daterange(_start, _end):
    for n in range((_end - _start).days):
        yield _start + timedelta(n)


for i in daterange(start, end):
    print (i)

```

## 結果
```console
2018-09-01 00:00:00
2018-09-02 00:00:00
2018-09-03 00:00:00
2018-09-04 00:00:00
```

## 参考までに

```python
start = datetime.strptime('2018-09-01', '%Y-%m-%d')
end   = datetime.strptime('2018-09-05', '%Y-%m-%d')
```

の部分を、

```python
start = datetime.strptime('201801', '%Y%m')
end   = datetime.strptime('201802', '%Y%m')
```

というふうに変えると、

```console
2018-01-01 00:00:00
2018-01-02 00:00:00
2018-01-03 00:00:00
2018-01-04 00:00:00
2018-01-05 00:00:00
2018-01-06 00:00:00
2018-01-07 00:00:00
2018-01-08 00:00:00
2018-01-09 00:00:00
2018-01-10 00:00:00
2018-01-11 00:00:00
2018-01-12 00:00:00
2018-01-13 00:00:00
2018-01-14 00:00:00
2018-01-15 00:00:00
2018-01-16 00:00:00
2018-01-17 00:00:00
2018-01-18 00:00:00
2018-01-19 00:00:00
2018-01-20 00:00:00
2018-01-21 00:00:00
2018-01-22 00:00:00
2018-01-23 00:00:00
2018-01-24 00:00:00
2018-01-25 00:00:00
2018-01-26 00:00:00
2018-01-27 00:00:00
2018-01-28 00:00:00
2018-01-29 00:00:00
2018-01-30 00:00:00
2018-01-31 00:00:00
```

という風にもだせます。幸せいっぱいです。


`00:00:00`

がいらない場合は、

```python
start = datetime.strptime('201801', '%Y%m').date()
end = datetime.strptime('201802', '%Y%m').date()
```

とすれば大丈夫です。
Unity開発者がMacでFlutterの環境構築をして躓いた点

プライベートでゲーム開発をしてますが、仕事でFlutterを使ったアプリ開発をやらせてもらえることになったので、まずは環境構築からしてました。

基本的な流れは[公式チュートリアル](https://flutter.io/get-started/install/)を見るのがいいでしょう（英語）

英語苦手な人は[こちら](https://qiita.com/yshishido/items/fd8a1becff840e6ad295)

自分は両方見ながらやりました。

PATHが通ったあとに```flutter doctor```で環境を確認しますが、この際にこんなメッセージが。

```
Android sdkmanager tool not found ({path/to/home}/Library/Android/sdk/tools/bin/sdkmanager).
Try re-installing or updating your Android SDK,
visit https://flutter.io/setup/#android-setup for detailed instructions.
```

自分はUnity2017.4を使ってますが、このバージョンだとUnityがAndroidの最新版sdkmanagerのtoolsに対応していないため、バージョンを下げたtools（tools_r25.2.5）に差し替える対応をしてました（Unity2017以前でAndroidビルドを試みた多くの方が通る道かと思われます・・・）

flutterだとこの古いskdmanager toolを使えないので、Android Studioで最新版をダウンロードします。

上書きしちゃうと今度Unityでビルドできなくなるので、BKします。
<img width="397" alt="スクリーンショット 2018-09-04 14.33.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/237014/846464a1-57aa-edee-ac29-0460efc87aa2.png">

BK後、AndroidStudioのSDK Managerからダウンロード。
<img width="725" alt="スクリーンショット 2018-09-04 14.34.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/237014/dfbac68a-dd9f-b834-b5b6-0890a3198b7d.png">

その後、```flutter doctor```をしてみるとうまくいきました。
<img width="245" alt="スクリーンショット 2018-09-04 14.35.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/237014/dd908599-eea0-d727-784a-8fa834d9b6fd.png">

今後はUnityでAndroidビルドをする時は古いtools、Flutter使うときは新しいtoolsに切り替えてやることになりそうです（ちょっとめんどい）。

Unity2018は最新版のsdkmanager toolsに対応してるんですかね？
いずれ2018に上げると思うのでその時にまた調べてみようと思います。
SFTPでのみアクセスできるユーザーの作成

#やりたいこと
・外部のエンジニアさんと連携したい
・でもドキュメントルートは見せたくない
・でもソースの再配置とかめんどくさい

#実現方法
・sshd_configでsftp専用のユーザーを作成
（bashにはログインさせない）
・sshd_configでchrootでSFTPログイン時のルートディレクトリの変更
（その配下しか見せない）
・chrootのディレクトリ配下に作成したディレクトリをドキュメントルート配下にシンボリックリンクを張る
（chrootの制限としてディレクトリの所有者がroot且つ権限は755で固定＝直下にファイルを配置できない）

#やる
兎にも角にもユーザーを作成する

```
groupadd sftp
useradd -g sftp sftp-user1
```

chroot先のディレクトリを作成する
この時ディレクトリの所有者と権限に注意。
間違った設定をするとログインできないことになる

```
mkdir /var/www/sftp
chown root /var/www/sftp
chmod 755 /var/www/sftp
```

次にsshd_configを編集をする
デフォルトならパスはここ→```/etc/ssh/sshd_config```
最終行にこんな感じで追記する。

```
Match group  sftp
  ChrootDirectory /var/www/sftp
  ForceCommand internal-sftp
```
意味合いとしては、sftpグループのユーザーがアクセスしてきたら
ルートディレクトリを```/var/www/sftp```とすること
sftpアクセスのみでsshでのシェルへのログインは許可しない

```
sshd -t #コンフィグファイルのチェック 
service sshd reload
```
※必ず、```sshd -t```を実行すること。
　sshdが死んだら大変。。。


ここまででSFTP専用ユーザーに対象ディレクトリ配下しか見せない設定は完了
しかし、このままではsftp-userは新たにファイルの配置ができない。
アパッチのドキュメントルートにファイルが無いので、httpで配置したソースも見れない。

なので、/var/www/sftpに新たにディレクトリを切ってそこにシンボリックリンクを張る

```
#/var/www/sftpで実行
mkdir web_source 
chmod 777 web_souce 
```

```
#ドキュメントルートで実行
ln -s /var/www/sftp/web_souce ./web_souce
```

これでドキュメントルートの配下に"/var/www/sftp/web_souce"へのシンボリックリンク
"web_souce"が作成される。

最後にhttpd.confのFollowSymlink？みたいなやつが設定されていれば
アパッチからweb_sourceが見れるはず。

以上。
GASのProperties Serviceを使ってスクリプトのプロパティを読み書きをする

GASのプロジェクトプロパティを変更する際に、[ファイル] - [プロジェクトのプロパティ] - [スクリプトのプロパティ] から変更する以外に、PropertiesServiceを使う方法があったのでメモ。


# データの保存

単一の値を保存するには`Properties.setProperty(key, value)`を使う。

```
PropertiesService.getScriptProperties().setProperty("key","value");
```

<img width="628" alt="スクリーンショット 2018-09-04 14.05.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/6380/727356c2-348c-010f-6789-112fd5e4c30e.png">

データ変更時は同一キー名で上書きするだけでOK。

# データの読み込み
単一の値を取得するには`Properties.getProperty(key)`を使う。

```
var value = PropertiesService.getScriptProperties().getProperty("key");
Logger.log(value);
```
<img width="219" alt="スクリーンショット 2018-09-04 14.15.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/6380/d86f4845-a070-ac9e-af5f-b0607ddea450.png">

## 注意点
プロパティの値はすべて文字列として保存される。
valueに数値など文字列以外のデータ型を渡しても、取得時は文字列として扱われる点に注意。


> Data format
> The Properties service stores all data as strings in key-value pairs. Data types that are not already strings are automatically converted to strings, including methods contained within saved objects.
> [Properties Service  |  Apps Script  |  Google Developers](https://developers.google.com/apps-script/guides/properties)

# 参考
[Class PropertiesService  |  Apps Script  |  Google Developers](https://developers.google.com/apps-script/reference/properties/properties-service)
プロジェクトのnpmパッケージを全てアップデートしたい時の手順

package.jsonでnpmパッケージを管理しているプロジェクトで、一括アップデートかけたい場合の手順

# npm-check-updateインストール
```
npm install -g npm-check-updates
```

# 更新対象のパッケージ確認
```
ncu
```

# package.jsonをアップデート
```
ncu -u
```

# パッケージをアップデート
```
npm update
```

# 参考
https://github.com/tjunnone/npm-check-updates

# yarnだと
```
yarn upgrade-interactive
```
で出来るすごい便利
Windows10 快適にする不要なサービスの停止

# 不要なサービスの停止をする。

* コマンドプロンプトを管理者として起動する。
* 以下のコマンドを実行する。

```
rem Adobeアラーム(うるさいので)
sc config AdobeARMservice                      start=disabled

rem Offline Files の無効化(提案)
sc config CscService                           start=disabled

rem 勝手に Windows が予測して、ディスクからメモリに読み込む動作
rem sc config SysMain                              start=disabled
rem sc config UevAgentService                      start=demand

rem XBoxは利用しない
sc config XblAuthManager                       start=disabled
sc config XblGameSave                          start=disabled
sc config XboxNetApiSvc                        start=disabled

```


※実行は自己責任でお願いします。