OmniAICreator/Qiita-1.07M huggingface dataset:
リード全削除するの巻


メリークリスマス　皆様:champagne::birthday:

アルマクリエイション　大伴です。
セールスフォースとPardotの管理者として働いております。

今回は、私が「リード」と「個人取引先」の重複、Salesforce⇔Pardotとのやりとりにおいて苦労した点、注意したほうがいい点などを紹介させていただきます。

# 「個人取引先とは」・・・

2019年7月に入社した際に、初めて、この会社にて、「個人取引先」という概念を知りました。

個人取引先とはなんぞや・・・（SFDCサイトより抜粋）
“特定の取引先項目および取引先責任者項目が 1 つのレコードに結合されて、個人に関する情報が保存されます。”

## その前に重要事項のご説明。

（以下、SFDCサイトより抜粋）
**個人取引先は一度有効にすると、無効にできません。**
Sandbox を作成して個人取引先が Salesforce 組織に与える影響をプレビューすることをお勧めします。
https://help.salesforce.com/articleView?id=account_person_enable.htm&type=5

###### ということで
私が入社した時点では既に「個人取引先」が有効になってしまってる状態では、もう無効にすることは出来ないので、「個人取引先」として管理していくことで合っているの？とか、もしかして、違うんじゃ、、
とか議論をしても時すでに遅し！なのでした。

弊社の場合は、セールスフォースでは、法人のお客様ではなく、個人のお客様として顧客管理をしていくという方針なので、「個人取引先」として管理していくということで問題ない、ということです。

######ここで、問題になったのは、
大量に登録されているリードが、個人取引先とかなり重複している・・・

######なぜ・・・!?

という問題でした。

確認すると、

弊社独自システムから入ってくるお客様情報は、セールスフォースへ個人取引先として流れてくる
そして、Pardotのフォームから流れてくる情報は、リードとして登録される、ということでした。

なんで、リードで登録されるんじゃーぃ！！！

ということを調査していくと、
**リードとして登録するか、個人取引先として登録するかを、Pardotのほうで選択できるようになっている**　とのことでした。
その設定の変更は、**自社では行えず、セールスフォースのサポートチームへ依頼**　しないといけないので、皆さんもご注意ください。

そんなこんなで重複がわんさか・・・
一旦、どないしよ。。。と悩みに悩みました。

ただ、今回、2020年1月から新システムの稼働をプロジェクトとして掲げていたこともあり、荒業にでることにしました。
Pardotに入っているリードをアクティビティごと全削除する、と決断しました。
（きっと通常の会社様はしないであろう・・・あ、そしたら、このブログの意味があまり・・・参考にならないですよね:thinking::pray:）

リードを全て削除し、重複していないリードの全情報を個人取引先としてセールスフォースへ入れ、その取引先責任者IDをPardotの静的リストとして再度、インポートして情報を紐づけるということを考えました。

######まず、前提のお話ですが、

弊社、メルマガの種類がたくさんあります。
各事業部がそれぞれで施策を企画し、それぞれが、メールを書き、メルマガを出しているという感じです。
社員のPardot利用率がかなりの高さです。

ということもあり、
プリファレンスページを作成し、それぞれで登録解除などを管理しています。
そこで、お客様がどのメルマガリストに属しているのかということがとても重要な情報になってきます。

プリファレンスページはメルマガがたくさんある場合は、とても有効です！！！

