OmniAICreator/Qiita-1.07M huggingface dataset:
RPA実践　シナリオ検討

1st Try （難易度 ★）
　申込データを台帳に登録する
　　手順
　　0. 申込リスト（csv）（Daily）と、台帳（Excel）を用意
　　1. 申込リストをコピーし、所定フォルダに格納
　　2. 申込リストから申込情報（すべて）を取得
　　3. 申込情報を申込番号の降順にソートし台帳に申込情報を追加
　　4. 完了後、ポップアップで通知を出す
　　
　　ポイント
　　☑ 日付取得（本日）
　　☑ ファイルのコピー・リネーム
　　☑ csvデータの取得
　　☑ データのソート
　　☑ Excelに書出し
　　☑ ポップアップ表示（静的）

2nd Try（難易度 ★★）
　精算システムに支払情報を登録する
　　手順
　　0. 請求書台帳（Excel）を用意
　　1. 請求書台帳から、ユーザー指定日の支払情報を取得
　　2. 精算システムに支払情報を入力・登録
　　3. 台帳に登録済フラグを立てる
　　4. 完了後、メールで完了通知を出す
　　
　　ポイント
　　☑ ダイアログデータ取得
　　☑ Excelデータの取得
　　☑ データのフィルタ
　　☑ 日付取得（本日）
　　☑ Web画面入力
　　☑ 処理の繰返し
　　☑ メール送信（静的）
　　☑ メール送信（画像挿入）

3rd Try（難易度 ★★★）
　WebサイトにNGワードが含まれていないか検査する
　　手順
　　0. NGワードリスト（Excel）と検査対象Webリスト（Excel）を用意
　　1. NGワードリストと検査対象Webリストを取得
　　2. WebサイトにNGワードがないか検索する
　　3. ヒットした場合、該当箇所のスクリーンショットとURLを取得し、
　　　　Webオーナーに、NGワード有のメール通知を出す
　　4. 完了後、ポップアップでNGワードのヒット件数を通知する

　　ポイント
　　☑ 検索処理（検査）
　　☑ 条件分岐
　　☑ メール送信（動的）
　　☑ ポップアップ表示（動的）

4th Try（難易度 ★★★）
　3つのシステムに登録された支払情報の取引先名・税込金額が同じか確認する
　　手順
　　0. web・txt・PDFの3パターンの支払情報と、比較結果シート（Excel）を用意
　　1. web・txt・PDFから取引先名と合計金額を取得
　　3. 比較し、内容に差異がある場合、支払情報を比較結果シートに書込む
　　
　　ポイント
　　☑ webデータ取得
　　☑ txtデータ取得
　　☑ PDFデータ取得
　　☑ データ比較

5th Try（難易度 ★★★）
　販売実績のDaily Reportを作成する
　　手順
　　0. 販売実績（Access）とReport雛形（Excel/PPT）を用意
　　1. 販売実績から、代理店ごとの販売実績（前日分）を取得
　　2. 代理店ごとの当月の販売件数・売上額を棒グラフで作成
　　3. Report雛形（Excel）にグラフを貼付け、PDFとして保存
　　4. メールにPDF添付かつ本文にグラフ画像を貼付けて関係者に送信
　　5. プレゼン用にReport雛形（PPT）にグラフを貼付け、保存

　　ポイント
　　☑ Access操作
　　☑ PowerPoint編集
　　☑ グラフ作成

6th Try（難易度 ★★★）
　同業他社の新商品情報をニュース専門HPから仕入れる
　　手順
　　0. 同業他社リスト（Excel）と新商品関連ニュース一覧（Excel）を用意
　　1. ニュース専門HPにログイン
　　2. 取引先名を検索ボックスに入力して検索
　　3. 公開日が1時間以内のニュースを対象に、内容に”販売”が含まれるか検索
　　4. 含まれる場合、新商品関連ニュース一覧に追加し、メール通知
　　
　　ポイント
　　☑ ログイン操作
　　☑ Web検索処理
オブジェクトのpublicなプロパティの値をCSV文字列化する

オブジェクトのpublicなプロパティの値をCSV文字列化するサンプルコード。
列はDataMemberのOrderで指定した順に出力する。
CSV文字列化したいクラスのTypeのGetPropertiesメソッドを呼び出すとpublicなプロパティのPropertyInfoを配列で取得できる。
GetPropertyメソッドはプロパティ個別にPropertyInfoを取得できる。

```C#
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
namespace ConsoleApp1 {
    // CSV文字列化したいクラス
    class Student {
        [DataMember(Name = "id", Order = 1)]
        public int Id { set; get; }
        [DataMember(Name = "name", Order = 2)]
        public string Name { set; get; }
        [DataMember(Name = "v1")]
        public int V1 { set; get; }
        [DataMember(Name = "v2")]
        public int V2 { set; get; }
    }
    // CSV文字列化を拡張メソッドで実装してみた
    static class StudentExtension {
        public static string ToCsvString(this Student student) {
            var list = student.GetType().GetProperties()
                .OrderBy(_ => GetOrder(_))
                .ThenBy(_ => _.Name)
                .Select(_ => _.GetValue(student));
            return string.Join(", ", list);
        }
        private static int GetOrder(PropertyInfo info) {
            if (Attribute.GetCustomAttribute(info, typeof(DataMemberAttribute))
                is DataMemberAttribute attr)
            {
                return attr.Order;
            }
            return -1;
        }
    }
    // StudentクラスのCSV文字列化
    class Program {
        static void Main(string[] args) {
            var student = new Student() { Id = 1, Name = "山田", V1 = 1, V2 = 2 };
            Console.WriteLine(student.ToCsvString());
        }
    }
}
```
Kivy Fatal Error Minimum Required OpenGL version(2.0) NOT found! OpenGL version detected:1.1 の対策

Kivy Fatal Error Minimum Required OpenGL version(2.0) NOT found! OpenGL version detected:1.1 の対策

下記の４行コードを追加するだけで解決

```python
import os
os.environ ['KIVY_GL_BACKEND'] = 'angle_sdl2'

from kivy import Config
Config.set('graphics', 'multisamples', '0')
```
『 ! [rejected] master -> master (non-fast-forward) 』のせいでpushできねぇッ!!


# 補足

学んだ知識のほとんどは，Qiitaではなくこちらにバーッとまとめております．

https://hiroki-it.github.io/tech-notebook/software_development_methodology/software_development_git_command.html

## 今回のエラー

コミットメッセージを修正したので、

```
$ git commit --amend -m "add #0 xxxさんのREADMEを追加"
[master 41cc21b] add #0 xxxさんのREADMEを追加
 Date: Wed Mar 20 20:54:34 2019 +0900
 1 file changed, 193 insertions(+)
 create mode 100644 README.md
```

```push```したら、 ```! [rejected] master -> master (non-fast-forward)```が表示されてしまう。