プリファレンスページで管理してセールスフォースへビジュアルページとして表示させてみてはどうだろう、とアドバイスをくださったのは、[csyk1004](https://qiita.com/csyk1004)様　です。　　
あの日は本当に感謝感激感動でした。いつぞやのSalesforceSaturdayでした。このご恩は忘れません。

話が少しそれましたが、作業内容としては、以下です。
dataloderフル活用です！

作業の詳細内容
1.　全メルマガリストのメール可能なリストをリスト毎にエクスポートする
2.　その各CSVからe-mailのリストを作成
3.　dataloderで取引先とリードのデータ全件エクスポートする
4.　2で作ったメールアドレスがリードのCSVに含まれているもののみ抽出する
5.　4で作ったリストから3の取引先のリストにあるものを削除する
6.　全リードを削除する
7.　Pardotのゴミ箱に入っているので永久削除する　※注意a
8.　5のメールアドレスを元に1のリストから名前とメールアドレスのCSVを作成する
9.　これをdataloderで7のリストを取引先としてインポートする
10.　dataloderで取引先責任者をエクスポートする
11. 　9で出したリストの取引先責任者IDを8のリストにマージする　※注意b
12.　Pardotへ1の各リストへ再インポートする

この流れで、全リードを削除し、個人取引先としてSFDCへインポートし、その取引先責任者IDを紐づけ、Pardotのメルマガリストへインポートすることで、もともとのメルマガリストにも入ってくる、いうことになります。
（注意：リードのときに紐づいていたアクティビティは消えます）


※注意a:永久削除をしておかないと、SFDCへdataloderでinsertできないのでご注意ください。ただ、本当に事前に確認してからやるようにしてください。

※注意b：個人取引先としてインポートし、その後、インポートリスト分をエクスポートし、取引先責任者IDをPardotへインポートするリストと突合すること

ちょっと荒業過ぎましたが、

今回は、2020年1月のプロジェクト始動が重要だったので、このような手段をとりました。
スーパーエンジニア山本さんのおかげで、ECサイトからの注文情報が全てSFDCに入ってくるようになったので、それをそのままPardotに引っ張れるようになりました！（カスタムオブジェクト連携です。）これで、いろいろな施策が簡単に打てるようになります！

今後は、しっかりデータを貯めて、分析に活かせるようにしていきたいです。

######最後に、
2019年7月からSalesforce＋Pardotのadminとして転職し、その後、業務があまりに孤独のため、SalesforceSaturdayというコミュニティを知り参加するようになりました。そこで普段の疑問や気になる点、分からないことなどを教えていただいたことが本当に自分の糧になっています。今後ともよろしくお願いいたします。この場をお借りしてお礼を申し上げます。
いつもありがとうございます。

皆さま、お会いする機会もあるかと思いますので、まだまだ未熟者ですが、今後とも、宜しくお願い致します。

拙い文章を最後まで読んでくださりありがとうございます。
nginx locationまとめ

# はじめに
nginxのlocationの設定でつまづくことがあったので、まとめる。
気付いた点があれば都度更新。

## 基本
URIのパス毎の設定を記述できる。
基本的には下記の形で記述できる。

```nginx
location プレフィックス URIのパス {
  # パスでの設定
}
```

### プレフィックスの種類
プレフィックスというものでパスの評価形式を指定できる。
下記テーブルの上から記述された順番で優先度が高く、
nginxのlocationの設定は優先度が最も高い設定が**一つだけ**適用される。

|プレフィックス |説明 |
|---|---|
|=|完全一致|
|^~|前方一致。一致したら正規表現を適用しない。|
|~|正規表現(大文字・小文字を区別する)|
|~*|正規表現(大文字・小文字を区別しない)|
|なし|前方一致|

前方一致の記述が複数ある場合は、一致する文字列が一番長いものが適用される。
正規表現の記述が複数ある場合は、設定ファイルに書いた順番で最初に一致したものが適用される。

## よく使いそうな正規表現
### トレイリングスラッシュ有無の統一

```nginx
location ~ ^/(.*)/$ {
  return 301 path # トレイリングスラッシュを消す
}
```

### 拡張子の排除

```nginx
location ~ \.拡張子$ {
  # 設定を記述
}
```

## 参考
[nginxの設定、その3 - locationディレクティブ](http://www.magic-object.mydns.jp/fedora20withnginx/)
[Nginx を導入する](https://heartbeats.jp/hbblog/2012/04/nginx05.html)
SwiftUIを簡易的にUserDefaultsを使う方法

# はじめに
SwiftUIでViewModelを使ったUserDefaultsを使う方法はよく見かけるのですが、簡易的にUserDefaultsを使う方法がありましたので紹介します。
あくまでの簡易的な使い方です。

# 今回のサンプルコード
TextFieldに文字入力が終わるとTextに入力した結果が表示されます。
今回は、文字入力が終わったタイミングでUserDefaultsに保持して、アプリ起動時にUserDefaultsから読み出します。

<img height="400" alt="1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42735/86201ade-b9c7-e529-8530-6fc0d1faeced.png"> <img height="400" alt="2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42735/3785af86-91dd-cf28-8165-693e688f2f9e.png"> <img height="400" alt="3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42735/62bfdd1b-c0e7-20d3-a483-e69b064334a8.png">

# コード
今回は、文字入力が終わると`onCommit`内が実行されるのでそこでUserDefaultsに保存してします。
`VStack{}`の後に`.onAppear`が表示前に呼ばれるのでそこでUserDefaultsから値を読み出しています。

```Swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    @State var inputText:String = ""
    @State var displayText:String = ""

    var body: some View {
        VStack {
            Text("Hello, \(self.displayText)!")
            TextField("Your name", text: $inputText , onCommit: {
                UserDefaults.standard.set(self.inputText, forKey: "name")
                self.displayText = self.inputText
                self.inputText = ""
                })
                .padding()
        }
        .onAppear {
            self.displayText = "World"
            guard let userdefaultText = UserDefaults.standard.value(forKey: "name") as? String else { return }
            self.displayText = userdefaultText
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

# 参考サイト
- [User Defaults In SwiftUI - How To Store Data Using User Defaults With SwiftUI](https://youtu.be/ufI1GO9Xxi4)
SwiftUIでTabBarを作る方法

# はじめに
SwiftUIでTabBarの作る方法を紹介します。
TIPSとして役立ててください。

# 今回の作るTabBar画面
`TabAView`と`TabBView`を作りそれをTabBarで画面を切り替えるSwiftUIを作ります。

<img height="500" alt="TabA.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42735/5aa993d9-532f-a92f-61ff-7a20ee4d97ef.png"> <img height="500" alt="TabB.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42735/33b84205-fdd7-2d21-2dc8-462e83b6e6e4.png">

# 作り方

## TabAViewとTabBViewを作る
File>New>File...でSwiftUI Viewで`TabAview`と`TabBView`を新規にファイル追加します。
中のコードは今回はわかりやすいように、Textに**This is Tab A.**と**This is Tab B.**と表示するようにします。

```Swift:TabAview.swift
import SwiftUI

struct TabAView: View {
    var body: some View {
        Text("This is Tab A.")
    }
}

struct TabAView_Previews: PreviewProvider {
    static var previews: some View {
        TabAView()
    }
}
```

```Swift:TabBview.swift
import SwiftUI

struct TabBView: View {
    var body: some View {
        Text("This is Tab B.")
    }
}

struct TabAView_Previews: PreviewProvider {
    static var previews: some View {
        TabAView()
    }
}
```

## ContentView.swiftにTabBarを定義する
TabViewと定義しその中に、TabBarの中のデザイン定義を`VStack{}`内に定義します。
今回は、アイコンと文字にしました。アイコンはどの環境にもあるSF Symbolを使っています。

```Swift:ContentView.swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        TabView {
            TabAView()
                .tabItem {
                    VStack {
                        Image(systemName: "a")
                        Text("TabA")
                    }
            }.tag(1)
            TabBView()
                .tabItem {
                    VStack {
                        Image(systemName: "bold")
                        Text("TabB")
                    }
            }.tag(2)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

# 参考サイト
- [Implementing TabView in SwiftUI](https://youtu.be/yRFzfuqjZao)
Django+venvの環境でi18nを使用できるようにGNU gettextをインストールする

#DjangoでGNU gettextを利用できるようにする

Djangoのi18n対応でGNU gettextをインストールする必要があったので、手順をまとめました。
https://leben.mobi/blog/django_i18n/python/ の補完的な記事です。
初めはbrewでgettextをインストールしたものの、
`django-admin.py makemessages -l ja`
を実行すると
`CommandError: Can't find msguniq. Make sure you have GNU gettext tools 0.15 or newer installed.`
というエラーが出てきてしまいました。
これは私がvenvを使用していたことに起因するものですが、venvにPATH variableを設定することでこのエラーが解消されました。

.....と思いきや、私の環境ではその後も
`.../bin/rst2html5.py:2: Unknown encoding "utf8". Proceeding with ASCII instead.`
という新たなエラーが出てきしまいました。どうやらこっちの方はvenv内のrst2html5.pyというファイルにあるバグが原因のようです。こちらのバグについては記事の最後で解説しています。


動作環境

- Mac OSX(10.14.6)
- Django  2.2.5
- homebrew

##homebrewでGettextをインストール

`brew install gettext`

##venvにPATH variableを設定
venv/binにあるactivateファイルを開いて、末尾に

`export PATH=$PATH:/usr/local/Cellar/gettext/YOUR_VERSION/bin`

を挿入する 
※YOUR_VERSIONは使用中のgettextのバージョンと差し替えてください

##rst2html5.py内のバグを直す
utf8という記述をutf-8に直す

これで上記のリンクの手順通りにi18nを使えるようになるはずです。
VSCodeの統合ターミナルのシェルをzshに変更する

# はじめに

macOS Catalinaからデフォルトシェルが`zsh`に変更になったので設定変更してみたが、VSCode開いたら統合ターミナルがまだ`bash`だったので変更した時のメモ。

# VSCodeの統合ターミナル

統合ターミナル開いた時に以下のようなエラーが出てた。(正確なエラーメッセージをメモし忘れたが、多分合ってる)
※参考: `anyenv`で`xxxenv`系をインストールしている環境です。

```sh
~/.anyenv/envs/nodenv/libexec/../completions/nodenv.bash:16: command not found: complete
```

## 解決方法

統合ターミナル上で

```sh
chsh -s /bin/zsh
```

を実行しても新しい統合ターミナルを開くとまたエラーメッセージが表示される。

`.zprofile`に書き加えて`eval "$(anyenv init - zsh)"`みたいなのを実行すればいい、という情報がヒットしたけど、既定のシェルをわざわざ書かないといけないのは違和感があったのでしばらく粘ってみた。

結果的に**Macを再起動**したらエラーメッセージが出なくなった。
(`chsh`した内容がどこかに保存される(想像)として、VSCodeからは上手く読み込めてなかった、ということなのかな。。。)


# おまけ: Macのターミナルのシェル

ターミナル起動時にワーニングが表示されるので、素直にそこに書かれてるコマンドを実行すべし。

```sh
chsh -s /bin/zsh
```

あと`.bash_profile`の内容をコピーして`.zprofile`という名前で保存する。
zshの設定ファイルはbash以上にいろいろあるらしいが、`xxxprofile`という名前どうしにしておくのが無難かな、という程度の判断。

```sh
source .zprofile
```

`.bash_profile`の中もあまり書いてなかったので、今のところ特に問題なし。
LeetCode: 1. Two Sums

# 問題の要約

整数配列から足したらnになる要素を２つ探し、そのインデックスを返す


# 問題
[問題へのリンク](https://leetcode.com/problems/two-sum/description/)

# 回答１　全探索
まずは、総当たりの回答を作成。

```cpp
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        for(int i=0; i<nums.size(); i++) {
            for(int j=1; j<nums.size(); j++) {
                if(i!=j && nums[i]+nums[j]==target){
                    return {i,j};
                }
            }
        }
        return {-1};
    }
};
```

```math 
時間計算量: O(N^2) 、空間計算量:O(1)
```
全探索でも提出すると意外とAccepted されました。
でも Runtime=268 ms、Memor=9 MB と実行時間が多めな結果になりました。

# 回答２　unordered_map
次に、ハッシュマップを使うことにしました。

```cpp
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int,int> hashMap ;
        for(int i=0; i<nums.size();i++) {
            hashMap[nums[i]] = i;
        }
        
        for(int i=0; i<nums.size();i++) {
            if( i != hashMap[target-nums[i]] &&
                hashMap[target-nums[i]] != 0) {
                return {i, hashMap[target-nums[i]]};
            } 
        }
        return {-1};
    }
};
```
```math 
時間計算量: O(N) 、空間計算量:O(1)
```
提出すると、Runtime=12 ms、Memory=11.1 MB と早くなりました。
回答１に比べて 22倍の速度を達成しました。
QGraphicsViewのスケールを取得する方法

# はじめに

こんばんは、[Qt Advent Calendar 2019](https://qiita.com/advent-calendar/2019/qt) の 24日目の記事です。
ケーキを作ってたら少し遅刻してしまいました。


# QGraphicsView内の移動、拡大縮小、回転する時の基本的な情報

QGraphicsViewはQTransformを使うことでビューの移動や拡大縮小、回転などか行えます。

以下がQTransformに含まれる3*3のマトリクスの図です。
![MatrixOperation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552929/ace48a7c-a96f-ac1b-5a22-c39db76e1a4f.png)

m31（ dx ）とm32（ dy ）要素は、水平方向と垂直方向の平行移動を指定します。m11とm22要素は、水平方向と垂直方向のスケーリングを指定します。 m21およびm12要素は、水平および垂直のせん断を指定します。そして最後に、 m13とm23要素は水平投影と垂直投影を指定し、追加の投影係数としてm33を指定します。

# スケールを取得する方法（不十分）

単純なQGraphicsViewの場合、次のコードで現在のQGraphicsViewのスケールを取得することができます。

```
    QTransform trans = this->transform();
    qreal xScale = trans.m11();
    qreal yScale = trans.m22();
```

また次のコードは、QTransformを使用してビューの移動と拡大、回転を行った例です。

```
    QGraphicsView* view = new QGraphicsView(this);
    setCentralWidget(view);
    QGraphicsScene* scene = new QGraphicsScene(this);
    view->setScene(scene);

    qreal x = 50.0;
    qreal y = 50.0;
    qreal angle = 45.0;
    qreal xScale = 2.0;
    qreal yScale = 2.0;

    double theta = M_PI/180 * angle;
    double sinTheta = sin(theta);
    double cosTheta = cos(theta);

    QTransform translationTransform(1, 0, 0, 1, x, y);
    QTransform rotationTransform(cosTheta, sinTheta, -sinTheta, cosTheta, 0, 0); // 回転！
    QTransform scalingTransform(xScale, 0, 0, yScale, 0, 0);                     // 拡大！
    transform = scalingTransform * rotationTransform * translationTransform;
    view->setTransform(transform);

```
回転と拡大の両方でm11とm22の両方の要素を使用しているのがわかります。
最初に示したコードでは、回転したビューで正しい値を取得することができません。

# スケールを取得する方法

回転に左右されない現在のQGraphicsViewのスケールの取得方法です。

```
    QTransform trans = this->transform();
    qreal xScale = qSqrt(qPow(trans.m11(), 2) + qPow(trans.m12(), 2));
    qreal yScale = qSqrt(qPow(trans.m21(), 2) + qPow(trans.m22(), 2));
```

# おわりに
おやすみなさい。
RxSwiftをざっくりと理解する(1)基礎用語まとめ

##はじめに
[andfactory Advent Calendar 23日目](https://qiita.com/advent-calendar/2019/andfactory)の記事です！
RxSwiftの内容を初学者に向けてざっくりとまとめてみました。
第1回は基礎用語です。


##Observable
それを購読(≒監視)しているオブジェクトに処理されるデータを流すもの。
 * Observableはelementを含むnextイベントを流すことができる。
 * terminateイベント(error, completed)が流れるまで続く。
 * Observableが停止するとイベントは流れなくなる。

##Subscribe
Observableを購読(≒監視)すること。
onNext,onError,onCompleted,onDisposedのタイミングで行いたい処理を登録することができる。
##Disposable
メモリリーク防止のために必要であれば購読を解除してくれるオブジェクト

```swift

  let observable = Observable.of(1, 2, 3)

  observable
    .subscribe(
        onNext: { element in
            // 値が流れてきたときの処理
        },
        onError: { error in
            // エラー時の処理
        },
        onCompleted: {
            // completeイベントが流れたときの処理
        },
        onDisposed: {
            // disposeされたときの処理
        })
   .disposed(by: disposeBag)

```
## Traits
通常のObservableより機能を限定したもの。
* コードの意図をより明確に伝えるのに役立つ。
* Single,Maybe,Completableの3つがある。
* Singleは success(value) か error しか流さないため、API通信などに便利。

```swift
class Reader {
    static func loadData(from data: String) -> Single<String> { // 略 }
}

Reader.loadData(from: "text")
    .subscribe(
        onSuccess: { text in
            print(text)
        },
        onError: { error in
            // エラー処理
    })
    .disposed(by: disposeBag)
```

## Subjects
ObserverとObservableの両方の役割を持てる。
RxSwiftにおいて4つのSubjectとそれをラップした2つのRelayがある。
* PublishSubject -> 初期値は持たない。購読者に対して新しいeventのみを流す。
* BehaviorSubject -> 初期値を持つ。最新の値を新たな購読者に対して流す。
* ReplaySubject -> バッファサイズを持って初期化される。そのサイズ分だけ値を保持して新たな購読者に流す。

```swift
let subject = ReplaySubject<String>.create(bufferSize: 2)

subject.onNext("a")
subject.onNext("b")
subject.onNext("c")

subject
    .subscribe {
        print($0)
        // bとcがプリントされる
    }
    .disposed(by: disposeBag)

```

* AsyncSubject -> completedを受け取ると、nextイベントの最後の値だけを流す。あまり使われない?
* PublishRelay / BehaviorRelay ->  それぞれPublishSubjectとBehaviorSubjectのラッパー。 
* nextイベントのみを流せる。relayに対して.completedや.errorを流すことはできない。
* subjectをクラスの外部に公開してしまうと値の書き換えが自由にできてしまう。それを防ぐためにsubjectをprivateにしてObservableを外部に公開する。

```swift
class Some {
    private let someSubject = PublishSubject<String>()  //Subjectはprivate

    var observable: Observable<String> { return someSubject } //Observableはpublic
    func sendString(str: String) {
        someSubject.onNext(str) //Subjectでイベントを流す
    }
}
```
SAMで容量の大きなlambdaのコードをCodeUriで指定する方法

SAMで容量の大きなlambdaのコードをCodeUriで指定する方法についてです。

# ローカルにzipにしておいておく。

lambdaはあまりにもでかいコードをあげる場合にはzipにするか、それ以上ではcliからs3にアップし、lambdaからはそのURIを指定する方法をとります。
実際、以前の記事（[近くで開催される勉強会を定期的にslackへ流す。](https://qiita.com/Kept1994/items/d4d9af93e160b22916df)）ではdockerコンテナをs3へ置いてlambdaから参照して実行していました。

他方で`sam-cli`は、`template.yml`の`CodeUri`に指定した相対パスのソースコードをs3へアップします。

つまり、zipにまでして相対パスを指定しておけばs3へのアップは`sam package`コマンドの中で同時に行われます。
リファレンスを見るとこの`CodeUri`にはs3のパスを指定できていることから、一度s3にあげたコードをここで参照させるのかと思っていましたが`template.yml`の中ではローカルでのパスを指定するものでした。

~~ただしここには注意が必要で、絶対パスを指定するとSuccessfullyとは出ますがうまくいきません。~~
訂正） 指定可能でした。 (2019/12/26)

```yml:template.yml
AWSTemplateFormatVersion : '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: get connpass info

Resources:
  getConnpassSchedule:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.lambda_handler
      Runtime: python3.6
# パスを指定
      CodeUri: ./deploy_package.zip
      Description: ''
      MemorySize: 512
      Timeout: 120
# lambdaのバージョンのエイリアスを指定（必須）
      AutoPublishAlias: prod
      FunctionName: 'getConnpassSchedule'
# IAMロール
      Role: 'arn:aws:iam::xxxxxxxxxxxx:role/FullAccess'
# デプロイ方式 
      DeploymentPreference:
          Type: Linear10PercentEvery1Minute
```

```shell:デプロイ
$ sam package --template-file template.yml --output-template-file package.yml --s3-bucket YOUR_BUCKET
$ sam deploy --template-file package.yml --stack-name my-date-time-app2 --capabilities CAPABILITY_IAM
```

# s3にzipにしてあげておく。

テンプレートの中の`CodeUri`に`S3://〜`で始まるURIを指定して`sam package`を実行すると生成するパッケージの`CodeUri`にそのまま反映されます。このケースではS3へのアップロードを踏みません。

ただ、`sam package`コマンドでs3へ送ってくれるのであれば良いのではないかと思うのですがそこには差があるのでしょうか？（今後使ってみて探索と言ったところですかね）
Swift：macOSでSwifterを使ってTwitterの認証をする

いそぎ要点だけまとめます．

# Swifter

[mattdonnelly/Swifter](https://github.com/mattdonnelly/Swifter)
TwitterへのOAuth認証をいい感じにやってくれるiOS/macOS向けのライブラリです．

# やり方

## 1. Swifterの導入
- まずは，GitHubレポジトリに飛んでプロジェクトをダウンロードして`SwifterMac`をビルドしてフレームワークを作る．
- 自分のプロジェクトの`TARGETS`の`Frameworks, Libraries, and Embedded Content`にドラッグ＆ドロップして追加する．
- ソースの必要なところで`import SwifterMac`．

CocoaPodsとかCarthageの方法は調べてください．

## 2. App Sandboxの設定
`Signing & Capabilities` > `App Sandbox` > `Network` > `Outgoing Connections (client)`にチェックを入れる 

## 3. Twitter Appsでアプリを登録してKeyを手に入れる

![Screen Shot 2019-12-24 at 2.01.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67153/d87f16a9-8b94-ccb6-7b75-7a4053cc9d6b.png)

## 4. Twitter AppsでCallback URLを設定する

`swifter-[生成されたConsumer API Key]://`
をCallBack URLとして追加

## 5. URL Schemesの追加

`TARGETS` > `Info` > `URL Types`にURLSchemesを追加する．
`swifter-[生成されたConsumer API Key]`
を入力する．
![Screen Shot 2019-12-24 at 2.07.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67153/71ecdf0e-382d-2e4b-783b-404e9731c6fd.png)

## 6. AppDelegate.swift実装

```swift:AppDelegate.swift
import Cocoa
import SwifterMac

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        NSAppleEventManager.shared().setEventHandler(self, andSelector: #selector(handleEvent),
                                                     forEventClass: AEEventClass(kInternetEventClass),
                                                     andEventID: AEEventID(kAEGetURL))
        LSSetDefaultHandlerForURLScheme("swifter" as CFString, Bundle.main.bundleIdentifier! as CFString)
    }
    
    func applicationWillTerminate(_ aNotification: Notification) {
        // Insert code here to tear down your application
    }
    
    func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
        return true
    }
    
    @objc func handleEvent(_ event: NSAppleEventDescriptor!, withReplyEvent: NSAppleEventDescriptor!) {
        guard let callbackUrl = URL(string: "swifter-[生成されたConsumer API Key]://") else { return }
        guard let urlString = event.paramDescriptor(forKeyword: AEKeyword(keyDirectObject))?.stringValue else { return }
        guard let url = URL(string: urlString) else { return }
        Swifter.handleOpenURL(url, callbackURL: callbackUrl)
    }

}
```

## 7. ViewControllerの実装

```swift:ViewController.swift
import Cocoa
import Accounts
import SwifterMac

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let swifter = Swifter(consumerKey: "[生成されたConsumer API Key]",
                              consumerSecret: "[生成されたConsumer Secret Key]")
        let callbackUrl = URL(string: "swifter-[生成されたConsumer API Key]://")!
        swifter.authorize(withCallback: callbackUrl, success: { (_, _) in
            Swift.print("Success Authorizing")
            // この後はもう好き放題やってケロ
        }) { (error) in
            Swift.print(error.localizedDescription)
        }
    }

}
```

たぶんこれでいけるはずです．
form_tag,form_withと空のnewアクション

#はじめに
今スクールでpictweetという、写真とツイートを一緒に投稿できる、簡単な練習用のwebサービスをつくっているのですが、ふと気になったことがあったので記事にしてみました。
#form_tag,form_withとnewアクション
MVCモデル学習し、ツイートが投稿されたときの流れも理解していたつもりでした。
投稿されたデータがデータベースに保存されるためにはnewアクションでインスタンス変数を定義しなければいけないと思っていたのですが、、、
サンプルコードを見ると

```ruby:qiita.rb
def new
end
```
え？？？newアクションの中身なにもないやん！(◎_◎;)
でもよく似たwebサービスのサンプルコードではpostsコントローラに「＠post = Post.new」としっかりインスタンス変数が定義されていました。
何が違うの？
と思って数時間かけて調べた結果,HTMLでform_tagを使用していた場合は、newアクションでインスタンス変数は定義しなくていいみたいです。
逆にform_withを使用する場合は<b>@モデル名</b>で定義しなければいけないみたいです。

もう少し調べるとform_forというのも出てきて、そもそもform_forとform_tagは投稿されたデータをデータベースに保存するかしないかで使い分けるんですね。
ただ新しく作られたform_withは両方の用途で使用し、またセキュリティ面でも有能のようです。

結果、これからは投稿フォーム系にはform_withを使うのがベストというのが今回の学びです。
スクラムマスターにオレもなる！！！

プログラミング学習の話題とはずれてしまうのですが

某S氏が取得されたスクラムマスターに関してみようと思います。


Q.スクラムマスターとは？

アジャイル開発の手法の一つであるスクラムで全体の調整やサポートを行い、メンバーが成果を上げるために支援を行うリーダー。


Q.どんな種類があるの?

資格としてはScrum AlianceのCSMとScrum.orgのLMSの2種類があるそうです。


Scrum Aliance
https://www.scrumalliance.org/

Scrum.org
https://www.scrum.org/Courses


Q.どのくらい料金かかるの?

一人当たりCSMは３日間で30万円、LMSは２日間で20万円とのことです。
いいお値段しますよね。。。


Q.どこで申し込めるの?
基本以下のサイトから申し込むみたいですCSMに関しては公式サイト?的なところから申し込むわけではないみたいです。
開催規模の差なのか上のQで調べた料金違うところが出てきたりとちょっと謎です。。。
他に研修を開催しているところがあるのかも？

CSM→
https://www.odd-e.jp/training/

https://www.attractor.co.jp/index.html

LMS→
https://scruminc.jp/


Q.更新は必要なの？
２年に１度必要です。SEUというポイントと更新料がかかるようですね。


まとまっていないので後日追記していこうと思います。
マンデルブロ集合で遊ぶ(描画編)


![mandel_loop_gray.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101445/014185b5-bcab-0ae9-3f16-9720fe263595.png)

# マンデルブロ集合の描画バリエーションを考える

前記事「[マンデルブロ集合で遊ぶ（基本編）](https://qiita.com/fujim2/items/b2a0e00d5ecaeb3d4fde)」では、マンデルブロ集合の基本的な計算方法を紹介しましたが、描画については最も単純な方法を示しただけでした。
「集合に含まれるか含まれないか」なのですから、基本的には２値で表現され、したがって２色で色分けすれば十分ではあります。

ただ、マンデルブロ集合の計算プログラムをみたとき、次のような値が描画のバリエーションとして使えないでしょうか。

- 値が発散したときの繰り返し数(for文の制御変数の値)
- 値が発散の条件を超えたときの超えた瞬間のabs(z)の値

これらの値は、主にマンデルブロ集合から外れた座標のものです。以下、イメージを付けるために、実際に得られた値を描いてみます。

![mandelbrot_base.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101445/2e6d52ae-25c1-5a65-e3c3-39b514ab1d4d.png)


# 値が発散したときの繰り返し数(for文の制御変数の値)

![mandel_loop_val.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101445/42bfb683-c78c-651a-8f74-8b286d869547.png)

マンデルブロ集合に含まれる座標の値は、収束判定のための繰り返し数の最大値(20)になっています。

# 値が発散の条件を超えたときの超えた瞬間のabs(z)の値

![mandel_divergence_val.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101445/ef9c7f9c-1f9f-1de2-5a56-425559dc25b0.png)

傾向としては、マンデルブロ集合から外側に離れるにしたがって発散判定されたabs(z)の値が、大きくなっていますね。

# コード

データを出力する c++ のコードは以下。

```c++
#include <iostream>
#include <complex>
using namespace std;
int main()
{
  for(float x = -2.0; x <= 2.0; x += 0.01 ) {
    for(float y = -2.0; y <= 2.0; y += 0.01 ) {
      complex<float> c(x, y);
      complex<float> z(0, 0);
      int i;
      float r;
      for(i = 0; i < 20; i++) {
         r = abs(z);
         if(r > 10.0) {
            break;
         }
         z = z*z + c;
      }
      cout << x << " " << y << " " << i << endl;  // 繰り返しの値をz値として出力
      //cout << x << " " << y << " " << r << endl;  // 発散判定された値をz値として出力
    }
  }
}

```

最後のcout出力の行のコメントを切り替えて出力データ(tt.dat)をgnuplotの以下のコマンドで描画します。


```sh
gnuplot> set size ratio -1
gnuplot> splot "tt.dat" w d
```

# 画像表示する

マンデルブロ集合の２次元的な描画は、「画像データ」に近い情報になります。gnuplotでは画像情報の描画機能があるので、それを使って表示してみます。まず出力プログラムを若干変更します。次の４点です。

- x軸方向のループとy軸方向のループの順を入れ替える
- yのfor文の初期値、脱出の判定条件、インクリメントを逆にする
- 出力するのは、x,y座標ではなく、x,y座標でのrの値とする
- xのループの終了時点で改行する

```c++
#include <iostream>
#include <complex>
using namespace std;
int main()
{
  for(float y = 2.0; y >= -2.0; y -= 0.01 ) {
    for(float x = -2.0; x <= 2.0; x += 0.01 ) {
      complex<float> c(x, y);
      complex<float> z(0, 0);
      int i;
      float r;
      for(i = 0; i < 20; i++) {
         r = abs(z);
         if(r > 10.0) {
            break;
         }
         z = z*z + c;
      }
      cout << r << " ";
    }
    cout << endl;
  }
}

```

上記で出力したデータ(tt.dat)を次のgnuplotのコマンドで描画します。

```sh
set size ratio -1
set palette greys
plot "tt.dat" matrix with image

```

![mandelbrot_gnuplot_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/101445/43cfd83b-73a4-c9c4-ac0a-668618216a48.png)



# まとめ

これらの値(　発散値、繰り返し数）をに対応するカラーマップや濃淡を使って多彩な色分けを行うとさらに不思議な絵を描画する事ができます。

以上です。
Djangoのサーバ起動をPycharmではなくVScode でやってみた件

# introduction
最近、自己啓発のためにDjangoを始めて3日のよわよわエンジニアのまるん(仮)です。
ハンドルネーム募集中です('ω')
[動かして学ぶ! Python Django開発入門](https://www.amazon.co.jp/%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E5%AD%A6%E3%81%B6-Python-Django%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-NEXT-ONE/dp/4798162507/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr=)を買って始めてみました。
書籍ではPycharmで開発やサーバの起動をやってて、最近VScode主体で開発をしてる自分としてはうーーーーんって感じでした。コードの修正は良いとして、サーバの起動も何とかVScodeで出来ないかなと。
そこで、12/18 に開催されたVScode Meetup で学んだデバッグ機能を使って実装してみました！
前置き長くなったので早速書いていきます。

# 環境
- Windows10
- Python：3.7
- Django：2.2.2
- VScode：1.4.1
- 作業ディレクトリは右記とする：/Users/marun/test

# 1. Interpreter の設定
venv を使用して開発を行っていたので、settings.json を新規で作成し、venvのPython.exeを以下のように再設定。

```/Users/marun/test/.vscode/settings.json
{
    "files.eol": "\n",
    "terminal.integrated.env.windows": {
        "PATH": "${workspaceFolder}/venv/Scripts;${env:PATH}"
    },
    // ${workspaceFolder}：C:Users/marun/test のことである。
    "python.pythonPath": "${workspaceFolder}/venv/Scripts/python.exe"
}

```

# 2. 環境変数の設定
WindowsでDjangoのサーバを起動するには環境変数が必要だったのですが、launch.jsonに.envファイルを読み込ませることが出来るようなのでそれを作成します。

```/Users/marun/test/.vscode/.env
PYTHONUNBUFFERED=1
DB_USER="[ユーザ名]"
DB_PASSWORD="[パスワード]"
```
これは自分の場合の設定ですので、必要に応じて中身を書き換えてください。
macの場合は環境変数いらないぽい？

# 3. launch.json の作成
最後に、デバッグ機能の構成の元になるlaunch.json を作成していきます。

```/Users/marun/test/.vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Django",
            "type": "python",
            "request": "launch",
            "program": "${workspaceFolder}/manage.py",
            "console": "integratedTerminal",
            "args": [
                "runserver",
                "--noreload"
            ],
            "envFile": "${workspaceFolder}/.vscode/.env",
            "django": true
        }
    ]
}
```

envFile を定義することにより、無事環境変数を読み込むことが出来ました。
これを作成すると、以下のようなボタンがデバッグモードのところに出てくると思います。

<img width="254" alt="VScode1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373211/30b6d5f0-fe5d-031c-3bff-85a2228df3aa.png">

# 4.実行
ボタンを押すと、ターミナルが動き出します。
エラーが出ず、以下のような出力があれば成功です！
実際に赤で囲ったURLを叩いて確認してみましょう！
<img width="360" alt="VScode2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/373211/89ab7f3e-bef3-f8e0-0bc2-27468b3b4386.png">


# 5.最後に
Qiitaで記事書くのも2回目とかですし、まだまだ勉強不足でつよつよエンジニアとはとてもじゃないが言えないですが、これからもこんなのあったらいいなと思うものを実装してアウトプットしていこうと思います。
同じような悩みを持った同胞がこの記事で救われますように。
また、もっとこうした方がいいよとか、これ間違ってるよ等のコメントお待ちしてます！
皆さんの知識を下さい！！

### 余談
[動かして学ぶ! Python Django開発入門](https://www.amazon.co.jp/%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E5%AD%A6%E3%81%B6-Python-Django%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-NEXT-ONE/dp/4798162507/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr=)ですが、フルカラーだし、Django以外にも色々技術学べるしとても良書だと思いました(異論大歓迎です)。
#### 余談の余談
ハンドルネームとかいい感じのアイコンってみんなどうやって探しているのだろう。。。
Microsoft Azure画像認識に子供の遊び心を解釈できるように学習させられるか？


##ある朝、子供が「1が出来た！」と叫んだ。
なんのことかと振り返るとチーズ食パンを食べていたら、1という数字になったそうな・・・。確かにね、言われてみればそんな感じ。と、その時思わず、この前の手書き文字を学習させたAIなら分かってくれるのかしら？と余計な考えが浮かび試すことに。
![DSC_1795.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508834/ab365609-068f-4146-ef84-270a7c576959.jpeg)

もちろん結果は✖。当たり前ですよね～。インクと食パンでは違いますもの。無茶ぶりにAIも1％の確率でアルファベットです」って全く無意味な回答をしてくれてました（笑）。そこで！この1％がどこまで改善できるか挑戦してみることにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508834/e9e9fbb9-3c54-bf1d-3f7f-66b06e2aa27b.png)

##方法
[前回の記事](https://qiita.com/marumaruchan/items/bede44ab2b6480c96a39)で作成したAIにいろんな画像を追加して精度をあげます。因みにMicrosoft AzureのCustom Vision Serviceはクラウド上にある自前のコンピューターで確立できないめちゃ賢いAIを部分的にお借りできるシステム。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508834/d5f40a49-262d-261a-5817-f27186532317.png)

##AI学習と結果
Try1.まずは色々な「１」を学習してもらうべく、[Unsplush](https://unsplash.com![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508834/55b59362-99da-8654-b0b2-4a134d724e98.png)
/)から「1」に関係する写真をいただき、ひたすら学習させる。
結果は1が3％！。まだまだ低いけど、少なくとも１が最初に出てきた（一応最初の3倍の確率に向上してる（笑）。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508834/48120779-2dee-3157-e379-ac0a887e6296.png)

Try2.ある一定を超えると情報量が増えてもあまり変わらなかったので「1」ではない数字のサンプルを学習させてみた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508834/ae60d5a2-a279-a770-12f4-92f3e99cad86.png)

微妙だけど、5.3％まで精度は向上。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508834/6ab28a2f-c140-bbf9-6ba4-321e818028b4.png)

Try3.やっぱり食パンの画像が必要かな？ということで下の写真を「1」として追加。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508834/90deef7e-63b3-324b-685d-4b4553259d40.png)


なんと一気に99％まで精度が向上。AIって1枚の画像でもこんなに変わるんですね。驚きです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508834/58113e96-4a9d-dd76-5fd4-d236cee0fde1.png)
かといって食パン＝1になるわけではないようで、1枚のパンは14％くらいしか精度はありませんでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508834/950a9ec8-c498-2c11-f79f-1f0c1bc316ba.png)


##まとめ
当たり前だし、巷でも言われていることですが、あらためて機械学習はサンプルの質が重要だということが体感できました。今回の場合は、食パンという素材の質感が非常にKEYとなりましたが、画像認識で何を識別するかによって違うと思うので引き続きいろんなもので実験してみたいと思います。
クリスマスだけに今晩はパネトーネでチャレンジしてみようかな。
HeadlessCMS(WordPress)とGatsby.jsで制作したサイトをNetlifyで公開する手順

Gatsby.jsで制作したサイトを静的Webホスティングサービスの「Netlify」で公開する手順をまとめました。

※Headless CMS(WordPress)とGatsby.jsでサイト制作するの備忘録については別の記事としてまとめています。「[Headless CMS(WordPress)とGatsby.jsでサイト制作する際の備忘録](https://qiita.com/macotok/items/501e581ff826c6755ab7)」

## localのWordPressのdataをhostingされたWordPressに移行

WordPressのプラグイン「All-in-One WP Migration」を使用

- localではexport機能でファイルをダウンロード
- hostingされたWordPressでそのファイルをimport

※ファイルサイズを拡大させるには`.httaccess`、`wp-config.php`を編集

## デプロイ準備

- netlifyのアカウント作成
- Production branchをmasterにする
- GithubのRepositoryを指定(Gitlab、Bitbucketも可)
- Build commandを設定(例: `gatsby build`)
- Publish directoryを指定(例: `public/`)

## 環境変数を設定

- netlifyの`Environment variables`でhostingされているWordPressの`API_PROTOCOL`と`API_URL`を指定
- .envファイルにlocal環境の`API_PROTOCOL`と`API_URL`を指定
- `gatsby-config.js`で下記設定

```javascript:gatsby-config.js
require("dotenv").config({
  path: '.env',
});
{
  resolve: "gatsby-source-wordpress",
  options: {
    baseUrl: process.env.API_URL,
    protocol: process.env.API_PROTOCOL,
  }
}
```

## publicのルートに_headersファイルと_redirectsファイルを自動的に生成

- npmのpackage`gatsby-plugin-netlify`をinstall
- `gatby-config.js`のpluginに追加

## WordPressの投稿保存をトリガーにbuild

``` php:functions.php
add_action('save_post', 'netlify_build');

function netlify_build() {
	wp_remote_post({netlifyのsetting/Build hooksに設定したURL});
}
```

## ハマったポイント

以上の手順でbuildするとsuccessするが、deployすると下記のエラーが起こる

```
Path: /wp-json
The server response was "403 Forbidden"
error "gatsby-source-wordpress" threw an error while running the sourceNodes lifecycle:
Cannot read property 'data' of undefined

TypeError: Cannot read property 'data' of undefined
```

これはWordPressを置いてるサーバーがレンタルサーバーの「エックスサーバー」だから起こるエラーで、海外IPからのwp-jsonへのアクセスを許可してないためでした。
そのため国外IPアドレスからのアクセスを許可する必要があります。
`サーバーについて ＞ WordPressセキュリティ設定 ＞ 国外IPアクセス制限設定`

参考サイト
[ブログをWordpressからGatsbyに移行したので、その手順とハマったポイントを解説する](https://qiita.com/akashixi/items/9653d0a6522117618e0f)
緯度経度とURLの紐付けサービスの仕様検討

# 背景

ある場所にAR/MRオブジェクトを置きたい場合、画像処理で特徴点とマッチングさせるなどの方法もあるが、シンプルに緯度経度と組み合わせることを考えます。（実装が簡単、バックエンドのデータベースも既存の考え方で作れる、データの持ち方もシンプルなど、PoCとして作成してみる場合やりやすい。）
この場合、どういうデータ構造にしておくと便利かを考えてみます。

# 階層構造と検索容易性

世界規模での展開を考えた場合、DNSの階層構造のように、地域ごとに分散できるとよいのではないかと考えると、検索のためのKeyに相当するものに階層構造を持たせたいです。また、単純な一致検索で高速・低コストに検索できるものがよいと思われます。

# 地理空間インデックスを参考にする

多次元座標の検索を高速にするデータ構造やアルゴリズムは、これまで多く考案されています。
3Dゲーム「DOOM」に使われたことで有名なBSP木や、四分木、八分木、R木などです。
ここでは、空間を均等に分けたいことや、2次元を扱えばいいことなどから、[Z階数曲線](https://ja.wikipedia.org/wiki/Z階数曲線)をベースにしたいと思います。

# Z階数曲線ベースのインデックスの作り方

まず、緯度経度の値を用意します。これは64ビット精度の浮動小数点です。
それぞれを整数にします。緯度なら、+90して180で割ったものをINTMAXにかけてやればよいです。経度は、+180して360で割ります。
この際、数値誤差が最小になるように計算の順序は気にした方がいいでしょう。また、最後の数値はキャストしてuintにします。
次に、上位ビットから順番に並べていきます。経度の最上位ビット→緯度の最上位ビット→経度の2番目のビット→緯度の2番目のビットというように、交互に順番にならべて、64ビットの整数値を作ります。これが、キーになります。
なお、どちらを先にするかをシステム全体で決めてさえいれば、緯度経度のどちらを先にしてもかまわないかと思います。
ちなみに、この交互に並べる操作ですが、ハードウェアだとロジック回路を組むまでもなく、ラッチと配線でできそうなのでいいのですが、ソフトウェアの場合は少しめんどくさくなります。
個人的に一番楽かなと思うのは、4ビットごとの変換テーブルなどを作って、ORで結合することかなと思います。
なにかいい計算方法があれば知りたいです。

# 検索クエリの設計

上で作成したインデックスと、どのレベルの検索かを表す数字8ビット(0-255で十分)を合わせて送ってやれば、地球上の2のn乗分割された矩形を指定することができます。
また、ビット列を上位から見ていくことで、空間的な階層構造をたどることになるので、例えば上位Nビットがこのビット列と一致したら、このクエリをXというサーバーに移譲する、といった処理はやりやすいと思います。

# 検索処理とデータベースの構造について

データベースをKeyの完全一致検索が可能なKVSとして考えます。
検索クエリとして投げられるのは、自分の現在位置の緯度経度を元に計算した上記のインデックス値として、それに対応するValueでその矩形範囲に登録されているURL群をBLOBなどに格納した文字列配列の形などで持っていて、応答すればよいとなります。

# まとめ

地理座標とURLを紐付けるサービスのクエリ仕様を検討してみました。大急ぎで書いたので、正確じゃないところとかあるかもしれませんので、気を付けてください。よろしくお願いします。
Amazon SES の配信結果をDynamoDBに保存するLambda関数

この記事は、[AWS LambdaとServerless #2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/lambda2) の24日目の記事です。

## Introduction

システムからのメール配信を行っている場合、BounceやComplaintとなったメールアドレスへの対処が求められます。
Amazon SES の場合、SandboxからProductionで使用する際にどのようなプロセスを実装しているかを申請する必要があります。

AWS公式では以下のような知見が紹介されています。
[How do I create an AWS Lambda function to store Amazon SNS notification contents for Amazon SES to an Amazon DynamoDB database?](https://aws.amazon.com/premiumsupport/knowledge-center/lambda-sns-ses-dynamodb/?nc1=h_ls)

概要としては、以下のような処理になります。
1. Eメールのバウンス、苦情、配信などを Amazon SNS で通知するよう設定
2. SNSでの通知を受けて、Lambdaを実行
3. Lambdaの処理でDynamoDBへ保存

今回は上の記事をSAMで実装したものを紹介します。

## レポジトリ

https://github.com/yyoshiki41/ses-notification-to-ddb

SAM でデプロイ可能なものを上のレポジトリで公開しております。
ランタイムは、Python3.7

### 設定方法

#### 1. 通知に使用するSNSを作成

```bash
$ aws sns create-topic --name ses-messages --region us-east-1
```

※このとき、Amazon SES と同じリージョン内でSNSを作成する必要があります。

#### 2. SESの設定で、バウンス、苦情、配信を通知するSNSを設定

bounces, complaints, deliveries に関する通知を受け取るためのSNSとして、
1.で作成したSNSを設定します。

<img width="1206" alt="Screen Shot 2019-12-22 at 18.17.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20609/4c9cd095-3a96-d79a-c63c-2a4de01674d1.png">

#### 3. 配信結果を保存するDynamoDBを作成

DynamoDBのスキーマを定義したjsonファイルを置いております。

```bash
$ aws dynamodb create-table --cli-input-json file://dynamodb_table.json
```

※ 上で作成したDynamoDBのテーブルは簡易的なものです。GSIの設定、READ/WRITE Capacityの設定などは運用に合わせて必要です。


#### 4. Lambda関数をデプロイ

##### 環境変数

- `S3_BUCKET`
SAMデプロイ用のS3バケット

- `TABLE_SES_NOTIFICATIONS`
3.で作成したDynamoDBのテーブル名

- `SNS_TOPIC_ARN` created sns topic
2.で作成したSNS ARN

各環境変数を設定してデプロイ完了です。

```bash
$ S3_BUCKET=sam-app-artifacts \
TABLE_SES_NOTIFICATIONS=SESNotifications \
SNS_TOPIC_ARN=arn:aws:sns:us-east-1:xxx:ses-messages \
make deploy
```

## メール配信結果

以下のように、送信毎にDynamoDBに保存されます。

<img width="1376" alt="Screen Shot 2019-12-22 at 18.34.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/20609/706498ff-e8df-c540-0759-59c3cce7037a.png">

あとはこのテーブルを使って、DynamoDB Triggersや別のバッチ処理を書いてそれぞれの処理を行います。
これで Bounce, Complaint などへの対処が可能になりました。

## Lambda関数での処理

https://github.com/yyoshiki41/ses-notification-to-ddb/blob/master/lambda_function.py

特に手の凝った処理はしていません。
よくあるSNSからの通知内容をパースして保存する形です。

Bounce, Complaint といった各シナリオでテストがしたい場合は、以下のアドレスが用意されています。
これを使えばデバッグもスムーズに行なえます。
https://docs.aws.amazon.com/ja_jp/ses/latest/DeveloperGuide/mailbox-simulator.html
NewRelicを導入して運用効率を改善させた話

# はじめに
こんにちは。[NewRelicアドベントカレンダー](https://qiita.com/advent-calendar/2019/newrelic)24日目のエントリになります。

最近会社のメインプロダクトにNewRelicを導入し、運用効率が上がり捗るようになったのでその話を書きます。
ライトな内容ですので、NewRelicの導入を検討している方、もしくは導入直後の方などを対象にしています。

# なんでNewRelicを導入したか？
長らくオンプレ環境で稼働させていたメインプロダクトを、GCPに移行させてKubernetes環境(GKE)で稼働するように構成を一新しました。
その際に、インフラ監視ツールをKubernetes環境に適したものを導入するのに合わせて、今まで導入していなかったAPMも入れることになりました。
監視ツール自体に保守工数をかけたくなかったのでSaaSサービスで探したところ、インフラ監視とAPMを兼ね備えているのはNewRelicとDatadogの2つがあり、トライアル導入をさせていただき比較検討を行いました。
<img width="767" alt="スクリーンショット 2019-12-23 21.35.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87316/09eb76d3-e912-0f95-0644-0b57c371894c.png">
結果として、DatadogのAPMは対応しているフレームワークを使用していないと見れる情報が少なく、フレームワークを使用していないメインプロダクトではあまり有用ではなかったため、NewRelicを導入することとしました。


# 導入前の運用
NewRelic導入前はzabbixでインフラ監視はしていたものの、APMのようなサービス状況を可視化するものは導入していませんでした。
そのため、インフラ監視のアラートがなった時に、サービスの状況がどうなっているかわからない状態でした。
問題が発生して調査が必要な時は、人力でアクセスログの集計やDBデータから検索など、様々なオペレーションを行い調査を行なっていました。
この運用だと、問題の原因把握にまず時間がかかってしまうことで根本対応を行うスピード感も落ちてしまい、結果としてサービス品質を落としてしまうことがあり、NewRelic導入により改善を進めました。

# 導入後の運用

## 導入直後

NewRelicを導入することで、様々な情報がデフォルトで可視化できるようになりました。
<img width="968" alt="スクリーンショット 2019-12-24 0.19.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87316/ae822203-e95f-592b-4aec-bf67d9f7c585.png">
しかし、弊社のメインプロダクトはBtoBのSaaSサービスで、導入企業により契約プランが複数あり、導入企業ごとの環境や性能上限が異なる作りをしていました。
そのため、運用においてはサービス全体での状況というよりも、導入企業ごとの状況を知りたいケースが多く、それらを見れるように設定を追加していきました。
また、NewRelicでは上記のグラフをホスト単位で切り替えて見ていくことはできるのですが、特定のホスト(Node)に依存しないKubernetes環境では有効ではなかったため、設定の追加を進めました。

## 導入企業ごとの状況の可視化

NewRelicではコードを数行書くことで任意のデータをNewRelicに連携できる仕組みがあり、
導入企業ごとのIDをカスタムパラメータとして連携することで企業ごとに可視化することにしました。
書き方はAgentの言語ごとに異なるのですが、PHPだと下記のようなサンプルになります。

```php

// NewRelicを導入していない環境でもエラーにならないように
if (extension_loaded('newrelic')) {
    // 導入企業のIDを取得する処理
    $client_code = ClientDetector::detectCodeFromDomain($_SERVER['SERVER_NAME']);
    // NewRelicへデータ(カスタムパラメータ)を連携する処理
    newrelic_add_custom_parameter('client_code', $client_code);
}
```
これをアプリケーションが実行されるごとに必ず実行される場所に書くと、全てのトランザクションデータに導入企業のIDのパラメータが追加されるようになります。
弊社のメインプロダクトではフレームワークを使用していないため自前の仕組みの中に記述しましたが、laravelだと`bootstrap/app.php`などになるかと思います。

さて、後はこのIDカットで見れるダッシュボードを作るだけです。
ダッシュボードは[NRQL](https://docs.newrelic.com/docs/query-data/nrql-new-relic-query-language/getting-started/introduction-nrql)というSQLに似たNewRelic独自の言語によって作成できます。
FACETという構文を使うことで、導入企業IDごとのアクセス数・エラー発生件数を可視化しました。

![スクリーンショット 2019-12-24 1.03.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/87316/ed772183-2807-bfaa-f740-5b799f27e677.png)

これにより、アラートがなった際にも、導入企業ごとにどのぐらいアクセスがあって、エラーが発生しているか、というサービス状況が一目でわかるようになりました。
今ではアクセスログを集計する、等のオペレーションもなくなり、快適に日々のサービス運用を行なっております。


# まとめ
NewRelicを導入したことで今まで人力で行なっていた運用作業がなくなり、日々の運用を効率化することができました。
また、NewRelicでは今回紹介したカスタムパラメータの追加のような、様々なニーズに答えてくれる機能を備えています。
個々のアプリケーションに沿った見える化を行なっていくことで運用効率を向上していけることができるかと思います。
matplotlibのfigureをエクセルにペタペタ貼る

# 背景/目的
画像ファイルがいっぱい出来るのがうざいので、１つのエクセルにペタペタ貼りたい

# 方針
figureを画像形式でファイルに保存し、xlsxwriterのinsert_imageで画像を貼る
一度書き出すのが無駄に思えるが、これが一番簡単だと思う

# サンプルコードと結果イメージ
```python
import matplotlib.pyplot as plt
import numpy as np

# サンプルfigure作成
plt.close(1)  # なくてもいいけど初期化的な気持ち
fig, ax = plt.subplots(1, 1, figsize=(6,3))
ax.barh(y=range(10),width=range(10))
plt.tight_layout()

# 画像を保存
image_file_path = 'tmp.png'
fig.savefig(image_file_path)

# 新規ワークブックを開く、シートを作る
import xlsxwriter
workbook = xlsxwriter.Workbook('tmp.xlsx')
worksheet = workbook.add_worksheet()

# セルに画像を貼る
worksheet.insert_image('A1', image_file_path)
worksheet.insert_image('K1', image_file_path, {'x_scale': 0.7, 'y_scale': 0.7})
worksheet.insert_image(19, 0, image_file_path)
worksheet.insert_image(19, 10, image_file_path)

# ズーム調整
worksheet.set_zoom(50)

# エクセルワークブックを閉じて終わり
workbook.close()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237551/34c6c82e-30db-adb5-1c1c-4e9450981dd8.png)


# 参考
xlsxwriterの公式ドキュメント。割としっかりしてる。searchが便利。
https://xlsxwriter.readthedocs.io/worksheet.html#insert_image
Rubyであたまの体操

## あたまの体操してますか？
- 以下は、 [プログラマ脳を鍛える数学パズル](https://www.amazon.co.jp/%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E8%84%B3%E3%82%92%E9%8D%9B%E3%81%88%E3%82%8B%E6%95%B0%E5%AD%A6%E3%83%91%E3%82%BA%E3%83%AB-%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%A7%E9%AB%98%E9%80%9F%E3%81%AA%E3%82%B3%E3%83%BC%E3%83%89%E3%81%8C%E6%9B%B8%E3%81%91%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%AA%E3%82%8B70%E5%95%8F-%E5%A2%97%E4%BA%95-%E6%95%8F%E5%85%8B/dp/479814245X/ref=sr_1_2?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=%E6%95%B0%E5%AD%A6%E8%84%B3%E3%82%92%E9%8D%9B%E3%81%88%E3%82%8B&qid=1577081367&sr=8-2)を写経したときのメモです。回答例はRubyで記述したものです。もっとRubyのメソッドを活用すればきれいに書けるZE☆などあったら、コメントください。

- Q1〜Q3まであります。前提条件の後に問題が表示されます。回答例を見る前にコーディングしてみると、あたまの体操になります。（たぶん）

## Q1.カードを裏返したい
**【前提】**
1〜100までの番号が書かれた100枚のカードが1から順番に並んでいます。最初、すべてのカードは裏返しの状態になっています。ある人が2番目のカードから、1枚おきにカードを裏返していきます（２,4,6...100のカードが表を向く）。次に３番めのカードから２枚お気にカードを裏返していく。（表向きのカードは裏返され、裏向きのカードは表を向く）。このようにn番目のカードからn-1枚おきにカードを裏返す操作を、どのカードの向きも変わらなくなるまで続けたとします。

**【問題】**
カードの向きが変わらなくなったとき、裏向きになっているカードの番号をすべて求めてください。

**【回答例】**

```ruby
# 100枚のカードを初期化
N = 100
cards = Array.new(N, false)

# 2〜Nまで裏返す
(2..N).each do |i|
  j= i -1
  while (j < cards.size ) do
    cards[j] = !cards[j]
    j += i
  end
end

# 最後に裏向きのカードを出力する
N.times do |i|
  puts i + 1 if !cards[i]
end
```
**【考え方】**
左から順番に処理していけばOK。
表向きを「true」, 裏向きを「false」とすることで、反転を表現できる。

## Q2.棒を切り分けたい
**【前提】**
長さn[cm]の一本の棒を、1cm単位に切り分ける。一本の棒を一度に切ることができるのは、1人だけ。例えば、切り分けられた棒が3本あるとき、同時に3人で切ることができる。
最大m人の人間がいるとき、最短で何回切り分けられるかを求めてください。

**【問題】**
n=100, m=8のときの回数を求めよ。

```ruby
  # currentは現在の棒の数
def cutbar(m, n , current)
  # 切り終えたとき
  if current >= n then
    0
  # 切る人より、現在の棒の数が少ない時、切れる
  elsif current < m then
  # 切れたら現在の2倍になる
    1 + cutbar(m, n, current * 2)
  else
  # 切る人の数だけ増加
    1 + cutbar(m, n, current + m)
  end
end

puts cutbar(8, 100, 1)
```

【考え方】
一本の棒を切れるのは1人だけ。棒を切れるときの条件を整理する。
（問題を逆に読み、1cmの棒をm人で繋いで、n[cm]の棒を作ることとも読み替えられる→別解を考えてみよう！）


## Q3.どうしても現金で払いたい
**【前提】**
バスに設置されているような、両替機を想定してください。この機械では、10円、5０円、100円、500円の組み合わせで両替することができる。両替する際に、使われない硬貨があっても構いませんが、大量の小銭は持て余すので、最大で15枚となるように両替します。例えば、「50円玉、20枚」という両替はNG。

**【問題】**
1,000円札を入れた時の、硬貨の組み合わせは何通りになるか？

- 回答例[強引ver.]

```ruby
count = 0
(0..2).each do |y500|
  (0..10).each do |y100|
    (0..15).each do |y50|
      (0..15).each do |y10|
        if y500 + y100 + y50 + y10 <= 15 then
          if y500 * 500 + y100 * 100 + y50 * 50 + y10 * 10 == 1000 then
            count +=1
          end
        end
      end
    end
  end
end
puts count
```
- 回答例[メソッド活用ver.]

```ruby
coins = [10, 50, 100, 500]
count = 0
(2..15).each do |i|
  coins.repeated_combination(i).each do |coin_set|
    count +=1 if coin_set.inject(:+) == 1000
  end
end
puts count
```

## おわりに
いかがでしたか？
今回は、ちょっとあたまの体操がしたいなという時に解いてみたいpuzzleをご紹介しました。
ぜひこの機会にあたまをやわらかくしてみてくださいね。（こちらの記事の情報は記事更新時点のものです。事前に最新のあたまの体操情報をご確認ください）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/401601/0a0d812b-197a-1d81-175d-19c9f5e5ea1d.png" width=300>
go getしてから、プログラムが実行できなくなり、解決するまでの話

#経緯
実行環境は`Mac`です。

Go環境を構築時、`$GOPATHを明示的に指定せず`構築してからプログラムを書き、実行していた。
外部パッケージを`go get`で取得してプログラム内でインポートする必要が出てきた。
`go get`自体は実行だけなら問題なく出来たものの、その後`go run foo.go`がエラーで通らなくなった。

エラー内容で案内された通りのコマンド叩いても、`反応無し`。
※`warning`については後述

```go:エラー内容
human$ go run foo.go 
warning: GOPATH set to GOROOT (/usr/local/opt/go/libexec) has no effect
go: inconsistent vendoring in /usr/local/opt/go/libexec/src:
        go.mod requires github.com/stretchr/testify v1.4.0 but vendor/modules.txt does not include it.
        run 'go mod tidy; go mod vendor' to sync
```

`$GOPATH`と`$GOROOT`を設定して、再度`go run` → 失敗
`go get`を再実行しても、`最新だよ`と怒られる。

`困った`。

#結論
`$GOPATH`と`$GOROOT`を適当に理解し、途中でイジイジしたせいでディレクトリ構造がイカれた。

修正の対応として、以下を実施
1. $GOPATHを設定&反映
2. エラー内容で案内されたコマンドを再度叩く
3. 100回目の`go run xxx.go`再実行 → `成功`

#もう少し詳しく
そもそも、筆者は`$GOPATH`と`$GOROOT`をあまり理解していなかった。
簡単な違いを以下に示します。

`$GOPATH`: 
Goのワーキングディレクトリを指す。
自分で`好きな場所を指定`して良い。
そこでGoのプログラムを書き、実行する。
`go get`や`go install`を実行した時のインストール先となる。

`$GOROOT`: 
Goのルートディレクトリを指す。
$GOROOTのPATHは`改めて設定すべきではない`。
理由は、既に然るべき場所を参照しているから。
`複数のGoバージョンを使う`のであれば、設定が必要かも。


詳細は、以下参照

- [【Go】結局、$GOPATHと$GOROOTはどこに設定すればいいの？](https://teratail.com/questions/41746)
- [あなたがGOROOTを本当に設定しなくていい理由](https://kwmt27.net/index.php/2013/06/14/you-dont-need-to-set-goroot-really/)

#実践

筆者は＄GOPATHと＄GOROOTについて`完全に理解した`ので、早速`bash_profile`を修正&反映する。

```
$ vi ~/.bash_profile

export GOPATH="$(brew --prefix golang)/libexec"
export PATH=$PATH:$HOME/go/bin
~
~
~
:wq

$ source ~/.bash_profile
```

`go get`で欲しかった外部パッケージを再度取得

```
$ go get xxxxxxx
go: downloading xxxx
go: downloading xxxx
go: extracting xxxx
...
...
```

エラーで案内されたコマンドを再度叩く
うんともすんとも言わなかったこのコマンドが、何やら喋り始めたが、無視。
※この時のログ取れてませんでした...

```
$ go mod tidy

$ go mod vendor
```

最後は、`go run foo.go`で実行確認をして、無事実行できることを確認。

`経緯`で書いていた`warning`ですが、あれは`$GOPATH`と`$GOROOT`が同じディレクトリを指している場合に表示されるようです。
そうです、筆者はまだ`完全に理解していませんでした`。

`$GOPATH`と`$GOROOT`が同じディレクトリを指しているということは、Goのルートディレクトリで作業をしていることになるので、気持ち悪いです。

なので、しっかり分けてあげたところ`warningも出なく`なりました。

#最後に
この問題で２時間くらい潰してしまったので、同じ状況に陥ってしまった方々の救いになれば...と。
これで明日からまたGoをイジイジ出来ます。

以上です。
GraphQLのリゾルバ（Resolver）とは

## リゾルバとは
[GraphQLのスキーマと型定義](https://qiita.com/NagaokaKenichi/items/d341dc092012e05d6606)でも触れたとおり、スキーマ定義にてクライアントが操作できるクエリや様々な型を定義しています。ただし、スキーマはあくまで定義のみで実際のデータ操作は行いません。実際のデータ操作を行うのがリゾルバというものになります。リゾルバの実態は特定のフィールドのデータを返す関数（メソッド）です。リゾルバはGraphQLサーバー開発者が提供する必要があります。
例えば、以下のようなスキーマ定義があった場合、type Queryにある通りID型を引数にとりBook型を返すbookByIdという名前の関数を用意する必要があります。

```schema.graphqls
type Book {
    id: ID
    name: String
    pageCount: Int
    author: Author
}

type Author {
    id: ID
    firstName: String
    lastName: String
}

type Query {
    bookById(id: ID): Book
}
```

上記例のリゾルバの実装は[「Spring BootでGraphQLを実装する」のリゾルバ（Resolver）の作成](https://qiita.com/NagaokaKenichi/items/2770b151e86fb65ea11d#%E3%83%AA%E3%82%BE%E3%83%AB%E3%83%90resolver%E3%81%AE%E4%BD%9C%E6%88%90)にある通りです。
また、全てのフィールドに対応するリゾルバが必要です。例えば、以下のようなスキーマ定義があった場合、ID型を引数にとりBook型を返すbookByIdという名前の関数と、引数なしでUser型のリストを返すusersという名前の関数が必要です。

```schema.graphqls
type Book {
    id: ID
    name: String
    pageCount: Int
    author: Author
}

type Author {
    id: ID
    firstName: String
    lastName: String
}

type User {
    id: ID
    name: String
}

type Query {
    bookById(id: ID): Book
    users: [User]
}
```

以上です。

## 参考
* https://www.amazon.co.jp/%E5%88%9D%E3%82%81%E3%81%A6%E3%81%AEGraphQL-%E2%80%95Web%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E5%AD%A6%E3%81%B6%E6%96%B0%E4%B8%96%E4%BB%A3API-Eve-Porcello/dp/487311893X
* https://www.apollographql.com/docs/graphql-tools/resolvers/
Pythonで再帰関数の繰り返し回数の上限を設定する

[AtCoder上の問題](https://atcoder.jp/contests/atc001/tasks/dfs_a)で再帰関数を実装していたらRE(RuntimeError)に。。。
原因は再帰関数の繰り返し回数の上限がデフォルトでは1000に設定されていたためでした。
※Python3.4.3

```python
[in]  import sys
[in]  sys.getrecursionlimit()
[out] 1000
```

## 解決方法
```Python
[in]  sys.setrecursionlimit(1000000) #上限を１００万回に設定
[in]  sys.getrecursionlimit()
[out] 1000000
```

Pythonで再帰関数を実装する時は気をつける必要がありますね〜。。
Rails の scope 便利手帳

Ruby on Rails で、使いやすいScopeをまとめました。
Model の設計によって差異があるものの、視点は使えるのでは？と思います。

# Scopeとは
Model に scope を記載することで、Controllerなどから、複雑なWhere条件を書かずにデータを取りだせるようになります。

```ruby:app/model/article.rb
class Article < ApplicationRecord
  scope :newer, ->{order("updated_at DESC") }
end
```

これによって、Controllerなどで

```ruby:app/controllers/articles_controller.rb
@articles = Article.newer
```
で、更新日が新しい順に取得することができます。

```ruby:app/controllers/article.rb
@articles = Article.newer.limit(5)
```
とすると、更新日が新しい順に５件を取得できます。


# 公開されているか
```ruby
  scope :published, -> { where(published: true) }
```

# ある時間より前か
```ruby
  scope :created_before, ->(dt) { where("created_at < ?", dt) }
```

呼び出し元では、引数に時間をいれる

```ruby
@articles = Article.created_before(Time.now)
```

## 予約時間が過ぎたものであるか
```ruby
  scope :reserved, ->{where("reserve_at < '#{Time.zone.now}'") }
```
現在時刻以下


# 終了状態であるか
終了日の有無で判定する場合

```ruby
  scope :finished, ->{where("finished_at IS NOT NULL") }
```

その他追記予定