```
$ git push
To github.com:Hiroki-IT/Symfony2_Nyumon.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:Hiroki-IT/Symfony2_Nyumon.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```
## 原因
ブランチの状態には、```fast-forward```型と、
![アジャイルSEを目指すブログ1.png](https://qiita-image-store.s3.amazonaws.com/0/292201/17ef2aca-6116-b82a-8e28-53c394048dda.png)

```non fast-forward```型があり、
![アジャイルSEを目指すブログ2.png](https://qiita-image-store.s3.amazonaws.com/0/292201/2c9150c4-bdef-8958-e045-b9d7b38b0816.png)

ローカルでは、コメント変更前のブランチから変更後ブランチが作られて```non fast-forward```型となっており、それを```push```しようとしたためエラーになったみたい。

**引用元：アジャイルSEを目指すブログ，http://d.hatena.ne.jp/sinsoku/20111025/1319497900**
**引用元：NullNote，https://nullnote.com/web/git/merge_rebase/**

## 解決法
ローカルのコメント変更前ブランチと変更後ブランチを```merge```する。

##（１）履歴の異なるブランチを```merge```
引数として、```--allow-unrelated-histories```をつけることで、履歴の異なるブランチ同士をマージさせることが可能。
今回は、ローカルのコメント変更前ブランチと変更後ブランチを```merge```

```
$ git merge --allow-unrelated-histories
Merge made by the 'recursive' strategy.
```

※補足
```--allow-unrelated-histories```をつけないと、無関係の履歴を```merge```することはできないと表示される。

```
$ git merge
fatal: refusing to merge unrelated histories
```

##（２）```push```を実行

```
$ git push
Enumerating objects: 2, done.
Counting objects: 100% (2/2), done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 333 bytes | 111.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To github.com:Hiroki-IT/Symfony2_Nyumon.git
   f81c813..f17f68e  master -> master

//成功
```

## 引用リンク
- http://d.hatena.ne.jp/sinsoku/20111025/1319497900
- https://www.ipentec.com/document/git-error-non-fast-forward-in-git-push
- https://qiita.com/takanatsu/items/fc89de9bd11148da1438
- https://blog.clock-up.jp/entry/2018/12/01/git-merge-allow-unrelated-histories
docker環境における、mailcatcherのSMTPサーバー構築方法

# 前提
　dockerのコンテナ内で作業していることを前提とします。

```
docker exec -it コンテナ名 bash
```



# mailcatcherとは？
　mailcathcerとは、railsのgemの一つです。
ちなみにGemfileに直接書き込むとエラーが出てしまうようです。

```
gem install mailcatcher
```

　でインストールしてください。

　ちなみに、mailcatcherとは開発環境用に簡易的なSMTPサーバーを立ち上げる事ができる便利なgemです。
　

　例えば開発環境にて、RailsのAction Mailerなどでメール通知がしたい時に、送信先に実際に送るのではなく、mailcatcherのサーバーに送ります。
　そして、「localhost:1080」にアクセスすると、送ったメールを確認する事ができ流というわけです。


 [mailcatcher](https://mailcatcher.me/)



# docker環境で、mailcatcherを使う方法
　docker環境では、mailcatherのサーバーにそのままメールを送っても、接続を拒否されてしまいます。
　まず、dockerのホスト、IPアドレスを特定しましょう。

```
ip route
```

　これで、IPアドレスを特定できます。
そして、そのIPアドレス宛に（ポートは１０２５）メールを送信します。


　また、docker-compose.ymlに以下を書き込みましょう。

```
smtp:
    image: schickling/mailcatcher
    ports:
      - "1080:1080"
      - "1025:1025"
```

　あとは、

```
docker-compose up
```

をするだけです。

　docker-compose upはコンテナ外でしましょう。
ちなみにdockerが立ち上がっている状態での「docker-compose up」は新たにコンテナを作成する動きにならず、上書きのようになるようです。


　「localhost:1080」にアクセスして、メールが届いていればOKです。


お二方の記事を参考にさせて頂きました。
[@jiz](https://qiita.com/jiz/items/ca584ac2012ba6955552)
[@shinofara](https://qiita.com/shinofara/items/3ea9591f7bd2f0348227)
Dockerでnodeをバージョン指定してインストールする

## 環境
macOS Mojave 10.14.3
Ruby 2.4.0
Node.js 9.2.0
Docker

## Curlでインストール

チーム開発では、ローカルに開発環境を構築する際、nodeのバージョンを合わせる必要があるかと思います。
今回は`v9.2.0`が必要でした。
Dockerfileにnodeのインストールを記述したいのですが、

```Docker

RUN curl -sL https://deb.nodesource.com/setup_9.X | sudo -E bash -
RUN sudo apt-get install -y nodejs
```

上記のような方法ではバージョンが細かく指定できずに困りました。
調べたところ、`n package`を使用してインストールする方法があるようです。

## `n package`を使用してのインストール

```Docker
RUN apt-get install -y nodejs npm
RUN npm install n -g 
RUN n 9.2.0
```
`n {version}`のように、バージョンが指定できます。

## 完成

最終的に下記のようなDockerfileを作成しました。

```Docker
FROM ruby:2.4.0
RUN apt-get update -qq && apt-get install -y build-essential libxslt-dev libxml2-dev cmake
RUN apt-get install -y nodejs npm && npm install n -g && n 9.2.0
RUN mkdir /app
WORKDIR /app
COPY ./src/Gemfile /app/Gemfile
COPY ./src/Gemfile.lock /app/Gemfile.lock
RUN bundle install
COPY ./src /app

```

### 参考
[Ubuntuに最新のNode.jsを難なくインストールする](https://qiita.com/seibe/items/36cef7df85fe2cefa3ea)
8日目: Deep Neural Network Ensembles for Time Series Classification

# Deep Neural Network Ensembles for Time Series Classification 要約
## 読んだ論文
Deep Neural Network Ensembles for Time Series Classification
Hassan Ismail Fawaz, Germain Forestier, Jonathan Weber, Lhassane Idoumghar and Pierre-Alain Muller
https://arxiv.org/abs/1903.06602v1
上記の論文を読んだので簡単にまとめます．IJCNN 2019に採択されているようです．二日目に読んだ[転移学習のやつ](https://qiita.com/himatya/items/6c4aaaa1da8ea0e41906)と，六日目に読んだ[データ拡張のやつ](https://qiita.com/himatya/items/97b35eb6d1b847996876)の著者の人ですね．時系列分類で随分ご活躍の様子です．

## 3行でまとめる
- 時系列分類問題におけるアンサンブルCNNモデルを提案する．
- おなじみUCR公開データセットで評価して有用性を示した．

## 提案手法
### 提案モデル
![20190320_001.png](https://qiita-image-store.s3.amazonaws.com/0/376859/93a2ab8c-7eec-21ee-b85d-d6ad6cd63aa1.png)

- 図のように同一の入力時系列から複数のCNNモデルをアンサンブルし，出力を決定するモデルである．
- 6つの異なるアーキテクチャを採用し，最終的には事後確率の平均をとって出力を決定している．
 - MLP: 隠れ層が3層のDropoutありMLP
 - FCN: Convolution3層からのGlobal Average PoolingでSoftMaxするCNN
 - ResNet: ショートカットゲートを導入したResidualブロックを積み上げるCNN
 - Encoder: FCNにDropoutとAttentionを拡張した手法
 - Multi-Channels DCNN: Convolution-MaxPoolingを積み合わせる手法．普通のCNN．
 - Time-CNN: 普通のCNNだが，損失にMSEを使用したもの（普通はCategorical-cross-entropy）．
- 上記6種類を重みの初期化をランダムにして複数並列化して比較．
- 最終的に，FCN，ResNet，Encoderの3つを並列にした様子．
- また，85のデータセットのうちターゲットドメインを除いたソースドメイン84個でここに学習したFCNをアンサンブルした手法とランダム初期化してアンサンブルしたFCNｓの比較も行っていた．

## まとめ
- 時系列データのアンサンブルCNN手法を提案した．

## 所管
本日は帰宅が遅く，24時開始だったので心が折れそうでしたので，簡単そうな手法を読みました．ただ，やっぱりしっかり理解しようとしながら読むと1時間位はかかってしまいますね．精進します．
メモ:ラズパイにおけるjavaMailの設定

###詳細
ラズパイでmailの受信をしたいなーと思ったので、javaMailを使うことにしました。
ラズパイにはデフォルトでjavaEEが入っていないのでjavaMail単体を入れることにしました。

今回のこれは、今までよくわからず扱っていたclassPathに重点をおいて取り組んだのでメモします。

###javaMailをダウンロード
javaMailを使うにはjavaMailとJAFを使うらしいのでその二つを以下のURLからダウンロードします。
・[javaMail](https://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-eeplat-419426.html#javamail-1.4.5-oth-JPR)
・[JAF](https://www.oracle.com/technetwork/java/javasebusiness/downloads/java-archive-downloads-java-plat-419418.html#jaf-1.1.1-fcs-oth-JPR)
###設定
ダウンロードした２つのファイルをunzipすると以下のようなディレクトリが出てくる
<img width="584" alt="スクリーンショット 2019-03-21 0.11.18.png" src="https://qiita-image-store.s3.amazonaws.com/0/246212/b986daaf-a1dc-7b74-a1ca-ea21ed76ef77.png">

javaMailを使う為にはmail.jafとactivation.jarの２つのjarファイルをCLASSPATHで設定すればいい。

UNIX系のCLASSPATH設定には主に２通り方法がりシェルによって方法が異なる。
[参考:クラスパス](http://www.javaroad.jp/java_basic2.htm)

```
//シェル(csh)の場合
setenv　CLASSPATH　クラスパス１：クラスパス２

//シェル(bash)の場合
CLASSPATH=クラスパス１：クラスパス２；　export　CLASSPATH

**シェルの確認**
echo $SHELL
```
「シェルの確認」で自分のOSのシェルがなんなのかが分かります。ラズビアンはbashです。
<img width="951" alt="スクリーンショット 2019-03-21 0.34.52.png" src="https://qiita-image-store.s3.amazonaws.com/0/246212/b29079ca-2668-89eb-27e5-756ba3838855.png">
僕の場合はこのようになります。
また、:(コロン)で区切り.(ドット)はカレントディレクトリを表します。

これでCLASSPATHの設定は終わりました。
###確認
CLASSPATHが設定できているか確認のために以下のくだらないプログラムをコンパイルします。

```Sample.java
import javax.mail.Address;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Sample{
 public static void main(String args[]){
  System.out.println("OK");
 }
}
```
これで問題なくコンパイルできれば大丈夫だと思います。

###感想
今まではCLASSPATHについてよくわからなかったが、今回の「これ」で使えるようにはなった。
詳細は曖昧なままだが今回はこれでよしとしましょう！！

設定に時間がかかってしまい、疲れてメモが雑になってしまいましたね。
次回からはもっと効率よく作業を終わらせたいです。

この分野を何も知らない身ですので、間違いがありましたらコメントにて指摘お願いします。
また、豆知識を教えてくれる方もコメントお願いします。
Windows で Bundler で sqlite3-ruby を使う

# はじめに

SQLite3 使ってますかー！　便利ですよね。
何の因果か Windows でも sqlite3-ruby を使う必要が生じ、何故だか苦労したので、メモを残します。


# 問題点

ググると色々と見つかりますが Windows で sqlite3-ruby を使う場合は幾つか問題にぶつかります。

* sqlite3-ruby は native extension が必須
* 1.4.0 は dlopen() に依存している
* 1.3.13 はデフォルトの bundle install だと native extension がビルドされない


# 対策

### 1.4.0 ⇒ dlopen をインストールする

参照：[sqlite3-ruby issue 248](https://github.com/sparklemotion/sqlite3-ruby/issues/248)

RubyInstaller2 環境であれば下記でインストールされます。

```:(32bit)
C:\Users\JohnDoe> ridk exec pacman -S mingw-w64-i686-dlfcn
```

```:(64bit)
C:\Users\JohnDoe> ridk exec pacman -S mingw-w64-x86_64-dlfcn
```

どっちも mingw-w64 なんす。


### 1.3.13 ⇒ bundle を config する

参照：[bundler docs](https://bundler.io/v2.0/man/bundle-config.1.html#LIST-OF-AVAILABLE-KEYS)

```
C:\Users\JohnDoe> bundle config force_ruby_platform true
```

要するに `gem install --platform=ruby` と同じです。けど `bundle install` を使いたいですよねぇ。


# 所感

64bit 環境で RAM が潤沢なら、仮想環境で Linux 使うようにした方が幸せになれる気がします。

否、32bit 環境で RAM が貧弱だったので、不幸になったんですね。。。
Windows上でVisual Studio Codeを使う際に英語キーボードのCtrl+backquote(`)が認識されない問題の対策

英語キーボードを使用してWindows上でVSCodeを使用する際に、`` Ctrl ``+`` ` ``が認識されないという問題があります。
下記のissueの情報によると`` Ctrl ``+`` ` ``を入力した際に、WindowsによってZenkakuとかHankakuとして認識されてしまいVSCode側ではどうしようもないそうです。

https://github.com/Microsoft/vscode/issues/63659

ただ、VSCodeにおける`` Ctrl ``+`` ` ``は、結合ターミナルの切り替えという非常に重要なコマンドに割り当てられていますのでデフォルト厨の僕みたいな人間には非常につらい状況です。
![toggle_terminal.gif](https://qiita-image-store.s3.amazonaws.com/0/57937/6e39ce59-c5c7-2951-72c8-c26f95d28359.gif)


そこで、今回はEnglish(United States)の言語オプションを追加して対策しました。
言語オプションの追加は、下記のリンク先を参考にして設定してみてください。

https://support.microsoft.com/ja-jp/help/4027670/windows-10-add-and-switch-input-and-display-language-preferences

その上でEnglish(United States)の言語オプションでVSCodeを動作させている際には、`` Ctrl ``+`` ` ``が正しく認識されるようになります。
もちろん、English(United States)の言語オプションでは日本語の入力ができないので日本語を打つ際には不便なのですが、``Windows`` + ``Space``で日本語とEnglishの切り替えができるので、入力切り替えの``Alt``+`` ` ``の代替になります。
webツールのID/パスワードを事前登録しておく

社内のWebツール操作の自動化をするのに問題になるのが、ログインする際にID/パスワードを入力することです。
コード内にベタ書きするのは良くないですし、パスワードを変更するたびにコードを修正する必要性が生じます。

そこで、事前にID/パスワードを登録しておき、pythonプログラム実行時にID/パスワードを入力するツールを作ってみましたので、備忘録として残しておきます。

#環境
* OS : Windows 10 64bit
* Python 3.6.0 (Anaconda3-4.3.1)

#使用したモジュール
* TKInter
* csv
* sys

# コード

```entry.py
import tkinter as tk
from tkinter import messagebox
import sys
import csv

# ID/PWDの登録
def Entry_ID_PW():
    System_id = str(Enter_ID.get())
    System_pw = str(Enter_PW.get())
    System_list = [System_id, System_pw]
    with open('System_entry.csv', 'w') as file:
        writer = csv.writer(file, lineterminator='')
        writer.writerow(System_list)

    # 結果をラベルに表示
    Label_Msg['text'] = "登録しました。終了してください"

# 処理の終了
def Entry_Finish():
    sys.exit()

# 操作ボックス
root = tk.Tk()
root.title(u"System ID/PW入力")
root.geometry("300x230")
Label_Msg = tk.Label(root, text=u'SystemのID/PWDを入力してください。')
Label_Msg.pack()
# ID
Label_ID = tk.Label(root, text=u'System ID')
Label_ID.pack()
Enter_ID = tk.Entry(root)
Enter_ID.insert(tk.END, '')
Enter_ID.pack()
# PW
Label_PW = tk.Label(root, text=u'System Password')
Label_PW.pack()
Enter_PW = tk.Entry(root)
Enter_PW.insert(tk.END, '')
Enter_PW.pack()
# 登録ボタン
Label_Blanc = tk.Label(root, text=u'')
Label_Blanc.pack()
Entry_Btn = tk.Button(root, text=u'登録')
Entry_Btn["command"] = Entry_ID_PW
Entry_Btn.pack()
# 終了ボタン
Label_Blanc = tk.Label(root, text=u'')
Label_Blanc.pack()
Finish_Btn = tk.Button(root, text=u'終了')
Finish_Btn["command"] = Entry_Finish
Finish_Btn.pack()

root.mainloop()
```

#説明
###入力ボックス
入力ボックスを表示して、ID/パスワードを入力させます。
入力ボックスの説明は、こちらの記事（[メニューボックスからプログラムを実行する](https://qiita.com/takanorimutoh/items/3ec2d57c730aed995156)）を参照してください。
![entry.PNG](https://qiita-image-store.s3.amazonaws.com/0/353289/d27f8466-fb56-5f4c-fa6b-36ac966514ff.png)

###CSVファイルへのID/パスワードの保存
入力したID/パスワードはCSVファイルに保存します。

```entry.py
# ID/PWDの登録
def Entry_ID_PW():
    System_id = str(Enter_ID.get())
    System_pw = str(Enter_PW.get())
    System_list = [System_id, System_pw]
    with open('System_entry.csv', 'w') as file:
        writer = csv.writer(file, lineterminator='')
        writer.writerow(System_list)
```

入力したID/パスワードは、文字列(string)としてリスト化。
それを`System_entry.csv`というCSVファイルに保存します。

CSVファイルはpythonファイルと同フォルダに作成されます。
別のフォルダに保存したい場合はパスを指定して保存してください。
（例）C:\document\System_entry.csv

`lineterminator`はwriterが作り出す各行を終端する際に用いられる文字列で、デフォルトでは`\r\n`です。
ID/パスワードを取り出す時に改行コードが入ってしまうと、それを取り除く作業が必要になるため、`''`とします。

`writerow`は、Writerオブジェクトに使うことで、csvファイルへの一行の書き込みができます。
writerowメソッドの引数はリスト(ここでは`System_list`)です。


###CSVファイルからのID/パスワードの取り出し
Webツールを自動操作するためのpythonファイルに以下のコードを記載します。

```web_automation.py
#pfosログイン情報を取得し、リスト化する。
f = open("W:\python\System_entry.csv", "r")
reader = csv.reader(f)
data = [ e for e in reader ]
f.close()

#ログイン情報をリストから取り出す。
System_id = data[0][0]
System_pw = data[0][1]
```
これでCSVファイルからID/パスワードを取り出すことができますので、
その後、selenium等を使い、WebツールにID/パスワードを入力し、ログインさせて上げればOKです。

###備考
なお、SystemのログインID/パスワードを変更した場合は、前述の入力ボックスに新しいID/パスワードを再登録すれば、CSVファイルに上書き保存されるようになっています。
JS Consoleでコピーができる関数を紹介

今回は、ChromeのJS consoleでコピーができる関数を紹介します。

## JS Consoleでコピーができる関数を紹介
それが、`copy`です。
このように使えます。
![Bad Request](https://toshioakaneya.com/wp-content/uploads/2019/03/Screen-Shot-2019-03-20-at-23.49.09.png
)
ぜひ使ってみて下さい。
## 終わりに
私は現在、Web3のサービスの開発をしています。詳しくはこちらの記事をご覧下さい。
        [無料でイーサリアムが当たる、Web3時代の寄付サイトを作った話](https://note.com/kenroda/n/n814347fbdcd7)
text-indentとは

#text-indent
一行目のはじめに空白（インデント）を作る。
#html
![html_indent.png](https://qiita-image-store.s3.amazonaws.com/0/380575/1ed16596-43e6-5355-1887-3659c0649aef.png)
#css
![css_indent.png](https://qiita-image-store.s3.amazonaws.com/0/380575/e26958cb-498c-2ba1-ed16-77cd66391744.png)
#result
![result_indent.png](https://qiita-image-store.s3.amazonaws.com/0/380575/5f2bf7cb-69ef-2858-9f2b-9852e149822c.png)
先頭に空白ができた。
SlackからRaspberry Piにメッセージを送信する（2019年3月時点）

以前[こちら](https://qiita.com/nyas/items/7d49bccc7b23d3460c44)の記事にSlackからRaspberry Piにメッセージを送信する話を記載したのですが、2日ほど前にnode-red-contrib-slackのバージョンが上がりましたので、[最新版](https://github.com/yayadrian/node-red-slack)について記載したいと思います。
（私も感じていたのですが、以前のSlack Botノードはコネクションがいつの間にか切れていて応答がなくなる、ということがあったのですが、最新版では改善されているとのことです。）

# 以前のSlackノードを再現
[本家のサイト](https://github.com/yayadrian/node-red-slack)に記載してある通りですが、以前のノードの動きを再現するには、Slackノードの前にfunctionノードが必要です。username, emojiIcon, channel の "" の部分に表示される送信ユーザ名、ユーザの絵文字、チャネル（「#」で始まるもの）を入れておきます。

```
// https://api.slack.com/methods/chat.postMessage
msg.topic = "chat.postMessage"
var payload = {
    text: msg.payload
};

// set default username (replicate the node configuration value)
var username = "";
if (username) {
    payload.username = username;
    payload.as_user = false;
} else if (msg.username) {
    payload.username = msg.username;
    payload.as_user = false;
}

// set default emojiIcon (replicate the node configuration value)
var emojiIcon = "";
if (emojiIcon) {
    payload.icon_emoji = emojiIcon;
} else if (msg.emojiIcon) {
    payload.icon_emoji = msg.emojiIcon;
}

// set default channel (replicate the node configuration value)
var channel = "";
if (channel) {
    payload.channel = channel;
} else if (msg.channel) {
    payload.channel = msg.channel
}

if (msg.attachments) {
    payload.attachments = msg.attachments;
}

msg.payload = payload;

return msg;
```

※ 上記には msg.attachments があり、以前はできなかった画像送信ができるのかと思ったのですが、試してみたところ送信はできませんでした。。私の設定の問題かもしれません。

# 参考にしたサイト
[GitHub node-red-contrib-slack](https://github.com/yayadrian/node-red-slack)
text-decorationとは

さっそくサンプルを作ると、
#html
![html_decoration.png](https://qiita-image-store.s3.amazonaws.com/0/380575/c0ccf9f2-bbd1-9304-2e0e-13296ae408fe.png)
#css
![css_decoration.png](https://qiita-image-store.s3.amazonaws.com/0/380575/2611b84d-7e1c-387f-364d-7a07e329dcea.png)
#結果
![result_decoration.png](https://qiita-image-store.s3.amazonaws.com/0/380575/abcbee04-2a25-399a-bac8-368b7fb46e8d.png)
エクスプローラの左側にキーボードで移動したい！

<h4>[記事とかどうでもいいからショートカットだけ知りたい人はこちら](#ショートカット完成)</h4>

## エクスプローラの左側（ナビゲーションウィンドウ）に移動するのが面倒

いちいちマウスをクリックして移動するのは、あまりにもナンセンス。
なので、ナビゲーションウィンドウに移動するショートカットを探します。

[Windows のキーボード ショートカット](https://support.microsoft.com/ja-jp/help/12445/windows-keyboard-shortcuts)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/218691/66048cc5-628e-e2ca-b526-f7487a8c5459.png)

<h4>まさかのショートカットなし！</h4>
エディタとかはショートカットキーがちゃんと存在するのに、エクスプローラにはないらしい…… まじか
諦めるのはまだ早い、何かしらキーボードのみで移動する手段があるはず。

## `Tab`押せば移動できる

![01.gif](https://qiita-image-store.s3.amazonaws.com/0/218691/46c1a98b-746b-b2b5-251a-4daeb6a8632c.gif)

できました！`Tab`を何回か押せば、ナビゲーションウィンドウとコンテンツウィンドウの移動ができます。
ですが、自分がどこのウインドウにいるか分からないので使い勝手が悪すぎです。しかも何回も押さないといけない。
このままだとお世辞にもショートカットキーとは言えないです。何か方法を考えなければ。

**必ず一定の場所に移動したあと**ならば、`Tab`を押す回数は固定になるのでは？

<img src='https://qiita-image-store.s3.amazonaws.com/0/218691/92365a4e-70cd-9cb9-1335-fc7e7fa731b3.png' width='200'>

## ショートカット完成！

> ナビゲーションウインドウ：`Ctrl`+`F` `Tab`
> コンテンツウインドウ：`Ctrl`+`F` `Tab` `Tab`

![02.gif](https://qiita-image-store.s3.amazonaws.com/0/218691/49109ee6-2d75-c7d8-d6c3-5e7b008c43e3.gif)

遂に完成です！毎回検索ボックスに移動することで、`Tab`を押す回数が固定になります。
~~ちなみに、QTTabBarを入れている人は`Tab`が一回ずつ多くなるので注意が必要です。~~
追記：QTTabBarはタブの数が増えると、`Tab`を押す回数が変わってしまうので、`Shift`+`Tab`で逆方向に行く必要がありました。
以下の通りにして頂くと正しく動きます。
> ナビゲーションウインドウ：`Ctrl`+`F` (`Shift`+`Tab` \* 8)
> コンテンツウインドウ：`Ctrl`+`F` (`Shift`+`Tab` \* 7)


これで、エクスプローラの左側にキーボードのみで移動ができるようになりました！………んですが、実際押すのが面倒くさい。
`Ctrl`+`F` `Tab` `Tab`とか何回もキーを押すのは面倒くさい、そうなったらやるべきことはただ1つ！

## 自作ショートカットキーを作ろう！

<h3>キーが押しにくいなら押しやすいくすれば良いじゃない</h3>

![image.png](https://qiita-image-store.s3.amazonaws.com/0/218691/cc88317e-3514-15bf-2fd2-fc9bceb46416.png)

ということで、押しやすい自作ショートカットキーを作ります！
今回は、AutoHotKeyを使用します。ツールは何でもいいです（Keyhacとか）。
**要は押しやすいショートカットキーが作れればいいのです！**正直、手段は何でもいい。

闇雲にキーバインドを上書きし、元の機能をかき消すのはよくないので、さきほどの[Windows のキーボード ショートカット](https://support.microsoft.com/ja-jp/help/12445/windows-keyboard-shortcuts)の中からショートカットに使用されていないキーを探します。
今回は、`Ctrl`+`U` と `Ctrl`+`O` が空いているので、この2つでショートカットキーを作ります。
これなら押しやすいし、左と右の移動が分かりやすい（個人差があります）。

> `Ctrl`+`U` がナビゲーションウインドウへの移動
> `Ctrl`+`O` がコンテンツウインドウへの移動

あとはショートカットキーのコードを書くだけです。
AutoHotKeyとかの導入は適当にググってやってください。

```:AutoHotKeyで実装した例
#IfWinActive, ahk_exe explorer.exe
LCtrl & u::Send, ^{f}{Tab}
LCtrl & o::Send, ^{f}{Tab 2}
#IfWinActive
```

ようやく押しやすいショートカットキーで移動ができました！
｢俺はこっちのが押しやすい｣とかあればご自由に設定してください。別に自作ショートカットキーを作らなくても良いと思います。


## あとがき

想像以上に面倒くさかった、なぜ簡単に移動できるショートカットキーがないんだ！
あとwindows explorerの名称を調べるのが大変だった、コンテンツウインドウとか正しい名前なのか微妙なところ。
QTTabBarを入れている人は、自作ショートカットを設定するのをおすすめします。[QTTabBar](http://qttabbar-ja.wikidot.com/)もおすすめなので是非！
個人的に満足のいく結果になったので良かったです。

<h5><font color='red'>今後のアップデートで、ショートカットキーが標準搭載されることを心から願っています。</font></h5>
AI（機械学習）の適用限界を示唆する２つの数学定理【初心者向け】

#概要：　
AI（機械学習）の適用限界を示唆する次の２つの数学的定理が存在している。

- ノーフリーランチ定理
    - AI(機械学習)の手法にはただ一つのあらゆる状況に対処できる完全な方法はない
- 醜いアヒルの子の定理
    - ものごとをある概念に分けるというときには，必然的にそのものごとの特定の側面を重視し，他を無視することを伴う
 
この２つの定理が示唆する不可能性は、数学の形式的証明に基づいたものであり，
AI(機械学習）のみならず、人間の帰納的推論全てにあてはまる。


※　日本物理学会誌　[「変わりゆく機械学習と変わらない機械学習」](https://www.jps.or.jp/books/gakkaishi/2019/01/74-01seriesAIphys2.pdf) より抜粋＆まとめ：


##ノーフリーランチ定理:
> ある予測問題について手法AがBより汎化誤差に関して性能が良かったとしても，手法BがAより良くなるような別の予測問題が必ず存在することを示す．どの手法も他の手法より常によいということはあり得ないことになり，多くの機械学習手法を考案する必要が生じる．逆に，解こうとする予測問題について事前に情報があれば，それを活かした手法が有利になるので，問題に合わせて手法を構築したり選択することが重要になる


無料の昼食は存在しない（どこかで、例えば夜ごはんの料金が高くなる）ということですね。

この定理の名称は、ハインラインのSF小説『月は無慈悲な夜の女王』（1966年）が由来だそうです。　[by Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%8E%E3%83%BC%E3%83%95%E3%83%AA%E3%83%BC%E3%83%A9%E3%83%B3%E3%83%81%E5%AE%9A%E7%90%86)



##醜いアヒルの子の定理:
>この定理は，対象を表現している全ての特徴を同等に重要とみなす限り，純粋に形式的な観点のみだけでは，他より類似している対象の集まりというものは存在しえないことを示す

-------------

>この定理により，対象のある側面を重視し他を無視するという主観的規準なくしては，分類などの判断はコンピュータも人間もできない．また，特徴選択や次元削減など，一部の特徴を特に重視する操作が機械学習にとって本質的であることをこの定理は示唆している


##コメント：
[「変わりゆく機械学習と変わらない機械学習」](https://www.jps.or.jp/books/gakkaishi/2019/01/74-01seriesAIphys2.pdf)は、発展の歴史も含まれ、初心者＆入門者にも比較的読み易くお勧めです。
React HooksのuseEffect内でsetInterval等を呼び出すとstate等の値が変化しない問題の解決策

# 初めに
以下のコードはstateの値が変わった時どのような動作をすると思いますか？
本質ではないので`clearInterval`等の処理は省略しています。

```ts
const [x, setX] = useState(0);
useEffect(() => {
  setInterval(() => {
    console.log(x);
  }, 1000);
}, []);
```

このコード、思うようには動かずstateの値が変わってもずっと0が出力され続けます。
何故でしょうか？JSのクロージャをある程度理解している人なら分かると思います。
コンポーネント関数が呼ばれた時の動作を考えてみましょう。
初回呼び出しでは`useState(0)`を呼ぶと`x`に`0`という値が入ります。
`useEffect`のコールバックも呼び出されます。この時`setInterval`のコールバックは`x`という変数をキャプチャしています。
2回目以降の呼び出しでは`x`は現在のstateです(このコードだけでは具体的な値は分からない)
`useEffect`の第二引数が`[]`なのでコールバックは呼び出されません。
つまり`setInterval`のコールバックの`x`は**常に**1回目に呼び出された時の`x`をキャプチャしています。
また`useState`が原因で勘違いしやすい所ですが、N回目の`x`とN'回目の`x`は全く**別の変数**です。Reactがいい感じに管理してくれるので同じ変数のように見えますが、JSから見たら全くの別物です。ここがポイントです。
だから`setInterval`のコールバックがキャプチャしている`x`は常に1回目の呼び出しの値、つまり`0`となってしまうのです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/169698/3d2a3faa-e18b-92d2-70c3-bd0ff8f3c1f9.png)

# 解決策
ではどう解決したらいいでしょうか？
`useRef`と使って以下のように書きます。

```ts
const [x, setX] = useState(0);
const refX = useRef(x);
useEffect(() => {
  refX.current = x;
}, [x]);
useEffect(() => {
  setInterval(() => {
    console.log(refX.current);
  }, 1000);
}, []);
```

なぜこれで上手く行くのでしょうか？
まず2行目〜5行目で`x`の値を`refX.current`に常にいれるようなコードを書いています。
また今回は`setInterval`のコールバックは`x`ではなく`refX`をキャプチャしています。
今回もN回目の呼び出しとN'回目の呼び出しの`refX`が全く別の変数という事に変わりはないのですが、`useRef`が内部でいい感じにしてくれるおかげで、`refX`の値は**常に一定**です。だから全く別の変数であっても問題が起きません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/169698/234c02af-12f0-1e2a-9410-26dc7013adbc.png)

また`x`の値を`refX`にいれるコードはよく使うのでカスタムフック化しておきましょう。

```ts
export function useValueRef<T>(val: T) {
  const ref = React.useRef(val);
  React.useEffect(() => {
    ref.current = val;
  }, [val]);
  return ref;
}
```

これを使うとこうなります。

```ts
const [x, setX] = useState(0);
const refX = useValueRef(x);
useEffect(() => {
  setInterval(() => {
    console.log(refX.current);
  }, 1000);
}, []);
```

# 場合によってはシンプルになる解決策
さっきはキャプチャする変数を参照にすることで解決しました。
しかし変数が増えると大変です。
そこで今度は関数を参照にしてみましょう。

```ts
const [x, setX] = useState(0);
const f = useValueRef(() => {
  console.log(x);
});
useEffect(() => {
  setInterval(() => {
    f.current();
  }, 1000);
}, []);
```

最初のコード例でも最新の`x`をキャプチャするクロージャは常に生成されていました。
しかし`useEffect`の第二引数が`[]`であったため使われずに捨てられていました。
今回はこれを有効活用することで解決しています。`f.current`は常に最新の`x`をキャプチャしたクロージャです。

`f`という名前が汚染されるとめんどうなので以下のようなカスタムフックを作って綺麗にしましょう。

```ts
export function useEffectRef<T>(effect: (ref: React.MutableRefObject<T>) => void | (() => void | undefined), val: T, deps?: React.DependencyList) {
  const ref = useValueRef(val);
  React.useEffect(() => effect(ref), deps);
}
```

これを使うとこうなります。

```ts

const [x, setX] = useState(0);
useEffectRef(f => {
  setInterval(() => {
    f.current();
  },1000);
}, () => {
  console.log(x);
}, []);
```

これなら変数が増えても楽ですね。
学生ハッカソンにて優勝して100万円を獲得したぞ！

####3/16 ~ 3/17に大阪で開催されたTrunkハッカソンというものに出場してきました！

結果、優勝して賞金の100万を手にすることができたので今回はそのチーム活動の過程で良かったところと改善点をメインに書いていこうと思います。
大阪のハッカソンでありながら、名古屋からのチームが優勝したということは、これはもう名古屋が大阪より優れていることは間違いない 笑

##制作作品
####Okonomy(オコノミー)

![okonomyパワー-01.jpg](https://qiita-image-store.s3.amazonaws.com/0/185233/b57389bd-81d5-b974-2a32-df2478481928.jpeg)

Tinderのように左右にフリックするだけで、付近のオススメのお店を紹介してくれるというもの。
制作期間：約25時間

####チーム内分担
* デザイナー: 2人
* フロントエンド: 1人
* バックエンド兼インフラ: 1人
* 機械学習: 2人

ちなみに自分はバックエンド兼インフラの人です。


##制作過程

###アイデア出し
TRUNKハッカソンのテーマは『関西の食をテクノロジーで楽しくする』
自分たちのチームは3時間ほどと結構な時間をアイデア出しに割きました。出場チームは全部で17ほどあったのですが、その中で一番最後までアイデアを考えていたと思います 笑　別にアイデアを煮詰めていたとかじゃないんですけどね。最後の30分くらいで100万円を取れるアイデアを、ということで全員ゼロベースで考え直したら案外出てきました 笑

###使用技術
* バックエンド: Node.js
* フロントエンド: React
* DB: postgres
* 機械学習: Word2Vec
* インフラ: AWS

メンバーが使える技術から選んだところ上記のような構成に。レビューと画像のセットをネットからとってきて、そのレビューを機械学習の入力として用いています。正直なところ普通にCNNとか転移学習とかでやって欲しかった... DBも最初はDynamoDBとかのNoSQLでしたが、APIが使えないとかでRDSのPostgresに変えました。 Migration機構がNodeだとキツかったのでそこだけはKotlinで書きました。

構成図はこんな感じ。
![Untitled Diagram.png](https://qiita-image-store.s3.amazonaws.com/0/185233/1e765599-9c42-c0d3-e003-63502fbb9543.png)

###開発過程
簡単に4 termで分類

* 15:30 ~ 20:00
 - デザイナー: 簡単なデザインが完成。
 - フロント: デザインを元に作り始める。
 - バックエンド: インフラ周りの構築
 - 機械学習: データの収集方法調査
* 20:00 ~ 3:00
 - デザイナー: プレゼン資料の完成。
 - フロント: tinderのスワイプの箇所を実装
 - バックエンド: ExpressかいてDBのデータScraping
 - 機械学習: モデルを学習
* 3:00 ~ 8:00
 - デザイナー: プレゼンの準備
 - フロント: 大体のデザインの実装
 - バックエンド: RDSの設定でKotlinでデータ投入
 - 機械学習: 別のデータ収集方法を模索
* 8:00 ~ 14:00
 - デザイナー: プレゼンの準備
 - フロント: AmplifyへのデプロイとAPIとのつなぎ込み
 - バックエンド: Amplifyへデプロイとプレゼン準備。あとドメイン購入
 - 機械学習: モデルを学習してAPI化

##Good Points

####100万円が取れるアイデアを考えることができた。
他のチームが開発を始め遅れているという焦りを感じながらも、アイデアに妥協せず考え続けることができたのが良かったかなと。アイデアの配分の『新規性』『テーマに沿っているか』という点ではそれぞれ49/60, 45/60となかなか良い点数を貰えたので満足しています。

####初対面でもコミュニケーションが上手くとれた。
今回のハッカソンは事前にチームを組んで参加する形式のものでした。ほぼ初対面のメンバーで構成されているうちのチームはどうなるかと不安だったのですが、アイデア出し、要件定義などと結構円滑にコミュニケーションがとれたのかなと。呉越同舟という言葉もありますし、目的が一緒ですと案外うまくいくものです。

##Bad Points

####デザイナーが2人という貴重なリソースを無駄遣いしてしまった。
開発過程でもわかるのですが、後半明らかにデザイナーが暇になってしまいました。それにも関わらず、徹夜してチームについてきてくれたのは本当に感謝です。フロントで実装できる余裕がないと考えて追加のデザインなどはお願いしませんでしたが、それでもお願いできることは無限にあったなと後悔。

- mockだけでもいいのでspだけでなくpc用のデザインもお願いすればよかった。
- 機械学習のデータ収集などでリソースが比較的空いている間、フロントにリソースを割かせるべきだった。

####機械学習サイドともっと情報共有するべきだった
機械学習側の仕様が全く決まっていなかったので、かなり振り回されました 笑。実装はレビューの日本語文章をベクトル化するところまでで終わってしまいました。なんでdoc2vecじゃないのかなーとか思ったり。他にもバックエンドの方であらかじめDBに全部ベクトル化して突っ込むなどの事前処理も共有していただいたらできたのかなと。 ひとえに情報共有の甘さが露呈した感じですね。あと結果論ですが、個人的にはやっぱりCNNでやりたかったなー。

- 情報共有が甘かった
- モデルをよく考えて選んでもらうべきだった

インフラで無駄に時間を喰った。
ハッカソンにおいて時間は一番大事ですので、その時間をFargateの設定とかいうどうでもいい箇所に費やすべきではなかったなと。あらかじめ作成しておいて、Dockerでサクッと載せるぐらいが丁度いいかな。その分を他のメンバーのサポートに回せればもっと完成度が高くなったのかなと。

- AWSはあらかじめ構築しておけば時間短縮できた。

##まとめ
テーマが『食を楽しくする』とのことで、グルナビAPIを用いたレコメンド系のサービスが多かったのです。そんな中でOkonomyは

- MLを用いたレコメンドの実装
- フリックだけで使える簡単なUI
の2点を評価してもらったのかなと勝手に予想しております。

賞金は新しいパソコンでも買いますかね。ちょうど仕事用のMacBookPro欲しいと思ってたんですよね。
Trunkハッカソンは来年も開催されると思いますので、学生の方は是非出場して見てはいかがでしょうか!
Google  Developers ML Summitに参加してきました

2019/3/20にGoogle Japan本社で行われたGoogle  Developers ML Summitに参加してきました。
公式のページはこちら
https://events.withgoogle.com/google-developers-ml-summit-tokyo-vol-2/

午前は六本木ヒルズのアカデミックタワーの49階での講演で、午後はGoogle本社でのハンズオンでした。

##機械学習の概要
機械学習の中でも特に、深層学習に特化して説明されてました。例として、

・mnist datasetを用いた手書き数字の識別
・体重と身長から、子供か大人かを判別する
・渦巻き状の点データから、隠れ層を増やし、境界を正確につける

##Cloud ML API入門
Cloud ML APIのページ
https://cloud.google.com/products/ai/
Cloud ML API の Machine Learning API は機械学習の専門知識なしですぐに使い始められます。

大きく分けて三つあり、視覚、言語、会話に分類されます視覚は画像や動画分析。言語は機械、翻訳テキスト分析。会話は音声認識、音声合成といった内容です。


##TensolFlowの紹介
話者のLaurenceがスピーチを英語でしていたので、それを日本語に同時通訳するイヤホンを聞きながら、さらにGoogleの音声アシスタントで、画面上にスピーチしている内容がそのまま出力されるという、スピーチの技術力に度肝を抜かれました。

##Ml Kit 事例紹介
Ml Kit の紹介ページ
https://firebase.google.com/products/ml-kit/?hl=ja

ZOZO テクノロジーズの方が開発したWearというアプリの紹介がありました。

ユーザーが投稿したコーディネートの服のブランドをを機械学習で自動に判別しブランド名を自動で入力してもらうという機能を実装していました。

さらに プロトタイプのアプリであるファッションの点数をアプリで自動判別するものでは、ファッションをカメラで記録しそのファッションを点数化するというものでした。
この点数化の部分に Wear のコーディネートのいいね数のデータをもとに学習させ、点数をつけるという方法を採用していました。

##Actions on Google 事例紹介
こちらの事例ではヤマト運輸の営業部の方がスピーチをして実際に業務の中で行われている再配達を Google Assistantで行なっていました。


##ハンズオン
午後の部は、Tensorflowのハンズオンに参加してきました。
機械学習とはいっても、数学の知識なしでの進行なので初学者にとって優しい内容だったと思います。
ランタイムの設定で、GPUに変更しないと、実行にかなり時間がかかるとのことです。

資料はこちら
https://medium.com/@lmoroney_40129/codelabs-from-googlemlsummit-f9d53cac8d24

##まとめ
午前はGoogleが提供しているMLサービスの紹介で、午後はそのサービスを実際に使ってみるという内容でした。インプットとアウトプットを行うことで知識を身につけられたかと思います。
Swift で `Method cannot be marked @objc because its result type cannot be represented in Objective-C` が出るもクラスの定義がライブラリ内なとき

swift 4

# 課題
https://stackoverflow.com/questions/49552501/method-cannot-be-marked-objc-because-its-result-type-cannot-be-represented-in-o

とかにあるように、 `@objc` 修飾子をつけた場合、定義されているクラスは Objective-C から見られるよう NSObject を継承しなければならない

が、この例でいう `User` が、自分が定義しているクラスじゃないときは、

```swift
class User: NSObject
```

と書くことは当然できない。

# 解決策（の一例）
方針としては、

* objc 修飾子をつけたい func では、 Objective-C で解決できるようなものを指定する
* 必要であれば、 Swift 側で、それを適切に戻してあげる

たとえば `enum State: String` を受け取りたい callback の protocol があるとして、

```swift
@objc protocol SomethingDelegate {
    @objc optional
    func callback(state: String?, error : Error?)
```

のように、 State ではなく String で定義する。で、 Objective-C に渡す callback を swift 上では、下記のように書いてあげる

```swift
            callback: { (state: State?) in
                delegate.callback?(state: state?.rawValue, error: error)
            }
```

すると、 `Method cannot be marked @objc because its result type cannot be represented in Objective-C` を言われずに済む。


載せられるコードだけ部分的に載せたから、いい例になってなかったらごめんなさい。
Selenium+Rubyに入門してみる①

以下の記事を参考に実行してみる

5分でできる！Seleniumでブラウザテスト自動化入門
https://engineers.weddingpark.co.jp/?p=1363

Macを買ったばかりなので、空の状態から開始
macOS Mojava : 10.14.3

###HomeBrewのインストール
特に問題なし

###Rubyのインストール
適当にruby 2.5.4をインストール。

rubyのバージョンがなんかうまく切り替わらなかったが、
以下を参考にしてうまくいった・・・はず
https://qiita.com/akatsuki174/items/c0384b9903b4b5cbbdaf

###Seleniumのインストール
chromedriverのインストールでエラーが発生

```
$ brew install chromedriver
Updating Homebrew...
Error: No available formula with the name "chromedriver" 
It was migrated from homebrew/core to homebrew/cask.
You can access it again by running:
  brew tap homebrew/cask
And then you can install it by running:
  brew cask install chromedriver
```

エラーメッセージ通り、以下を実行したらインストールできた。
```brew tap homebrew/cask```
```brew cask install chromedriver```

###関連ライブラリのインストール(Capybara,RSpec)
特に問題なし

###プログラム作成〜実行
中身はいまいちよくわかってないけど、真似して実行。
chromeが立ち上がって、入力＞ページ遷移までできた！

ここで紹介されているページを使ってテストしているっぽい。
色々試せそう。
http://www.selenium.jp/test-site

インストールした環境
Ruby : 2.5.4
rbenv : 1.1.1
RSpec : 3.8.0
Capybara : 3.14.0
Selenium Webdriver : 3.141.0
ChromeDriver : 73.0.3683.68　？？？

次は以下をやってみる予定
http://katsulog.tech/ruby-selenium-webdriver-tutorial/
AWSでSiteGuard WP Pluginを使いログインページ変更しました

AWSのEC2にインストールしたWordPressで、Guard WP Pluginを使いログインページの変更することができませんでした。
下記の手順で、ログインページを変更できました。

サーバーのOSは、こちらです。
Ubuntu 14.04.5 LTS (GNU/Linux 3.13.0-125-generic x86_64)
WordPressは、こちらです。
Bitnami WordPress 4.8.1-0

まず、下記の記事を参考にしました。
[AWSでmod_rewriteが効かない場合](https://yasigani-ni.com/php/aws%E3%81%A7mod_rewrite%E3%81%8C%E5%8A%B9%E3%81%8B%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88/)

この記事で書かれているパスでは、目的のファイルが見つかりませんでした。
いろいろ探して、下記の場所にありました。

```
/opt/bitnami/apache2/conf/httpd.conf
```

下記のコマンドを入力してファイルを開き修正しました。

```
sudo vi /opt/bitnami/apache2/conf/httpd.conf
```

リスタートしました。

```
service httpd restart
```
AWSは、.htaccessが無効と知りました。
https://qiita.com/hnagao/items/b7b35ad01a8ba8a42548

このコマンドで修正しました。

```
sudo vi /opt/bitnami/apps/wordpress/conf/htaccess.conf
```

下記のコードを追加しました。

```
<Directory "/opt/bitnami/apps/wordpress/htdocs/">
RewriteRule ^hoge(.*)$ wp-login.php$1 [L]
</Directory>
```
hogeの部分が、実際の変更したログインページです。
SiteGuard WP Pluginでログインページを変更すると、メールが届きます。
このメールで、変更したログインページのURLを確認できます。

修正方法は、下記の記事を参考にしました。
http://kzhishu.hatenablog.jp/entry/2015/12/07/090000

ファイルを修正したら、apacheのリスタートを忘れずに行なってください。

```
sudo /opt/bitnami/ctlscript.sh restart apache
```

今回の対応にあたり、スタックオーバーフローで教えてくださった皆様に感謝しています。
https://ja.stackoverflow.com/questions/53501/aws%E3%81%AEec2%E3%81%A7mod-rewrite%E3%81%8C%E4%BD%BF%E3%81%88%E3%81%AA%E3%81%84%E3%82%88%E3%81%86%E3%81%A7%E3%81%99/53596#53596
XAMPP環境にLaravelを導入する

#はじめに
前回の記事（[WindowsにPHPの環境を作る（XAMPPのインストール）](https://qiita.com/qtminuro/items/d7f2b95b922ae302577c)）にてPHPの環境としてXAMPPをインストールしました。
フレームワークを使ってみたいと思い、Laravelの環境を作りました。

#環境
・OS：Windows 10 home
・PHP：7.3.2
インストールしたもの
・Laravel：5.8.3

#Composer
Composerというものをインストールします。
これはパッケージ管理システムで、ファイルの依存関係も考慮していろいろダウンロードしてくれるものらしいです。

以下のリンク先からComposerをダウンロードします。
[Composer](https://getcomposer.org/)

Downloadを押します。
![1.png](https://qiita-image-store.s3.amazonaws.com/0/311225/d190ad66-d983-76aa-21ef-d4b3a5fb8891.png)

Composer-Setup.exeを押します。
![2.png](https://qiita-image-store.s3.amazonaws.com/0/311225/e851df15-b62f-4493-2f29-b5973cdfb01b.png)

ダウンロードしたインストーラを起動します。
![3.png](https://qiita-image-store.s3.amazonaws.com/0/311225/e256883b-8b47-9db0-4876-f3b219813eb9.png)

php.exeを選択します。
![4.png](https://qiita-image-store.s3.amazonaws.com/0/311225/2d0e7d1b-59bd-61b3-2c33-24234223aa41.png)

後は特に設定を変えずにNextを押して進みます。
以下の画面で終了です。
![8.png](https://qiita-image-store.s3.amazonaws.com/0/311225/3fe76fa5-e109-d4d5-ab33-fe3fee28cf39.png)

#プロジェクトの作成（Laravelのインストール）
コマンドプロンプト等を起動し、xampp配下のhtdocsにカレントディレクトリを移動します。
![1-2.png](https://qiita-image-store.s3.amazonaws.com/0/311225/9703b5c6-8157-50a8-b7ee-261376068489.png)
以下のコマンドを実行します。（「sample」の部分は任意のプロジェクト名です。）

```
　composer create-project --prefer-dist laravel/laravel sample
```
以下のように、「Application key set successfully.」と出て終了します。
![3-2.png](https://qiita-image-store.s3.amazonaws.com/0/311225/3c34ac69-e641-9b38-3d7a-e81dd1141708.png)

#実行
前回と同様にサーバーを起動します。
![x3.png](https://qiita-image-store.s3.amazonaws.com/0/311225/cebfd0e6-2ff7-6c57-3508-90b8d9d02d83.png)
以下のアドレスにアクセスします。

```
　http://localhost/sample/public
```
以下のページが表示されると成功です。
![l1.png](https://qiita-image-store.s3.amazonaws.com/0/311225/732af468-51e3-2819-ff23-02c0c27bb8a6.png)
プログラミングとは

プログラミング初学者として，日常的に何気なく **「プログラミング」** というワードを使っているわけですが，プログラミング学習をはじめるにあたって，言葉の意味を押さえておくことは重要だなと思いこの記事を書きました．
今回は，プログラミングとは何かを解説していきます．


# プログラミングとは
一般的に述べられているプログラミングとは…

コンピュータのプログラミング（英: programming）とは、コンピュータプログラムを作成することにより、人間の意図した処理を行うようにコンピュータに指示を与える行為である。[(wiki)](https://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0_(%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF))

つまり，**人間がコンピューターにやって欲しいことを伝え，伝えた通りに動いてもらうこと** なのです．

そのためには，まずコンピューターが理解できる言葉や文章を用いてなければなりません．


# コンピューターが理解できる言語
コンピューターが理解できる言語のことを，プログラミング言語と言います．

人間が扱う言語は主要言語で約6000以上あると言われており，そのうちの1つを指しますが，一口にプログラミング言語といっても様々な種類の言語に分かれています．

PHP，Python，Java，C#，Rubyなどなど10種類以上あり，**それぞれ得意とする分野があること，学習難易度が異なることなど特徴があります**．

例えば，**Ruby**．

これは，Webアプリケーションやホームページの開発などで使用されており，プログラミング初学者が学習しやすい言語と言われています．

Cookpadや食べログはこのRubyという言語で作られています．


# プログラミング言語の特徴
先に挙げたようなwebアプリケーションのように，プログラミング言語を用いることで様々なアプリやサービスを作ることができますが，**特徴的なルールを押さえておく必要があります**．

 1. 手順を伝える
　料理で例えると…
　1. 作るものを決める
　2. 必要な食材，道具を揃える
　3. 下ごしらえをする（切る，剥くなど）
　4. 調理する
　5. 盛りつける　となると思います．**このような一連の流れや，条件付けはプログラミングにおいてもこの手順はめちゃくちゃ大切です**．

 2. 正確に記す
　上記の料理では「5. 盛りつける」といっても千差万別．このような曖昧な表現ではコンピューターには全く理解してもらえません．
　直径20cm皿の右2/3の位置に200gのライスを盛りつけ，残り左1/3の位置にカレールー150gをそそぐ．といったように **正確に，明確に指示する必要があります**．

 3. 文章で伝える
　あるまとまった意味をなす文字や単語の連なりのことを文章と言います．
まずは，**プログラミング言語特有の文字や単語を知る必要があります**．基本は英語．中高生レベルの英単語や文法が理解できていれば大丈夫．あとは各プログラミング言語特有の言い回しや文法を理解できれば文章を書くことは容易です．

#まとめ
**・プログラミングとは，プログラミング言語を用いてコンピューターに手順を正確に記した文章を伝え，それに応えて動いて貰うこと**
(win)Pythonでsqlite3を用いてFirefoxの履歴を取得する(個人メモ)

```GetHistory_Firefox.py
#<sqlite3>ライブラリをインポート
import sqlite3

#Firefoxの履歴が保存されているsqliteファイルを指定(◆◆◆は各自で変更)
conn = sqlite3.connect("C:\\Users\\◆◆◆\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\◆◆◆\\places.sqlite")

# データベース接続とカーソル生成
cur = conn.cursor()

#executeを用いてSqlite3のコマンドを実行
#places.sqlite内のmoz_placesの項目にid,url,title,他・・・と列で並んで保存されている。
cur.execute("select * from moz_places")

#履歴に保存されている全てのTitleとURLを表示
for row in cur:
	print(row[2],row[1],)#Tile,URL
```
Singularityのインストール (v3)

# 概要

Singularity v3のインストール手順を説明します。Singularityはv3以前はC言語で実装されていましたが、v3からGo言語で再実装されたためインストール方法が大きく変わっています。v2以前のインストール方法は[以前の投稿](https://qiita.com/a210/items/a38101cec8084c066087)を参照ください。

インストールは以下の流れで行います。

1. 必要なパッケージをインストールします
2. Goをインストールします
3. GitHubからSingularityのソースを入手し、ビルドします

本稿においては、Debian系LinuxとRHEL系Linuxへのインストール手順を説明します。自身の環境ではUbuntu18.04およびCentOS7.6において以下手順で動作確認を行いました。同様の手順でWindows Subsystem for Linux (Ubuntu18.04)へのインストールも可能ですが、まともに動作しません(2019年3月時点)。

インストール手順のより詳細な説明については[公式マニュアル](https://www.sylabs.io/guides/3.0/user-guide/quick_start.html#quick-installation-steps)を参照ください。


# インストール手順

## パッケージのインストール
必要なパッケージ群をインストールします。  

### Debian系Linux
```bash
$ sudo apt-get update -y
$ sudo apt-get install -y build-essential libssl-dev uuid-dev libgpgme11-dev squashfs-tools
```

### RHEL系Linux
```bash
$ sudo yum update -y
$ sudo yum groupinstall -y 'Development Tools'
$ sudo yum install -y epel-release
$ sudo yum install -y openssl-devel libuuid-devel libseccomp-devel squashfs-tools
```

なお、RHEL/CentOS6以前のバージョンでは`libseccomp-devel`は不要のようです。

## Goのインストール
Goの実行環境を構築します。  

### Debian系Linux
以下の例ではv1.11.5をインストールしていますが、バージョンは適宜変更してください。

```bash
$ export VERSION=1.11.5 OS=linux ARCH=amd64
$ cd /tmp
$ wget https://dl.google.com/go/go${VERSION}.${OS}-${ARCH}.tar.gz
$ tar -C /usr/local -xzf go${VERSION}.${OS}-${ARCH}.tar.gz
```

### RHEL系Linux
`yum`コマンドによりインストールが可能です。

```bash
$ sudo yum install -y golang 
```

### Debian/RHEL系Linux共通
Singularityのビルドには`${GOPATH}`という環境変数が使用されるため、この設定を`~/.bashrc`に記述しておきます。

```bash
$ echo 'export GOPATH=${HOME}/go' >> ~/.bashrc
$ echo 'export PATH=/usr/local/go/bin:${PATH}:${GOPATH}/bin' >> ~/.bashrc
$ source ~/.bashrc
```
## Singularityのビルド・インストール
Singularityのソースをクローンし、ビルドします。特定のバージョンを選択したい場合は[リリースタグ](https://github.com/sylabs/singularity/tags)を参照ください。

```bash
$ mkdir -p ${GOPATH}/src/github.com/sylabs
$ cd ${GOPATH}/src/github.com/sylabs
$ git clone https://github.com/sylabs/singularity.git
$ cd singularity
```
(オプション) 特定バージョンをインストールする場合はチェックアウトします。

```bash
$ export SVERSION=v3.1.0
$ git checkout ${SVERSION}
```
依存関係をDepにより解決し、Singularityバイナリをビルドします。

```bash
$ go get -u -v github.com/golang/dep/cmd/dep
$ ./mconfig
$ make -C builddir
$ sudo make -C builddir install
```
`singularity`コマンドを実行し、問題なく動作することを確認します。

```bash
$ singularity version
3.1.0
```
Rubyの正規表現で〇〇回以上繰り返されたことを確認する


`{n,}`を使う。
n回以上(nは数字)にマッチする。

5桁の数字に対して確認。

5桁以上

```
 pry(main)> "12345".match(/[1-9]{5,}/)                                                                                                                                                 
=> #<MatchData "12345">
```

6桁以上

```
pry(main)> "12345".match(/[1-9]{6,}/)                                                                                                                                                 
=> nil
```
* {,m} m回以下(mは数字)
* {n} ちょうどn回(nは数字)
【mac】Finderのよく使うショートカットまとめ

</br>
PCをmacに変えてからFinderのショートカットをよく忘れてしまうので
備忘録として、残しておく。

<h2>よく使うコマンド</h2>
 「Command」＋「↑」  親フォルダに移動 
 「Command」＋「↓」  子フォルダに移動orアプリケーションを開く 
「Shift」＋「Command」＋「↑」  デスクトップ上にフォーカス移動 
「Return」キー  名前の変更 
「スペース」キー  QuickLook 
「Command」＋「Delete」  ゴミ箱にファイルを捨てる 
「Command」＋「A」  すべて選択 
「Command」＋「C」  選択したアイテムをコピー 
「Command」＋「N」  新規Finderウインドウ 
「Command」＋「X」  カット 
「Shift」＋「Command」＋「A」  「アプリケーション」フォルダに移動
「Shift」＋「Command」＋「C」  「コンピュータ」ウインドウに移動
「Shift」＋「Command」＋「D」  「デスクトップ」フォルダに移動
「Shift」＋「Command」＋「H」  「ユーザのホームフォルダ」に移動
ABC083 C - Multiple Gift (300点)

#問題概要
[問題のリンク](https://atcoder.jp/contests/abc083/tasks/arc088_a)

整数 $X$ と$Y$ が与えられる。

以下の条件を満たす数列Aを作る。

- $A$は$X$以上$Y$以下の整数からなる
- すべての$1 \leqq i \leqq |A|−1$に対し、$A_{i+1}$は $A_i$の倍数であり、かつ $A_{i+1}$ は $A_i$ より真に大きい

数列の長さが最大になるようにした時に、その最大値を求めなさい。

#制約条件
- $1\leqq X \leqq Y \leqq 10^{18}$
- 入力は全て整数である

#考えたこと
**「$A_{i+1}$は $A_i$の倍数であり、かつ $A_{i+1}$ は $A_i$ より真に大きい」**

という条件により $A_{i+1}$ は $A_i$ を正の整数倍(2以上)したものであることがわかる。よって配列に多くつめていくには、$A_i$の係数が小さければ小さいほどよい。2以上である必要があるので係数の最小値は2となる。
よって$Y$以上になるまで$X$に2をかけていく処理の回数を数えていけば答えが出る。

2の掛け算の繰り返しなので計算量は $O(\log n)$となる。よって制約条件$1\leqq X \leqq Y \leqq 10^{18}$は十分クリア。

あとは書くだけ。

#コード
```c.cs
using System;
using System.Linq;

class Program
{
    static void Main(string[] args) {
        long[] s = Console.ReadLine().Split().Select(long.Parse).ToArray();
        Console.WriteLine(solve(s[0], s[1]));
    }
    static int solve(long x, long y) {
        int count = 0;
        while(x <= y) {
            count ++;
            x *= 2;
        }
        return count;
    }
}
```
Djangoでhttpsにリダイレクトする簡単な方法

Djangoで作成したサイトをhttpsにリダイレクトさせるにはsetting.pyに以下の二行を追加します。

```python:setting.py
SECURE_SSL_REDIRECT = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
```

私はDjangoとAWSで自分のサイトを作成しているのですが、この機能を知らずにAWSの設定を変えたり、Apache使ったり苦戦してすごく時間を取られました笑
ぐるなびAPIを使って、営業時間がすぐにわかる飲食店検索サイトを作った

[ぐるなびAPI](https://api.gnavi.co.jp/api/)を使って、営業時間がすぐにわかる飲食店検索サイトを作った。

#[めしタイム](https://meshitime.herokuapp.com)


[![めしタイム](https://qiita-image-store.s3.amazonaws.com/0/240417/ba3ab269-da3b-79d8-cffb-f2e35590d694.png "めしタイム")](https://meshitime.herokuapp.com/)

めしタイムの作成中に考えたことや、やったことなどを書いていく。

##自己紹介
- Railsをメインに独学中の人。SESで働いて2年が経とうとしている。(24歳)
- HTMLの存在を1年前に知った
- Qiita初投稿

## なぜ作ったか
- 役に立つwebアプリを作ってみたかった
- ある特定の飲食店の営業時間を知りたいときに、スマホでググって大手グルメサイトのページに飛んで、下にスクロールという操作を簡略化したかった

## なぜぐるなびAPIを使うのか
- ホットペッパーグルメのAPIと迷ったが、ぐるなびの方が取得できる店の数が多かったため

## 使う場面の例
- ある特定の飲食店の開店時間、または閉店時間を知りたいとき   
検索キーワードの例「新小岩 牛角」
- その地域または駅付近で朝早くからやっている、または夜遅くまでやっている店を知りたいとき  
検索キーワードの例「目黒駅 居酒屋」

## 使った技術
- Rails
- Heroku
- Bulma

Herokuは無料プラン

## Herokuのdynoスリープ回避方法
- [uptimerobot](https://uptimerobot.com/)を使った

## .envを使用したときの注意点
- [こちらの記事](https://dackdive.hateblo.jp/entry/2016/01/26/121900)を参考にした

## コードの抜粋
``` shops_controller.rb
class ShopsController < ApplicationController
  def top
  end

  def create
    keyword = params[:keyword]
    redirect_to "/shops?keyword=#{keyword}"
  end

  def show
    begin
    keyword = params[:keyword]

    require 'open-uri'
    require 'json'
    require 'active_support'
    require 'active_support/core_ext'

    uri = "https://api.gnavi.co.jp/RestSearchAPI/20171213/"
    access_key = ENV['ACCESS_KEY']
    url = uri << "?keyid=" << access_key << "&hit_per_page=100"  << "&freeword=" << keyword
    url = URI.encode url
    json = open(url)
    hash = Hash.from_xml json
    shops = []

    if hash.has_key?("response")
      hash["response"]["rest"].each do |shop|
        shops.push({
          name: shop["name"],
          url: shop["url"],
          opentime: shop["opentime"],
          holiday: shop["holiday"],
          image1: shop["image_url"]["shop_image1"],
          image2: shop["image_url"]["shop_image2"]
        })
      end
    elsif hash.has_key?("gnavi")
      if hash["gnavi"].has_key?("error")
          if hash["gnavi"]["error"]["code"] == "600"
            @error = "該当する店舗の情報が存在しません"
          end
      end
    else
      @error = "エラーが発生しました"
    end

    rescue
      @error = "エラーが発生しました"
    end
    @shops = Kaminari.paginate_array(shops).page(params[:page]).per(10)
  end

  def description
  end

end

```

## 最初のリリースまでにかかった時間
- 10時間程度  
最初に公開したのは3月4日

## ユーザーからフィードバックを頂いた
- チャットサイトを使ってユーザーを集め、フィードバックを頂いた

## ユーザーフィードバックの抜粋
- どんなキーワードを入力すればいいのか分からない
- 検索画面が簡素すぎて食欲が湧かない
- 地域指定を入れてほしい
- 食事関係なのにTwitter色はどうなのか
- シンプルすぎてなんか怪しい
- 検索した後の表示が遅い
- トップページを見て何のサイトか明確じゃない

## ユーザーフィードバックをもとに修正
-  place holderの文字を変更した  
「キーワードを入力」→「キーワード(駅名、店名、料理名など)を入力」

## 今後修正したい箇所
- ページネーションをページが切り替わるのではなく、「もっと見る」ボタンを押したら、下に表示されるようにしたい
- 検索した後のレスポンスが遅いのでなんとかしたい

## ユーザー数とPV
(リリースから2週間経過)

- 170ユーザー
- 1000PV  
([Googleアナリティクスで自分のアクセスを除外する方法](https://wacul-ai.com/blog/access-analysis/google-analytics-setting/exclude-your-access/))  
([Googleアナリティクスで自分のスマホのアクセスを除外する方法](https://www.tentecomai.com/entry/how-to-opt-out-in-google-analytics))

## まとめ
- 作るの面白い！！


## 追記(2020/3/30)  
サイトは閉じたが、Flutterでアプリを作ってみた。
https://play.google.com/store/apps/details?id=meshitime.meshitime
SSE-KMS を指定したCloudTrailの証跡作成時エラーの原因と対処

あるアカウントのCloudTrail設定時に遭遇したエラー。
メッセージからすぐに原因が分からなかったので残しておきます。

## 事象
CloudTrailでSSE-KMSによってカスタマーマスターキー(CMK)
を使用したログの暗号化設定を有効化しようとすると、
**キーポリシーの CloudTrail の権限が十分でありません。**
というエラーメッセージが表示され、証跡の作成/保存ができない。
設定を行うIAMユーザーの権限には問題がないことを確認済み。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/8fe0e6ff-4fc1-d80e-9812-ef91839d08c6.png)

また新規のKMSキーの作成を選択した場合は、キーの自動作成自体は成功している。

## 発生条件と原因
新規作成ではなく、既存のS3バケットを指定しており、
バケットポリシーでSSE-S3による暗号化を要求している場合に発生する。

具体的には以下のようなポリシーを含む場合。

```policy.json
        {
            "Sid": "DenyUnEncryptedObjectUploads",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::<your_bucket_name>/*",
            "Condition": {
                "StringNotEquals": {
                    "s3:x-amz-server-side-encryption": "AES256"
                }
            }
        }
```

### 補足
上記は PutOubject リクエストに SSE-S3 によるサーバー側の暗号化を要求する 
x-amz-server-side-encryption ヘッダーが含まれていない場合は、
アップロードを拒否するという内容です。
SSE-KMS を有効化した証跡はこの条件に合致しませんので設定時にエラーとなります。

## 対処
同等のポリシーを維持したい場合は、s3:x-amz-server-side-encryption キーを
AES256 ではなく、aws:kms とする。

```policy2.json
        {
            "Sid": "DenyUnEncryptedObjectUploads",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::<your_bucket_name>/*",
            "Condition": {
                "StringNotEquals": {
                    "s3:x-amz-server-side-encryption": "aws:kms"
                }
            }
        }
```

### 補足
通常は CloudTrail 専用のバケットを作成すると思いますので、
ポリシーによる暗号化の要求は不要でないかと個人的には感じます。
その場合該当のポリシーを丸ごと削除でも対応できます。

分かってしまえば当然のエラーなのですが、エラーメッセージからは
想像しづらい原因でした。
他の原因でも表示されるメッセージかもしれませんが、参考になれば幸いです。

### 関連ドキュメント
**Amazon S3 で管理された暗号化キーによるサーバー側の暗号化 (SSE-S3) を使用したデータの保護**
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/UsingServerSideEncryption.html

**AWS KMS で管理されたキー (SSE-KMS) によるサーバー側の暗号化を使用してデータを保護する**
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/UsingKMSEncryption.html