OmniAICreator/Qiita-1.07M huggingface dataset:
ActiveRecordがgroupごとに orderしてくれなくて困った。

## 結論
MySQLの仕様上、処理順はgroup by→order byなので、
`Somemodel.order('some desc').group(:some_column)` のようにしても
グループごとにソートされた値は取ってこれなかった。

MySQLでGROUP BYとORDER BYを同時に使用する場合に気をつけたいこと
https://norm-nois.com/blog/archives/1293

pluckで配列として取ってきたものを、頑張って処理すればほしい値が取れる。
頑張って配列を処理する方法がわからない方は、
最後まで見ても良いかもしれません。


## どんなことがしたかったか

Blogというモデルのblogsテーブルには
`id, category, user_id, text_length`　というカラムを持っていたとして、
user_idのcategoryごとに、text_lengthが最大のidを取得したかった。
(UserとBlogが1対多のイメージです)

最初やろうとしたこと

```ruby
Blog.order('text_length desc').group(:category,:user_id).pluck(:user_id,:category)
```

```SQL
# 出てくると思ってたクエリ
SELECT user_id, category FROM  blogs
ORDER BY text_length desc
GROUP BY category, user_id 
```

ところがどっこい、出てくるクエリはORDER BY　とGROUP BYが逆転してて、
出てくる値も当然期待通りのものではありませんでした。
(生クエリ書いて、逆転させても、エラーが出ます)

GROUP BY　が先に実行されるので、
category,user_idごとのgroup(idが一番若いものが代表になる)になってから、
それをtext_lengthでorderするので、全くuserごとのtext_lengthとは関係ないものが出てきてしまいました。

そしてこれができないのはMySQL側の問題のよう(結論の記事をご確認ください)なので、
仕方なくpluckしてRails側でgroup_byしてからorderすることにしました。

## 以下はただの配列操作したという話
```ruby
# 本当は全カラム取るならpluckだけでいいですが、
# 順番を明示するためにカラムを明示してpluckしてます
blogs = Blog.pluck(:user_id,:category,:id,:text_length)
#=> blogs = [
# [1, 2, 1, 4], [1, 2, 2, 5], [1, 1, 3, 6], [1, 1, 4, 7], 
# [2, 2, 5, 6], [2, 2, 6, 4], [2, 1, 7, 8], [2, 1, 8, 7]]
grouped_blogs = blogs.group_by {|b| [b.first, b.second]} 
grouped_blogs.each do |_key,value|
  max_ary = value.max_by(&:fourth) # ４つ目がtext_lengthなので. max_byは便利
  p "user_id:#{max_ary[0]} category:#{max_ary[1]} id:#{max_ary[2]} text_length:#{}{max_ary[3]}"
end
#=> # 
#   "user_id:1 category:2 id:2 text_length:5"
#   "user_id:1 category:1 id:4 text_length:7"
#   "user_id:2 category:2 id:5 text_length:6"
#   "user_id:2 category:1 id:7 text_length:8"
```
こうして欲しい情報を得られました。
pluckした後にカラム名でいい感じにhashにしたいですが、
簡単な方法は調べても出てこなかったので諦めてます。
翻訳：event-stream事件の詳細

npm blogの翻訳です。英語わかる方は元記事を読むことをおすすめします。
翻訳ミスや誤字脱字等あれば指摘してもらえるとありがたいです。
[The npm Blog — Details about the event-stream incident](https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident)

# event-stream事件の詳細

これは多くの人たちが今週の初めに気づいた[event-stream事件](https://www.theregister.co.uk/2018/11/26/npm_repo_bitcoin_stealer/)の分析です。npmは、コミュニティの安全に影響を及ぼす運用上の懸念事項や問題に即座に対応しますが、インシデントについて議論する前に、より詳細な分析を行うのが一般的です。（私たちはあなたが待っていたことを知っています。）

11月26日の朝、npmのセキュリティチームは、人気のnpmパッケージである`event-stream`に入った悪質なパッケージについて知らされました。マルウェアを調査した後、npmセキュリティは、レジストリから`flatmap-stream`と`event-stream@3.3.6`を削除し、さらなる悪用を防止するために`event-stream`パッケージの所有権を取得することで対応しました。

悪質なパッケージはバージョン0.1.1の`flatmap-stream`でした。このパッケージは、新しい管理者により`event-stream`パッケージの直接の依存関係として、バージョン3.3.6に2018年9月9日追加されました。`event-stream`パッケージは広く使用されていますが、悪意あるコードは非常に固有の開発環境を設定した企業の開発者をターゲットにしていました。つまり他の環境でペイロードを実行しても効果はありません。この特定のターゲット設定は、悪意のあるモジュールを誤ってインストールしても、最終的にはほとんどの開発者が影響を受けないことを意味します。

注入されたコードはCopayアプリを対象としています。Copayの開発者がリリースビルドスクリプトの1つを実行すると、結果のコードはアプリケーションにバンドルされる前に変更されます。このコードは、100 Bitcoinまたは1000 Bitcoin Cashの残高を持つアカウントからアカウントの詳細と秘密鍵を収集するように設計されています。

Copayの最初の対応は、この悪意のあるコードを含むビルドは公開されていないということでしたが、現在、Copayから「悪質なコードはバージョン5.0.2から5.1.0にデプロイされました。」という確認があります。

## 攻撃

この攻撃はソーシャルエンジニアリング攻撃から始まりました。攻撃者はメンテナーと見せかけ`event-stream`モジュールのメンテナンスを引き継ぎました。

## 技術的な詳細

興味のある人のために私たちが知っている技術的な詳細です。

注入されたコード：

- テスト用備品に偽装されたファイルからAES暗号化データを読み込む
- 1番目の解読されたチャンクから2番目の解読されたチャンクの終わりまでの小さなコメント付きプレフィックスを連結
- 連結されたコードブロックを無効なJSから有効なJSに変換する小さなデコードタスクを実行（これは動的解析ツールによる検出を回避するために行われたと考えられます）
- JSのこの処理済みブロックを、ビルドスクリプトによってパッケージ化される依存関係に格納されたファイルに書き出す

書き出されたコードは、実際の悪意のあるコードで、Copayのエンドユーザーが所有するデバイス上で実行されることを意図していました。

このコードは次のことを行います

- 現在の環境を検出する（モバイル/Cordova/Electron）
- 被害者の保険金口座のBitcoinとBitcoinの現金残高を確認する
- 現在の残高が100 Bitcoinまたは1000 Bitcoin Cashより多い場合
  - 被害者のアカウントデータを完全に収集する
  - 被害者のcopay秘密鍵を収集する
  - 被害者のアカウントデータ/秘密鍵を111.90.151.134で動作するコレクションサービスに送る

Copayアプリのユーザーは、「5.0.2から5.1.0のいずれかのバージョンを使用している場合は、Copayアプリを実行または開くべきではありません」と[bitpayが推奨](https://github.com/bitpay/copay/issues/9346#issuecomment-441827353)しています。

npmユーザーは、`npm audit`を実行することで、プロジェクトに脆弱な依存関係が含まれていないかどうかを確認できます。この`event-stream`の影響を受けるバージョンをインストールした場合は、できるだけ早く新しいバージョンにアップデートすることをお勧めします。
大規模プロジェクトの管理画面を育てるために TypeScript + React を選んだ理由

こんにちは！
LOB アドベントカレンダー一発目はもじばけおからお送りさせていただきます。すきな言語はハイパーテキストプリプロセッサーです！

今回は TypeScript + React というフロントエンドの技術選定について、どういう経緯やロジックで決定したものなのかをお送りします。
これから新規プロジェクトを開始される方の参考になればさいわいでございます。

# 前提
## LOB がやっていること
一言で言うと、広告プラットフォームを開発しています。

事業内容等々は[コーポレートサイト](https://lob-inc.com)に書いてあるとおりですが、要するに楽天経済圏のアセットを活かしたプロダクトを作っています。

# TypeScript + React にたどり着くまで

細かい説明は端折らせていただきますが、事業上最初に作らないといけないものは以下の 3 つです。

- 秒間数十万リクエストを処理できる広告配信サーバー
- リリース時からグローバル展開できる(つまり i18n 対応された)管理画面
- 配信用 SDK (iOS, Android, JavaScript)

ここからブレークダウンしてサーバーサイド → フロントエンドの順で技術選定を行いました。

広告配信サーバーについては、わたしの好きなハイパーテキストプリプロセッサーや Python 、 Rust 、 Java や C# など、プログラミング言語はたくさんありますが、以下の条件で絞り込み、最終的に Go に着地しました。

- 高速に動作する
- 可読性が高い
- 書きやすい
- 並列処理も書きやすい


## フロントエンドの技術選定開始
先に確定した広告配信サーバーに続いて管理画面をどうするか検討したのですが、どう控えめにとっても Go のテンプレートエンジンは使いやすいとは言い難いものに見え、むしろそこで使うものではないなという感触です。

とはいえ、広告配信サーバーと管理画面向けのバックエンドを同一言語にしておきたいという思いから、 Go からはブレず、 Go には API サーバーに徹してもらうことにし、いわゆる SPA として稼働するアプリケーションを作成することにしました。

そこで候補に上がったのは以下の 3 つでした。

- React
- Vue
- Angular

SPA を作る、だけであればもっと選択肢がありますが、長期間死んでしまいにくい/長期サポートされる期待が持てるものだけを固く選んでいきたいためこの3つです。


平行して検討したことは、型システムの導入です。

事業特性上、大きなエンジニア組織になっていくことが不可避であることが最初からわかっていたため、いろんなフロントエンジニアが同時に同じプロダクトを安心して触れるようにするための対策をいれておく必要を感じたためです。

型システムは Flow か TypeScript の ２ つが候補に上がりましたが、ここは簡単に TypeScript で決定しました。

理由は世に公開されているライブラリの型定義ファイルの数の多さと、 TypeScript 自体の開発の活発さです。(最近のマイクロソフトまじでがんばってますね！)

　

そして TypeScript を使うことが決まった段階で Vue そして Angular が候補から外れてしまいました。理由はシンプルに 1 つです。

## いつまで立っても結論がでない EcmaScript 側の Decorator 問題

Vue や　Angular は `@Component` や `@NgModule` などの TypeScript の Decorator に依存する形になっていくのですが、この実装に不安を覚えたためです。

憶測でしかありませんが、 TypeScript ⇔ EcmaScript 間の Decorator 周りに関しての絶望的な差分が万一でてしまったことで起きるリスクを飲み込めませんでした。
具体的には、

1. TypeScript が EcmaScript に寄せた実装に変更になる
2. Vue/Angular も追随
3. 何らかの Breaking Changes が発生する
4. アプリケーション側がフレームワーク/ライブラリのアップデートを行いにくなる

リスクを飲み込めなかった理由は、万が一が発生してしまった場合に、「アップデートにコストを取られて事業的にやりたいことが後回しになる」 or 「事業的にやりたいことを優先してアップデードするコストを取れない」 という２択問題自体への直面を回避したいからです。


# さいごに
さっさと Decorator 決着してくれさえすれば、 Vue や Angular も候補にあがったのに…というお気持ちが強く、とてもモヤモヤするところではありますし、我々の React というチョイスが数年後にゼロベースで考えたときにも大正解なものである、ということではないと思っています。
プロジェクトスタート時、事業上の未来からブレークダウンし、既存のエンジニアメンバーが「その瞬間に」できる/できないを無視して技術選定した一例としてお伝えしてみました。 (ちなみに Go もプロダクション向けにゴリゴリ書いてたひと誰もいませんでした)


直前まで i18n 対応についてのテーマを書こうとしていましたが、「なぜ TypeScript + React でやっているのか」を書き始めた結果、これだけでかなりボリューミーになってしまったので i18n 周りについては別記事でご紹介させていただくことにさせていただきました。
近日中に i18n 記事を別で投稿したいとおもいます。

ではでは。
インライン要素

#インライン要素

・ブロックレベル要素の中にいる子達
・内容
・文章の一部
・改行されないでそのまま表示される

|コード|内容|
|:--|:--|
|strong　　　|文字の強調|
|span　　|divのインライン要素ver。この子自体に意味はない。CSSを特定の文章に効かせたい時に使用|
|a|リンク|
|br|改行|
|img|画像|
|textalea  |よくある感想を書く白枠|

上記以外にも沢山あるが今（12/1）はまだこれでいい
後々覚える時に追加していく
Private VPCにECSクラスターを構築する

# Private VPCにECSクラスターを構築する

猫も杓子もコンテナである。
半年前までDockerコマンドを打ったことさえなかった５０手前のおじさんが、Private　VPC内にECSクラスターを構築するはめになった。困難の連続だったので、ハマりどころなどを共有したい。

## インターネットゲートウェイを持たないVPC

ここで言うPrivate VPCとは、インターネットゲートウェイ（NAT含む）を持たない、完全にPrivate IPのみで構成されるAWSのVPCを指す。
インターネットに全く出れないのかというとそんなことはなく、DirectConnect経由で（オンプレの）HTTP Proxyを経由して出ることが可能である。

## Docker HUB/ECRにどうやってアクセスするか

ここでまず問題となるのは、DockerのリポジトリがあるDocker HUBやECRにどうやってアクセスするか、ということである。まず抑えておかないといけないのは、**ECS始め、EKS、AKS、GKEなどのコンテナオーケストレーションサービスは、はインターネットゲートウェイが存在することを前提としている**ということである。インターネット上に存在するコンテナリポジトリからイメージをpullしてくるのだから当然といえば当然であり、何年か前の文献にはインターネットゲートウェイを持たないVPCにはECSのクラスターを置くことはできないとすら書かれている。

### ECS（EC2起動タイプ)はHTTP Proxy経由で利用可能

ECS(Fargate)を調べていたところ、早々にインターネットゲートウェイがないと使用できないことがわかって胸をなでおろした。dockerに何故かこだわっているプロジェクトマネージャーが、心を入れ替えてproxyを諦めてくれると思ったのだ。しかし彼は頑固だった。インターネットゲートウェイ(NAT含む)などけしからん！ということになり、なんとかPrivate VPC内でコンテナオーケストレーションをする方法を模索した。
そして、幸か不幸か以下の記事が見つかった。

[HTTPプロキシ設定](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/http_proxy_config.html)

結論を言えば、ECS(EC2起動タイプ)ではHTTP Proxy経由での利用が可能だった。

## 手順

### 空のクラスター(EC2起動タイプ)を作る

通常EC2起動タイプを選択するとEC2インスタンス（オートスケーリンググループ）も同時に作成されてしまうが、クラスター名入力の下にある「空のクラスターを作成」というチェックボックスをオンにすることにより、EC2インスタンスを作成せずにクラスターだけを作成できる。

### 起動設定を作成する

EC2の画面から「起動設定」を作成する。

* ベースとなるイメージは[ECS最適化インスタンス](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/ecs-optimized_AMI.html)を参考として選択する。東京リージョン(`ap-northeast-1`)の場合`ami-05b296a384694dfa4`を使用する。

* インスタンスタイプは何でもいいようだが、私はm4.largeを主に使用している。起動設定を作成したあとでインスタンスタイプを変更するのは面倒なので、あまり変更しないほうが良い(リソースの増加にはインスタンス数の増加＝スケールアウトで対応する）

* 起動設定の作成時に、「高度な設定」より[上述のリンク](https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/http_proxy_config.html)に記載されているユーザーデータをコピー＆ペーストする(proxyの設定及びクラスター名は自分の環境に合わせて修正する)

### オートスケーリンググループを作成する

上記起動設定からオートスケーリンググループを作成し、「インスタンスの必要数」を１以上に設定する。しばらくするとEC2インスタンスが起動してくる。この際のインスタンス名などは何でもよく、上述の起動設定でユーザーデータに記述されているクラスター名が全てである。

### クラスターへのインスタンス追加の確認

ECSのコンソールから、クラスターの「登録済みインスタンス」に０より大きい数字が表示されたら登録は成功している。いつまでも出てこない場合、クラスター名などのユーザーデータや、セキュリティグループの設定がおかしい可能性が高い。

### サービスの起動

私はecs-cli compose service upを利用して上記クラスターにサービスを登録・起動しているが、なんとか使用できている。

## 課題

### Cloudwatch Logsでログが取れない

ECSの説明を読む限り、logging driverにawslogsを指定するとCloudwatch Logsへのログ出力が可能なはずなのに、何度やってもコンテナのロードに失敗する。エラーメッセージもおかしいので、ECS自体のバグ(ないしproxy環境の制限)と考えている。
現在はsyslogドライバーを使用し、それをECSインスタンス上にインストールしたawslogsでCloudwatch Logsに飛ばすということをしているが、syslogが混ざってしまうのでとても見づらい。

### 時たまクラスターが死ぬ

デプロイを短時間に何度も繰り返したりした際に、クラスターが死ぬ（新しいコンテナが一切ロードされなくなる）という状態を何度か経験している。ECSインスタンスを作成しなおせば治るのだが、クラスターが死んでいると言ってもECSインスタンスそのものは生きているので、オートスケーリングが効かず手動でしか回復できない。幸い、クラスターを何本かに分けたことでそれ以降再現していない。

### スケールイン時に生きているコンテナが載ったECSインスタンスが殺されることがある

オートスケーリンググループの「インスタンスの必要数」をへらすとECSインスタンスのうち一部が勝手に終了（Terminate）されるが、殺される順序はスケールインのポリシーで制御できる。前述のインスタンスの入れ替えなどではOldest(古いものから殺す）というのが有効だった。が、「稼働コンテナ数が０のもの」といった細かい制御は（標準では）できないので、一工夫必要になる。
現在時点ではそこまでスケールアウト・スケールインの必要はないのであまり研究していない。

##　最後に

あまり技術的に深掘りした記事でなく恐縮だが、この手の情報があまりないので書いてみた。同じような境遇の方がおられたら、参考になれば幸いである。もちろん、もっとこうしたほうがいいよ、という情報も大歓迎である。
スタートアップエンジニア視点で見るサイゼリヤ


こんにちは。　`#teamサイゼリヤ` アドベントカレンダー 1日目マスブチです。
一応、チームサイゼリヤハッシュタグ作りました。  `#teamサイゼリヤ` で行きます。
これがワークするかは分かりませんが、とりあえずハッシュタグつけたくなったら、つけてみてください。

この謎のチームがなぜサイゼリヤと命名されたのかを解き明かすために、初日のこの記事ではこんな記事をQiitaに投稿して良いの？感ありますが、まがいなりにもスタートアップに1年以上関わってきた22才エンジニアの視点からみるサイゼリヤの魅力について書いていこうと思います。


## 安定のUXを体験できる

安定の〇〇。そう、安定の〜という言葉をよく聞きますが、サイゼリヤでいうと安定のミラノ風ドリア、弊社で流行っているのは安定のエスカルゴのオーブン焼き。

0→1を作る上で、様々な事象を抽象化してヒントになる要素を見つけ当てる手法として、"安定の〜"に着目することは何かしらのヒントになるのではないかと最近思います。

プロダクトを作る上で、根幹となるUXをなすMVP(Minimum Viable Product)をどう形成するかが重要になるというのは、有名な話ですが、これがサイゼリヤにおいても言えると思います。

僕たちがサイゼリヤに毎週のように向かう理由は、毎週のように終業後サクッと安く飲みたいとい僕たちが欲しい体験を満たしてくれる "安定" の美味しさ、価格、雰囲気などの要素があるからです。
この安定のUXを満たすという意味で、有名なエピソードとして、サイゼリヤはミラノ風ドリアの改良を1000回以上重ねたという話があります。

このことはプロダクト開発にも言えて、ユーザーがそのプロダクトに期待していること、要素を洗い出し、それを磨き上げ、要素の一つ一つを満たして行くことがリテンションを高める上で最も重要なことだなとサイゼリヤに行くたびに思います。

## 客層が幅広い

高校生が多いイメージがあるサイゼリヤですが、噂によるとサイゼリヤでは合コンも開かれることもあるそうです。

熟年のカップルがかエスカルゴつまみにワインをチンしてればオサレな店に見え、高校生が騒いでればファーストフード店に見える幅広い一面を見せるサイゼリヤ。

プロダクトに例えると、安定のMVPが作ったUXの先にあるスケール戦略がうまくワークした状態です。
ここにもヒントがある気がしてやみません。


## CPTB(CostPerTeamBuilding) が低い

ちょっと飲み行こうぜ！

終業後よくある会話ですね。うちの会社は週5ペースくらいであります。(？)


この1年ほどまがいなりにもスタートアップに関わってきて、創業期はチームビルディングが重要だということを認識しました。
特にtoCプロダクトだと改善サイクルを回す回数が勝負になるため、業務を外注するのにはなかなかきついものがあります。

そこで、お互いの価値観を確認しよりスムーズに仕事を行うために、チームビルディングとして飲みに行く機会が重要になってきますが、この東京という街は一回飲みに行くだけで、最低3000円も使ってしまう大学生もびっくり、金なしスタートアップには厳しい洗礼があります。

そんな時に活躍してくれるのが、飲み屋としてのサイゼリヤ。そうサイゼリヤにはワインというアルコールを入れるのに最高のコスパをなす商品がございます。
弊社の毎度の会計レシートを見ると、みんな酔っ払い、かつお腹を満たしているのにも関わらず、大学生もびっくり仰天価格、一人辺り1000円ほどに収まってしまいます。プラス、これは場所を選ぶかもしれませんが、ファミレスという特性上、閉店時間も居酒屋よりも早めに設定してあります。熱く語っていたら、うっかり終電を逃し、次の日の仕事に支障が。。。みたいなことも起こりにくくなります。

金銭的にも時間的にも最高のコスパです。


## 終わりに

最初にしてサイゼリヤを意識しすぎてしまい、こじつけみたいな記事感を書いてしまった感がありますが、今後の書く人のハードルが下がったということで良しとしてください！w
このアドベントカレンダーでは僕の枠はまだあるので、次回はエンジニアらしく技術的な記事を書こうと思います！それでは！

最後に余談ですが、お店の名前は「サイゼリヤ」だそうです。最後は「ヤ」です。「サイゼリア」ではありません。イタリア語で「クチナシの花」という意味らしいです。
茨城問題と似ていますね。
機械学習論文読みメモ_143

[Spectral Normalization for Generative Adversarial Networks](https://arxiv.org/abs/1802.05957)
GANのdiscriminatorの学習をstabilizeするための正則化手法としてspectral normalizationを提案する。
提案手法は計算コストが小さく、既存の実装に簡単に入れ込む事が可能である。
従来手法では、discriminatorの密度比推定の学習能力が低いため、翻ってgeneratorは
Multi-modalな生成分布を学習する事が困難となる。
またモデル分布とtarget分布が互いに素である場合に、discriminatorはそれらを完全に
分離するパラメータを学習可能で、そうなると勾配がゼロになってしまうことにより
翻ってgeneratorが一切学習されなくなってしまう問題もある。
提案するspectral normalizationでは、discriminatorネットワークの各レイヤの重みに関して
そのspectral normを用いて正則化を行う事により、discriminatorのLipschitz constantを
制御する。
この手法は、従来のL2正則化などとは違い、data dependentな正則化となっている。

[PointFusion: Deep Sensor Fusion for 3D Bounding Box Estimation](https://arxiv.org/abs/1711.10871)
本論ではPointFusionと呼ばれる、画像と3d point cloudの双方を活用した3dの一般物体認識手法
を提案する。
従来のmulti-step pipelineやデータにドメイン知識による仮定を置く手法と違って、
本手法は簡単でアプリケーションに依存しない。
画像とpoint cloudはそれぞれCNNとPointNetを用いて別々に処理される。
双方から得られた特徴量は新たに提案するfusion networkにより統合される。
このfusion networkは3D bounding box及びそのconfidenceを、
3d pointsをspatial anchorとして用いる事で予測する。

[One-Sided Unsupervised Domain Mapping](https://arxiv.org/abs/1706.00826)
最近のunsupervised domain mappingの研究では、
２つのドメインにおける双方向のマッピングを同時に学習する事で
高性能なマッピングを得られる事が出来た。
本論では、片方向の学習のみを行う場合を考える。
これは、２つのサンプル間における距離を保つ事で実現する。
さらには、サンプルの異なった部分間の距離を保つ事でも実現できる
事を示した。
MySQL　GRANT構文について

##GRANT構文とは

>GRANT ステートメントは、MySQL ユーザーアカウントに権限を付与します。GRANT にはまた、セキュア接続の使用やサーバーリソースへのアクセスに関する制限などの、その他のアカウント特性を指定する機能もあります。GRANT を使用するには、GRANT OPTION 権限が必要であり、かつ付与しようとしている権限を持っている必要があります。

引用：https://dev.mysql.com/doc/refman/5.6/ja/grant.html

GRANT構文は権限を付与したり、制限の操作が行える構文です。
セキュア接続というのは、HTTPS接続の事です。
HTTPSが分からない方は[こちら](https://cspssl.jp/guide/https.php)を参照してください。


##例

```
GRANT ALL ON *.* TO 'test'@'localhost';
```

この構文を例に解説します。

###GRANT
これはGRANT構文を使用する、という宣言です。

###ALL
今回の例からいうとグローバル権限を対象とする全ての権限を指します。
テーブルレベル、データベースレベルでALLを指定する事も可能です。
テーブルレベルでのALLの場合構文は下記のようになります。

```
GRANT ALL ON mydb.mytbl TO 'test'@'localhost';
```

例に挙げた構文との違いは、`ON`の後です。
`DB_name.Table_name`の形になります。

データベースレベルでのALLの場合、構文は下記のようになります。

```
GRANT ALL ON mydb.* TO 'test'@'localhost';
```

こちらも同様に例に挙げた構文との違いは、`ON`の後です。
`DB_name.*`の形になります。

###ON *.*
グローバル権限を割り当てる時に使用します。
特定のサーバー上のすべてのデータベースが対象となります。

###TO 'test'@'localhost'
どのユーザーで、どのホストなのか、を指定します。
例文では、`test`ユーザーで、`localhost`を指定しています。

ちなみに、`''`シングルクォーテーションで囲む必要は基本的にありません。
シングルクォーテーションで囲む必要があるのは、例えば特殊文字のハイフン`-`が入っている場合です。

##SHOW GRANTS構文

どのような権限が付与されているか、を確認する事が出来る構文です。
現在の状態や、権限付与後に確認する時は、この構文を使います。

```mysql:基礎構文
SHOW GRANTS [FOR user];
// [FOR user] → 'test'@'localhost'など
```

- SHOW GRANTS;
- SHOW GRANTS FOR CURRENT_USER;
- SHOW GRANTS FOR CURRENT_USER();

上記3つのいずれかを使用する事で、現在接続されているアカウントの権限を確認することが出来ます。


##参考

https://dev.mysql.com/doc/refman/5.6/ja/grant.html#grant-other-characteristics
https://dev.mysql.com/doc/refman/5.6/ja/show-grants.html
https://dev.mysql.com/doc/refman/5.6/ja/privileges-provided.html
Qiitaのトレンド情報を通知してくれるGASを書いた

Webエンジニアを初めて1年弱、ちょっと勉強のギアを上げたいなと色々調べ物をしていたところ「まずは日々の情報収集から始めねば」というところに行き当たりました。
「やっぱりちゃんとQiita見に行かないとだよなあ」と思ったものの、毎日見に行くのめんどくさいなと思ったので(だから今こまめに見に行ってない)、自動で通知してくれるGASのプログラムを書きました。
毎朝チャットに届くので仕事始めのタイミングでさらっと目を通すだけで簡単にトレンドを追えるようになりました。

## コード
``` qiita_trend.js
// 本当は拡張子はgsだけども見やすさのためにjsに変えてます。
function myFunction() {
  // HTMLのデータを取得する
  var url = 'https://qiita.com';
  var response = UrlFetchApp.fetch(url);
  var html = response.getContentText('UTF-8');

  // 取得したHTMLを通知用のテキストに加工する
  var lines = html.split('>');    // １行のテキストとして返ってくるので閉じタグで分割する
  var rank_line = lines.filter(isRankLine);    // ランキングデータが入ってる行のみを返す
  var rank_json = getRankJson(rank_line[0]);    // ランキングデータの行をjson形式に変換する
  var message = createMessage(rank_json['trend']['edges']);    // json形式のデータを通知用のテキストに変換する

  // jsonを加工してチャットワークに通知する
  notifyChatwork(message);
}

function isRankLine(line) {
  return line.match(/data\-hyperapp\-app\=\"Trend\"/);
}

function getRankJson(line) {
  line = line.replace(/&quot;/g, '"');
  var start = line.indexOf('{"trend"');
  var end  = line.indexOf('}}}]}}') + '}}}]}}'.length;
  var data = line.substring(start, end);
  return JSON.parse(data);
}

function createMessage(json){
  message = '';

  json.forEach(function (data) {
    var author = data['node']['author']['urlName'];
    var uuid = data['node']['uuid'];
    var title = data['node']['title'];
    var url = 'https://qiita.com/' + author + '/items/' + uuid;
    var likes = data['node']['likesCount'];

    message += title + '\n';
    message += url + '\n';
    message += likes + 'いいね\n\n';
  });
  return message;
}

function notifyChatwork(message) {
  var client = ChatWorkClient.factory({token: 'your api token'});
  client.sendMessage({room_id: 'your room id', body: message});
}
```

## 設定の仕方
1. Google Driveで新規のGASプロジェクトを作る
2. [Google Apps ScriptからChatWork APIを使って投稿する](https://qiita.com/y_minowa/items/39db8ca5bffc9c440caf) を参考にチャットワークのライブラリを追加する
3. APIトークンとルームIDを自分のものに書き換える
4. `編集>現在のプロジェクトのトリガー`から定期実行の設定を入れる。関数は`myFunction`を選択。
5. あとは定期実行を待つのみ

## 終わりに
HTMLをmessageに変換するまでのところはもっと綺麗に書ける気はしますね。
Pythonでスクレイピングをしたことはあったんですが、GASスクレイピングの仕様やhyperapp周りの対応に手間取りました。
とは言っても、環境構築不要のGASで便利なスクリプトを書けたのは良かったかなと思います。

もし良ければご利用ください。


## 関連リンク
[僕のレポジトリ](https://github.com/ymk1t/qiita_trand)
[Google Apps ScriptからChatWork APIを使って投稿する](https://qiita.com/y_minowa/items/39db8ca5bffc9c440caf)
[WIP] 次にiOSアプリ作るときに採用したい構成

# はじめに

アプリの前提として

- SNS的なアプリで比較的インタラクションやサーバ側の更新が多い
- 仕様の追加や変更が多い

# やりたい事

- ゴリゴリ機能を追加したい
- 読みにくい・学習コストの高いものは避けたい
  - 必要に応じて人数を増やせるように
- テストしにくいView/ViewControllerのコード量を減らしたい
- 仕様追加や変更の多いViewModelのコード量も減らしたい
- 古い表示が残る系のバグを根絶したい
- オフラインでもディスクキャッシュされてる情報は表示したい
- DBは使いたいが読み書きのオーバーヘッドは避けたい
- 冗長なAPI通信を無くしたい
- (できれば)重要な情報から早く取得して表示したい
- (できれば)先読みしたい

# パッケージ設計案

複雑でバグになりがちなところはなるべく汎化してまとめて、
それ以外の画面や機能をサクサク作れるようにする。

- ViewModels - ある画面の内容と状態とロジック
  - 自分を表示できるViewの知識 (protocolで決まる)
  - 画面の内容に必要なModel/Resourceの知識
  - Viewとの入出力に使うプロパティの実装 (tap, text, hidden, enable, offsetなど)
  - UIからの入力に対応するバリデーションやActionの知識
  - URIスキーム(ディープリンク)の、画面の内容を表す部分の知識？
  - 自身の状態のシリアライズ・デシリアライズ (ログやデバッグ用)
- Views - 特定のViewModel(群)を可視化・操作するUI
  - 画面に閉じたUIKit系の実装 (ViewController/View/Cell)
  - 注入されたViewModelと双方向バインディング (候補はSwiftBond)
  - そのViewとの入出力に使うプロパティのprotocol定義
  - 自身では基本的には状態を持たない (計算キャッシュくらい)
  - URIスキーム(ディープリンク)の、画面を表す部分の知識？
  - 画面遷移やダイアログは基本的には無し (ViewModelがActionを実行した時の副作用)
- Models - GETやPOSTの内容。Resourceの構成要素にも。
  - ほぼGETやPOSTで使うJSONのマッピング (ObjectMapper)
  - フルの情報を取れるAPIのpathの知識
- Resources - コレクション系のGETやPOSTの内容。
  - ResponseとModelとの対応の知識
  - APIのpathの知識
  - filter、無限スクロール、pagenationと、queryとの対応の知識
- Repository - Model/Resourceの作成、取得・更新と通知
  - Model/Resourceの状態管理: none, loading(progress), updated(timestamp), expired, error(info)
  - none/expiredなModel/Resourceを取得・更新
  - 100ms以上loadingが続いたら通知 (通信中の表示)
  - 更新の通知 (Realm.Notification? NotificationCenter?)
  - 無限スクロールやpagenationの状態管理、チャンク管理、取得・更新
  - 取得の優先度管理
- Actions - 画面遷移・投稿・更新系の処理
  - 必要なダイアログの知識 (投稿や削除の確認、アクションシートなど)
  - POST系APIの実行
  - エラー時の対策手段の提示 (ダイアログのメッセージなど)
  - 成功時に影響を受けるResourceのexpire処理
  - 遷移先画面の知識 (URIスキーム)
  - 複数の画面遷移を伴ってもよい？ (チュートリアルやフォームやカート的な)
- Router - ユーザのモード切り替えの知識
  - URIスキームのNotificationを処理
  - タブやナビゲーションバーの管理
  - タブに積まれたViewやActionの情報
  - ViewやActionを生成してスタックに積む
  - 積まれたActionからのダイアログの表示やインタラクション
  - 通信中、通信エラー等の通知
- Storage - Resource/Modelのキャッシュ、リファレンス管理
  - 基本はIn-memoryキャッシュ (Realm)
  - 非同期で永続化 (Realm)
  - ModelのObserverが無くなってしばらく経ったらIn-memoryキャッシュから削除
  - Modelが参照されたらIn-memoryキャッシュに復元
  - 永続DBも大きくなったら古い情報を削除？
- Service - API通信やリトライ (Alamofire/SwiftTask)
  - 複数のトリガーが同時に来ても同じAPI通信を投げない仕組み
  - キャンセルやキューイング
- Device
  - ネットワーク接続状態の検知と通知 (NotificationCenter)

# 検討中

- 画面表示だけ簡単にチェックする仕組み (Playground? URIスキーム?)
- GET系は先に画面遷移、POST系は成功/失敗してから画面遷移
Laravel5 Bladeテンプレートエンジンで出力されるHTMLで<head></head>内にコンテンツが入ってしまう

#結論
```*.blade.php
@extends('layouts.xxx')

@section('content')



@endsection // ←これを忘れている
```

原因が残念すぎて検索しても該当する情報が見当たらない。
エラーも出ないのでこの現象に出くわすとかなり慌てる。
php-master-changes 2018-11-30

今日は strncmp() のテスト修正があった！それだけだった！

# 2018-11-30
## swalk-cavium: fix bad assumption of strncmp return value
- https://github.com/php/php-src/commit/4e63df88d57dc2ef1634d546d513623469144af3
- [strncmp()](http://php.net/manual/ja/function.strncmp.php) の AArch64 でのテストこけを修正
- [#74082](https://bugs.php.net/bug.php?id=74082) の修正
- が、[PR:3689](https://github.com/php/php-src/pull/3689) で「そもそもこのテスト要らねえよな」という話になる

## swalk-cavium: [ci skip] remove test per discussion on github
- https://github.com/php/php-src/commit/ea1ea33bd5d967e9c04046c1af869e4e8b53116e
- ↑のテストを削除
よちよちKotlin - Android グラフ描画

## 1. 事前準備
Android Studioのインストール
SDKのインストール
Project > New　で新規のプロジェクトを作成する

参考サイト: 
https://github.com/AnyChart/AnyChart-Android/wiki/Getting-started
https://github.com/AnyChart/AnyChart-Android/tree/master/sample/src/main/java/com/anychart/sample/charts
https://wannabe-jellyfish.hatenablog.com/entry/2015/08/08/231840

## 2. AnyChartをgradleに追加
https://github.com/AnyChart/AnyChart-Android

## 3. gradleの設定ファイルを変更
### 3.1 mavenリポジトリの追加
<img width="253" alt="スクリーンショット 2018-12-01 10.54.59.png" src="https://qiita-image-store.s3.amazonaws.com/0/89997/e2afc077-0419-a2f0-a557-dae594f5accd.png">をクリックして選択

allprojects > repositories　の下に追加します。

>maven { url 'https://jitpack.io' }

```
allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }
    }
}
```
### 3.2 dependencyの追加
<img width="199" alt="スクリーンショット 2018-12-01 10.58.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/89997/156c71c7-e896-926b-f1d9-2fae6fb4ad1d.png">をクリックして選択


dependenciesの最後に追記します。

### 3.3 dependencyの追加
> implementation 'com.github.AnyChart:AnyChart-Android:1.0.6'

```
dependencies {
    .....省略
    implementation 'com.github.AnyChart:AnyChart-Android:1.0.6'
}
```
### 3.4 Sync
<img width="147" alt="スクリーンショット_2018-12-01_11_00_01.png" src="https://qiita-image-store.s3.amazonaws.com/0/89997/6af6c237-b025-4e9c-641b-705536de3707.png">
画面右上のMenuからSyncボタンを押してSyncをするとインストールされます。

## 4. サンプルコード追加
お試しなのでサクッとMainActivityの中のonCreateの一番下に追記しちゃいます。

```kotlin
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        .......
        .......
        Pie Chart Example
        val pie = AnyChart.pie()
        val data = ArrayList<DataEntry>()
        data.add(ValueDataEntry("John", 10000))
        data.add(ValueDataEntry("Jake", 12000))
        data.add(ValueDataEntry("Peter", 18000))
        pie.data(data)
    }
}
```

```res/layout/content_main.xml
    <com.anychart.AnyChartView
            android:id="@+id/pie_chart_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
    />

```

<img width="301" alt="スクリーンショット 2018-12-01 13.07.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/89997/e66f682a-4c69-1022-c427-103d41ab0f53.png">

## よちよちKotlin勉強会
(もくもくしてます)
https://techplay.jp/event/708101
PostgreSQL8.4でユーザのアクセス権限をテーブル全てに適用する方法

#経緯
PostgreSQLを使用する機会があり、要件としてはテーブルのアクセス権限を
ユーザ事に分ける必要がありました。
例：
Aユーザは読み取りのみ
Bユーザは読み取り、書き込み、削除

これがMySQLであればユーザ事に以下の設定で行けます。（たぶん）
一括の場合
`GRANT 権限 ON DB名.* TO user;`
テーブルごとの場合
`GRANT 権限 ON DB名.テーブル名 TO user;`

ちなみに現状は・・・
![image.png](https://qiita-image-store.s3.amazonaws.com/0/316662/711f3289-7811-5bd7-9a0d-3bebfbceb47e.png)


#ググってみた
MySQLは触ったことがある(一般的なSQL分を投げる程度)が、
PostgreSQLは未知の世界だったので調べてみた。

すると大体以下のコマンドを記載している記事がヒットします。
`GRANT SELECT ON ALL TABLES IN SCHEMA public TO user;`

#実行してみた
`dbtest=# GRANT SELECT ON ALL TABLES IN SCHEMA public TO user;
ERROR:  "ALL"またはその近辺で構文エラー
行 1: GRANT SELECT ON ALL TABLES IN SCHEMA public TO user;`

ALLなんてないけどと怒られる・・・

#更にググってみた
どうやらALLはPostgreSQL9以降で使えるらしい？
[19.2.6. 履歴データベースへの読み取り専用アクセスの許可](https://access.redhat.com/documentation/ja-JP/Red_Hat_Enterprise_Virtualization/3.2/html/Administration_Guide/Allowing_Read_Only_Access_to_the_History_Database.html)

#更に実行してみた
- ユーザ作成
`# psql -U postgres -c "CREATE ROLE ユーザ名 WITH LOGIN;" DB名`

- ユーザへDBへの接続権限付与
`# psql -U postgres -c "GRANT CONNECT ON DATABASE DB名 TO ユーザ名; DB名`

- public スキーマの使用をユーザーに許可
`psql -U postgres -c "GRANT USAGE ON SCHEMA public TO ユーザ名;" DB名`

- ユーザーに許可するパーミッションと全テーブルを対象とするSQL文を生成して、ファイルに保存
`# psql -U postgres -c "SELECT 'GRANT 付与したい権限 ON ' || relname || ' TO ユーザ名;' FROM pg_class JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace WHERE nspname = 'public' AND relkind IN ('r', 'v');" --pset=tuples_only=on DB名 > grant.sql`

- 作成されたgrant.sqlを実行
`psql -U postgres -f grant.sql DB名`
GRANTの表示が出ればOK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/316662/86645b21-1aad-e30f-7c97-c0cd51309f86.png)
権限を今回SELECTのみ付与した為、user01にrが追加されました。
(postgresアカウントはMySQLのrootの様な管理者アカウントなのでマスター権限が自動で付与されました。)

以下引用元[DBOnline](https://www.dbonline.jp/postgresql/role/index3.html)

              =xxxx -- PUBLICに与えられた権限
         uname=xxxx -- 1人のユーザに与えられた権限
    group gname=xxxx -- グループに与えられた権限
                  r -- SELECT（読み取り（read））
                  w -- UPDATE（書き込み（write））
                  a -- INSERT （追加（append））
                  d -- DELETE
                  R -- RULE
                  x -- REFERENCES
                  t -- TRIGGER
                  X -- EXECUTE
                  U -- USAGE
                  C -- CREATE
                  T -- TEMPORARY
            arwdRxt -- 全ての権限（テーブル用）
                  * -- 直前の権限に関するグラントオプション

              /yyyy -- この権限を付与したユーザ

#感想
調べ方が悪いのか古いバージョンだからなのかわかりませんが、対応方法がなかなか見つかりませんでした。
全テーブルに権限付与という操作はレアケースではないと思うのですが、初PostgreSQLということもあり苦戦しました。
誰かのお役に立てれば幸いです。
【FileMaker】FileMaker Goでクラウド案件計算

2018年も[QiitaでAdvent Calendar](https://qiita.com/advent-calendar/2018)が始まりました。
今回は[FileMaker GoのAdvent Calendar](https://qiita.com/advent-calendar/2018/filemakergo)も立ち上げてみましたので、他の方もどうぞ記事をお寄せください。

さて、フリーランスの私はネットの仲介サイトを介してお仕事を受注することもあります。
その際、見積金額を出すのですが、手数料がいくらかかって結局手取り金額がいくらになるのか、、、いちいちヘルプを見てめんどくさい計算を紙に書いて元本がこれだと手数料がいくら、と計算するのが面倒で、サクッと知りたいなと思い、FileMakerで作りました。

#動き
以下のYoutubeに公開しています。
https://youtu.be/BXePTdbj5ck

上部の仲介サイト名のタブをクリックすると、それぞれのページが開きます。
<img width="375" alt="FileMakerGo_Cloud4.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/60538/5b3b9042-d6fe-61b3-590e-12b3cc43bf54.png">

手数料率が設定されていますが、これは再設定できます。
<img width="375" alt="FileMakerGo_Cloud5.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/60538/b54e9516-a681-578f-ebeb-9a7ddbf62f3c.png">

基本的に入力するのは、元本のみです。
<img width="375" alt="FileMakerGo_Cloud6.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/60538/ce298ef8-0e8a-5054-8a7b-caa011152f0a.png">
元本を入力すると、手数料と手取りがくを自動で計算します。

#画面
それぞれの仲介サイトの切り替えは、ボタンバーツールを使っています。
![FileMakerGo_Cloud1.png](https://qiita-image-store.s3.amazonaws.com/0/60538/5e2601d7-b4bb-9083-d310-1e6b553005ff.png)
単純にレイアウトの切り替えをするだけなので、以前入力した値はそのまま出ますが、入力する元本をクリアしたい場合は、スクリプトにして、レイアウトの切り替えと元本のフィールドセットを「""」にしておけば入力時にいちいちクリアする必要もありません。

ただ、金額を見比べたい時があると思います。
その場合は元本がクリアになってしまうとまた入力しなければならないので、元本フィールドクリアはしないほうがいいです。

#データベース
テーブルは、どうなっているでしょう。
今回は、仲介サイトごとにテーブルを作りました。
![FileMakerGo_Cloud2.png](https://qiita-image-store.s3.amazonaws.com/0/60538/9db468c7-805b-2bdc-ba72-e8eb115f557a.png)
![FileMakerGo_Cloud3.png](https://qiita-image-store.s3.amazonaws.com/0/60538/4848163b-0041-c5ef-5444-c387dfb926a7.png)
もっと汎用的に作ってもいいのですが、そうすると仲介サイト基本データまでたどり着くリレーションシップを構築したりと初心者には一歩踏み込んだ実装になりますので、慣れない時はそれぞれでテーブルを作って実装してみてください。

ただ、個別に作りすぎると追加修正の際、似たようなフィールドをそれぞれのテーブルに追加することになりメンテナンス性が悪くなります。
その時、もっと効率よくできないか？といろんな方法の実装を試みてください。
（ファイルのバックアップは忘れずに！）

#まとめ
手取り金額の計算の他にも、自分の損益分岐点を求める機能も追加したかったのですが、まずは簡単に計算のみの実装で使っています。

今回のサンプルファイルは、以下のGitHubからダウンロードできますので、ぜひ参考にして、そしてFileMaker Goで動かしてみてください。
Sample_CloudAgencyTool.fmp12というファイル名です。
Github Repository : [FileMakerSample](https://github.com/atsukosekiguchi/FileMakerSample)

FileMaker Goで何ができるのか、というのはもうずいぶん前に言われていましたが、今ではなんでもできますよね（個人的感想）。
だからと言って、市販のアプリのような高度な物を作りたいと一念発起して取り組むとへこたれることもあるので、手っ取り早く自分のちょっと不便を取り除くツールを作って練習して関数やレイアウトの実装などの感触を体験していくのもいい方法だと思います。

そんな小さなちょっと便利アプリがFileMaker Goという手元にあるだけで、ちりつもで時間やコストが削減されていきますね。
（自分時間を増やしたい）

FileMaker Goアプリ、実践と演習のスパイラルで「働き方改革」の第一歩を踏み出してみませんか？
最寄り駅を導出するアルゴリズム

## はじめに

住所や緯度経度を扱うプログラミングをしていると、「最寄り駅を出しましょう」という要件が時々出てくる。

その時のアプローチはこんな感じ。

## APIを使うという方法

http://api-sdk.navitime.co.jp/api/
当然のことながら地図や経路探索サービスを提供している会社からAPIを使うというのが一番てっとりばやい。ただお金はかかる。

## 緯度経度から距離を求めて並び替える方法

GoogleMapのAPIを始め、大半の地図サービスはランドマークや住所から緯度経度に変換してくれるサービスを提供されている。有料のことも多いが、緯度経度を導出するだけなら安い。また最初から緯度経度が提供されていることも多い。

経度１度あたりの距離というのは、地球が丸いから緯度によって変わってくる。赤道の方が緯度あたりの距離は長いし、北に行くほど短い。したがって、ちょっとだけ面倒な計算が発生する。

アルゴリズム自体は地球が球体であることを前提に経度あたりの距離を補正するだけなので、10行もかからない。
https://qiita.com/4geru/items/27785e9066c4e22eeaa3
ただ、「最寄り」を導出するには、自分の位置から、他のスポットの緯度経度をすべて計算して、距離順にソートという面倒なことをしないといけない。

MySQLにはgeometoryという型があるので、そこに緯度経度を格納するという方法もある。
https://qiita.com/mitani/items/6909406ac4fe0db2d35c

ただ、当たり前だけど専用の方はバイナリになって扱いにくい。DBエンジンにすごく依存する。上記サンプルでは計算値フィールドで並び替えたりしているなど、手放しでは採用できない。

## 最初から緯度経度で絞る方法

考えてみると、地図アプリが表示をするときって、「最寄り」の計算なんてしてない。やっているのは、緯度FROM,緯度TO,経度FROM,経度TO の４点を指定してその範囲に含まれるスポットを出しているだけだ。

「最寄り」と考えるから距離を計算して距離の小さい順にソートしようとしてしまっている。10Km先が最寄りであってもそれは最寄り駅とは言わない。大事なのは、「近くの緯度経度に含まれるスポット」なのだから、`計算位置フィールドのORDERBY LIMIT` ではなく、`WHERE 緯度経度の範囲` なのだ。実データに対するWHEREならインデックスが効くので高速に検索できる。

そして最寄り駅のレベルなら徒歩圏内なので地球の丸さは影響しない。絞り込んだあとに緯度経度の差分の絶対値をみれば距離にも並べられる。これはフロント側で行っても良い。
Amazon Linux 2 に Apache JMeter をインストールする

## Java のインストール

```
[ec2-user@ip-172-31-43-188 ~]$ java -version
-bash: java: command not found
```

```
[ec2-user@ip-172-31-43-188 ~]$ sudo yum install -y java-1.8.0-openjdk.x86_64 java-1.8.0-openjdk-devel.x86_64
```

```
[ec2-user@ip-172-31-43-188 ~]$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

[ec2-user@ip-172-31-43-188 ~]$ javac -version
javac 1.8.0_191
```

## JMeter のインストール

https://jmeter.apache.org/download_jmeter.cgi

ここからバイナリをダウンロードする。

```
[ec2-user@ip-172-31-43-188 ]$ mkdir jmeter && cd jmeter
[ec2-user@ip-172-31-43-188 jmeter]$ wget http://ftp.kddilabs.jp/infosystems/apache//jmeter/binaries/apache-jmeter-5.0.tgz
--2018-12-01 03:30:16--  http://ftp.kddilabs.jp/infosystems/apache//jmeter/binaries/apache-jmeter-5.0.tgz
Resolving ftp.kddilabs.jp (ftp.kddilabs.jp)... 192.26.91.193, 2001:200:601:10:206:5bff:fef0:466c
Connecting to ftp.kddilabs.jp (ftp.kddilabs.jp)|192.26.91.193|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 53847192 (51M) [application/x-gzip]
Saving to: ‘apache-jmeter-5.0.tgz’

 2% [=>                                                                                 ] 1,360,323   13.3KB/s  eta 58m 27s
 ```

デフォルトのホスト `ftp.kddilabs.jp` から wget しようとしたら非常に遅かったのでミラーサイトに変更。

 
 ```
 [ec2-user@ip-172-31-43-188 jmeter]$ wget http://ftp.jaist.ac.jp/pub/apache//jmeter/binaries/apache-jmeter-5.0.tgz
--2018-12-01 03:31:51--  http://ftp.jaist.ac.jp/pub/apache//jmeter/binaries/apache-jmeter-5.0.tgz
Resolving ftp.jaist.ac.jp (ftp.jaist.ac.jp)... 150.65.7.130, 2001:df0:2ed:feed::feed
Connecting to ftp.jaist.ac.jp (ftp.jaist.ac.jp)|150.65.7.130|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 53847192 (51M) [application/x-gzip]
Saving to: ‘apache-jmeter-5.0.tgz'

100%[==================================================================================>] 53,847,192  13.9MB/s   in 3.8s

2018-12-01 03:31:55 (13.6 MB/s) - ‘apache-jmeter-5.0.tgz’ saved [53847192/53847192]
```

ダウンロードできたので、展開する。

```
[ec2-user@ip-172-31-43-188 jmeter]$ tar -xf apache-jmeter-5.0.tgz
```

サンプルの jmx を実行してみると、正常に動作している様子。

```
[ec2-user@ip-172-31-43-188 jmeter]$ cd apache-jmeter-5.0/bin
[ec2-user@ip-172-31-43-188 bin]$ ./jmeter.sh -n -t examples/CSVSample.jmx
Dec 01, 2018 3:35:47 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
Creating summariser <summary>
Created the tree successfully using examples/CSVSample.jmx
Starting the test @ Sat Dec 01 03:35:47 UTC 2018 (1543635347849)
Waiting for possible Shutdown/StopTestNow/Heapdump message on port 4445
summary =     12 in 00:00:03 =    4.4/s Avg:   199 Min:   100 Max:   327 Err:     0 (0.00%)
Tidying up ...    @ Sat Dec 01 03:35:50 UTC 2018 (1543635350635)
... end of run
```

## 参考

- https://www.youtube.com/watch?v=8_XM-z7blyk
Rspecで特定のテストケースでキャッシュストアを変更する

Rspecで特定のケースでは、cache_storeを変更したい場合があったのでその対応について残しておきます。

## Rails.cacheをmockする
以下のようにActiveSupport::Cache.lookup_storeでRails.cacheモックすることでcache_storeを変更できます。

```rb
describe 'キャッシュストアを変更してテスト' do
  let(:memory_store) { ActiveSupport::Cache.lookup_store(:memory_store) } 

  before(:each) { allow(Rails).to receive(:cache).and_return(memory_store) }
  after(:each) { Rails.cache.clear }
  
  it 'hogehoge' do
    ...
  end
end
```

上の例では、 `:memory_store` を設定していますが、  `:null_store` 、 `:redis_store` など必要に応じて変更することができます。

## metadataで設定を切り替えれるようにする
実際のプロジェクトで使う場合は毎回sotreの変更処理を書くのは手間なのでrails_helper.rbなどに以下のようにmetadataの設定のみで使えるようにするのが良いと思います。

```rb:rails_helper.rb
# 特定のキャッシュストアを使用したい場合に「chache_type: :memory_store」のように
# metadataを追加することで、そのテストケースのキャッシュストアを変更する。
config.around :each do |example|
  if example.metadata[:chache_type].present?
    old_store = Rails.cache
    chache_store = ActiveSupport::Cache.lookup_store(example.metadata[:chache_type])
    Rails.cache = chache_store
    example.run
    Rails.cache.clear rescue nil
    Rails.cache = old_store
  else
    example.run
  end
end
```

```rb:hoge_spec.rb
describe 'キャッシュストアを変更してテスト', chache_type: :memory_store do
  it 'hogehoge' do
    ...
  end
end
```

## 参考
https://makandracards.com/makandra/46189-how-to-rails-cache-for-individual-rspec-tests
他の言語(Java)っぽくPerlを使う話

この記事はPerl Advent Calendar 2018の2日目の記事です。

皆さん、PerlでOOPしてますか？
私は今年Perlに入門したのですが、Perlのオブジェクト指向はかなり独特だと感じました。
MouseやMooseを使って簡単に書くことはできますが、やはりそれでも他言語の構文との差はあるように思います。

そこで、Perlの柔軟な文法を活かしてclass構文を作ってみることにしました。

# 方針
- できるだけJavaっぽくする
- blessは裏側でやる
- クラス名をクォートで囲わなくてはいけない箇所を減らす
- 素のPerlよりも堅牢になる仕組みを組み込む

# 実装
https://github.com/yumlonne/p5-Class-JavaLike
突貫で作っていたためコード汚いです:bow:

現在実装できているのは以下の機能です

- class構文
- アクセス修飾子(public・protected・private)
- 変数宣言(`public var => @array`)
- コンストラクタ
- メソッド
- 引数型チェック
- 単純な継承

# 使用例
```perl
use Class::JavaLike;
use Class::JavaLike::Classes;
use Types::Standard -types;

# classの定義
class Point2D => sub {
    # 変数の定義
    public var => qw(x y);

    # コンストラクタ
    public new => args[Int, Int] => returns[classes->Point2D] => constructor {
        my ($self, $x, $y) = @_;
        $self->x = $x;
        $self->y = $y;
        return $self;
    };

    # addメソッド
    public add => args[classes->Point2D] => returns[classes->Point2D] => method {
        my ($self, $that) = @_;
        return classes->Point2D->new($self->x + $that->x, $self->y + $that->y);
    };

    # プライベートなnegateメソッド
    private negate => args[] => returns[classes->Point2D] => method {
        my $self = shift;
        return classes->Point2D->new(-$self->x, -$self->y);
    };
};
```

こんな感じでclassを定義できます。
裏側では、パッケージ`Class::JavaLike::Class::${ClassName}`にメソッド群を植え付けています。
このクラスは以下のように使えます。

```perl
# usage
my $p1  = classes->Point2D->new(10, 20);
my $p2  = classes->Point2D->new(22, 18);
my $res = $p1->add($p2);

say $res->x;    # -> 32
say $res->y;    # -> 38
```

`classes->Point2D`は単に`'Class::JavaLike::Class::Point2D'`という文字列を生成しています。
同様のことが`classof('Point2D')`でできますが、クォートが冗長だと思い、AUTOLOADを使ってクォート無しで指定できるようにしました。

また、継承ができるので、Point2Dを継承させてPoint3Dを作ることもできます。

```perl
class Point3D => extends Point2D => sub {
    public var => 'z';
    public new => args[Int ,Int ,Int] => returns[classes->Point3D] => constructor {
        my ($self, $x, $y, $z) = @_;
        $self->x = $x;
        $self->y = $y;
        $self->z = $z;
        return $self;
    };

    public add => args[classes->Point3D] => returns[classes->Point3D] => method {
        my ($self, $that) = @_;
        return classes->Point3D->new($self->x + $that->x, $self->y + $that->y, $self->z + $that->z);
    };
};
```

# 展望
戻り値の型チェックや再代入不能な変数(`val`)の導入などなど、Perlでのプログラミングを堅牢にする仕組みを導入できそうですね！
まだまだ未完成なので、今後も改善していきます。

# 最後に
Perlの文法を使い、何ができるか考えるのがすごく面白かったです。
みなさんもやってみてはいかがでしょうか。


明日は [omokawa_yasu](https://qiita.com/omokawa_yasu) さんです！お楽しみに！！
Swift 2chへの書き込み

# Alamofireを使っての2chへの書き込み
```swift
    func postTo2ch(_ urlString: String, _ postText: String) {

        let url     = URL(string: urlString)
        let domain  = (url?.host)!  // ドメイン

        let subStrings  = domain.components(separatedBy: ".")
        let server      = subStrings[0] // サーバー

        let paths   = urlString.components(separatedBy: "/")
        let thread  = paths[paths.count-2]  // スレッド
        let board   = paths[paths.count-3]  // 掲示板種別

        let bbsUrl  = "https://\(server).5ch.net/test/bbs.cgi"  // 投稿先CGI

        let headers: HTTPHeaders = [
            "Referer": urlString,
            "Accept-Encoding": "gzip",
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
            "Accept-Charset": "ISO-8859-1,utf-8;q=0.7,*;q=0.3",
            "Accept-Language": "en-US,en;q=0.8",
            "Connection": "keep-alive"
        ]
        let parameters: [String: Any] = [
            "bbs": board,
            "key": thread,
            "time": Int(Date().timeIntervalSince1970) - 60,
            "FROM": "",
            "subject": "",
            "mail": "",
            "MESSAGE": postText,
            "submit": "書き込む"
        ]

        Alamofire.request(bbsUrl, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseString { response in
            // 初回アクセス時のみクッキーを設定する
            if response.description.contains("書き込み確認") { 
                let res = response.response
                let cookies = HTTPCookie.cookies(withResponseHeaderFields: res?.allHeaderFields as! [String : String], for: (res?.url!)!)
Alamofire.SessionManager.default.session.configuration.httpCookieStorage?.setCookies(cookies, for: response.response?.url, mainDocumentURL: nil)
                // クッキーを設定してもう一回投稿！
                Alamofire.request(bbsUrl, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseString { response in
                    print(response)
                }
            }
        }
    }

// 使い方
self.postTo2ch("http://awabi.2ch.sc/test/read.cgi/male/1514976917/", "テスト")
```
- 改善すべき点
 - 掲示板のURLと書き込みCGIのURLが異なるようで、初回のクッキー保存が一度のアクセスでできなかった
 - 初回アクセス判定をレスポンス内の「書き込み確認」で行なっているのをやめたい
 - URLのパースももっとシンプルにしたい
 
[https://github.com/katafuchix/2chPost](https://github.com/katafuchix/2chPost)
TclでHello, world!

# 前置き
ActiveTcl 8.6 + Windows10 Japanese 64bitで動作確認しました。

スクリプト冒頭3行の

```sh
#!/bin/sh
#\
	exec tclsh "$0" ${1+"$@"}
```

や

```sh
#!/bin/sh
#\
	exec wish "$0" ${1+"$@"}
```

はActiveTclで実行する場合は不要なので以下では省略します。UNIX OSやCygwinでファイル名だけで実行する場合は必要です。

# Tcl編
```:実行方法
> c:\activetcl\bin\tclsh.exe hello.tcl
```

## シンプルにHello, world! ～その1～
```tcl:hello.tcl
puts {Hello, world!}
```

## シンプルにHello, world! ～その2～
```tcl:hello.tcl
puts "Hello, world!"
```

## 標準エラー出力にHello, world!
```tcl:hello.tcl
puts stderr "Hello, world!"
```

## 細切れにHello, world!
```tcl:hello.tcl
puts -nonewline "Hello"
puts -nonewline ", world!"
puts -nonewline "\n"
```

## printf風にHello, world!
```tcl:hello.tcl
puts -nonewline [format "%s, %s!\n" Hello world]
```

## 変数に入れてからHello, world! ～その1～
```tcl:hello.tcl
set foo "Hello, world!"
puts $foo
```

## 変数に入れてからHello, world! ～その2～
```tcl:hello.tcl
set foo Hello
set bar world
puts "$foo, $bar!"
```

## 変数に入れてからHello, world! ～その3～
```tcl:hello.tcl
set foo Hello
set bar world
puts "[set foo], [set bar]!"
```

## リストをつなげてHello, world!
```tcl:hello.tcl
set foo [list Hello world!]
puts [join $foo ", "]
```

## 関数の処理でHello, world!
```tcl:hello.tcl
proc helloworld {} {
    puts "Hello, world!"
}
helloworld
```

## 関数の戻り値でHello, world!
```tcl:hello.tcl
proc helloworld {} {
    return "Hello, world!"
}
puts [helloworld]
```

## シリアルポートにHello, world!
COM1とCOM2がクロスケーブルで接続されているとして。

```tcl:hello.tcl
set ch1 [open "\\\\.\\COM1" r+]
fconfigure $ch1 -mode 9600,n,8,1
set ch2 [open "\\\\.\\COM2" r+]
fconfigure $ch2 -mode 9600,n,8,1 -buffering line
puts $ch2 "Hello, world!"
close $ch2
puts [gets $ch1]
close $ch1
```
Ogaki Mini Maker Faire 2018に出展しています

Ogaki Mini Maker Faire 2018が開幕しました！

https://www.iamas.ac.jp/ommf2018/
（12/1-2、岐阜県大垣市のソフトピアジャパン・センタービル）

![IMG_2836.JPG](https://qiita-image-store.s3.amazonaws.com/0/109634/16746af5-58bc-9ed1-b59b-66e91f1b21d0.jpeg)

もちろん、Seeedも出展しています。

![IMG_2835.JPG](https://qiita-image-store.s3.amazonaws.com/0/109634/c0701416-05a7-f266-65c5-381b651c42b7.jpeg)

展示品は、、、
・Wio LTE Cat.1, Wio 3G, Wio LTE M1/NB1
・ReSpeaker Mic Array 2.0, ReSpeaker Core v2.0
・Azure Sphere MT3620開発キット, Azure Sphere Grove Starter Kit
・Grove Base HAT for Raspberry Pi/Raspberry Pi Zero  <--- New
・ReButton  <--- New!
・Wio Nodeの事例  <--- New!

プレゼンは、、、
Seeedシャチョーが12/2 14:00-14:20にSeeed製品紹介のプレゼンテーションをする予定です。



2日間、Seeed社員、事例の研究員がいます。お気軽にお声がけくださいませ。
C++で‘gets’ was not declared in this scopeエラー

コンパイラのバージョン
g++ (Debian 6.3.0-18+deb9u1) 6.3.0 20170516

[ロベールのＣ＋＋教室のweb版](http://www7b.biglobe.ne.jp/~robe/cpphtml/)第５４章を読んでいた。（最終更新日が2000.6.24）
getsを使用したとき下記のエラーが出た

```
error: ‘gets’ was not declared in this scope
```

コードは下記

```C++:File3.cpp
#include <stdio.h>
#include <string.h>

int main()
{
    FILE* pFile;
    char  buffer[512];
    int   i;

    printf("何か文字列を入力して下さい > ");
    gets(buffer);

    pFile = fopen("fprintf.txt", "w");
    fprintf(pFile, "%d\n", strlen(buffer));
    for(i = 0; buffer[i]; i++)
        fprintf(pFile, "%02X ", (unsigned char)buffer[i]);
    fclose(pFile);

    return 0;
}
```

`gets`ではなく`fgets`を使う。`gets`は[使用してはいけない](https://stackoverflow.com/questions/35250198/gets-was-not-declared-in-this-scope-error/35250237#comment58213750_35250198)らしい。

```C++:File3.cpp
#include<stdio.h>
#include<string.h>

int main(){

    FILE* pFile;
    char buffer[512];
    int i;

    printf("何か文字列を入力して下さい > ");
    fgets(buffer,256, stdin);
    //gets(buffer);

    pFile=fopen("fprintf.txt", "w");
    fprintf(pFile, "%d\n", strlen(buffer));
    for(i=0; buffer[i]; i++)
        fprintf(pFile, "%02X", (unsigned char)buffer[i]);

    fclose(pFile);

    return 0;

}

```
Deep Learning Advances on Different 3D Data Representations: A Survey


ごめんなさい。まだ下書きに近いです。。。

#TL;DL
* 3Dデータに対する深層学習手法のsurvey論文（[Deep Learning Advances on Different 3D Data Representations: A Survey](https://arxiv.org/abs/1808.01462)）を読んだ
* 3Dデータはユークリッドデータと非ユークリッドデータに大別される
* ユークリッドデータは通常のCNNを応用可能
* 非ユークリッドデータは独自の畳み込み手法を定義する必要がある

# 3次元データの種類
## ユークリッドデータ
* RGB-D
    * 写真に対して深度情報を付与したデータ
    * カメラとLiDARの組み合わせやKinectなど
* ボクセル
    * ある単位の立方体（ボクセル）を空間的に想定して、ボクセルを積み上げて三次元を表現する
    * ボクセルの作り方は点群から作成したりする
* マルチビュウデータ
    * 表現したい立体的なデータに対して、あらゆる方向から撮影した画像を作成
## 非ユークリッドデータ
* ３次元点群
    * x、y、zで表現される点の集まり
    * 同じ物体を表現するにしても、シーケンシャルになっていなくて、順番が変わったりする
* Graph 
    * 点群の各点をノードとして、近傍のつながりをエッジとする情報を持つ
* メッシュも結局Graph
    * メッシュは点群の頂点をノードとして、周辺の点群に対して三角形を作るようなエッジを持つ
    * つまりGraph


![3次元データの種類](https://qiita-image-store.s3.amazonaws.com/0/86094/1e2a7215-7188-9623-be3b-cdde316a49d9.png)

# 深層学習手法の適用
## ユークリッドデータ
* RGB-D
    * 単なる画像なので、一緒にCNNに入れるもよし
    * ２ストリームでRGB画像だけでCNNに入れ、Depth画像をCNNに入れて、FC層でConcatすることもOK
    ![image.png](https://qiita-image-store.s3.amazonaws.com/0/86094/a6a9d3e1-11bb-a904-640d-1f1bdfb1448d.png)

* ボクセル
    * 画像は２次元だっかけど、ボクセルになったデータは３次元の畳み込みをやればいい
    * ２次元画像の素直な拡張になる
    * ただし、表面形状を扱うので、空間的には疎な多次元配列になる
     ![image.png](https://qiita-image-store.s3.amazonaws.com/0/86094/df040aa9-11d3-abfc-a21f-fd5e712cff77.png)

* マルチビュウデータ
    * 各視点の画像は単なるRGB画像なので普通のCNNに入れることができる
    * 各支店の情報をFC層でConcatしたりすると立体的な情報をとることができる
     ![image.png](https://qiita-image-store.s3.amazonaws.com/0/86094/af1edebf-fe7f-1835-6a2c-52baf6cc7404.png)

## 非ユークリッドデータ
* ３次元点群
    * x,y,zの集まりである点群は入力される順番に依存しないような特徴量抽出が重要
    * PointNetでは入力順番に依存しないようにMaxPooingを入れている
*  Graph 
    * ある点に対して特徴をまとめたい点群をGraph構造を作成
    * そのGraphに対してGpaphConv.を適用
    * GraphCopnv.といっても単なるMLPであることが多い

![モデル](https://qiita-image-store.s3.amazonaws.com/0/86094/c51ce141-b428-1729-1e8a-fe7da67db498.png)
Teams Only Modeにしてみた

LyncからSkype for Businessへの切り替えは、クライアントアプリの入れ替えだけで済んだので課題は少なかった。しかし、Skype for BusinessからTeamsへの移行は使うサービス自体が別物なのでハードルは前者とくらべて高い。

移行するにあたり、「両方を併存させているモード（アイランドモード）」から、「Teams Only Mode（SkypeからのIMもTeamsで受けられるモード）]に順次ユーザーに適用・拡大していけばスムーズじゃないか、ってことで適用と、実際に適用して困ったことをまとめてみた。

## Teams Only Modeの適用
テナントに一発適用という方法もあるけど、そんなことすると刺されかねない。まずはユーザー別に適用。Win10で実施しました。

1.モジュールのインストール
https://www.microsoft.com/ja-jp/download/details.aspx?id=39366
ここからダウンロードしてインストール。365管理者の方なら、すでにインストール済みかも。

2.PowerShellで以下実施

    $session = New-CsOnlineSession -Username <管理者UPN> -verbose
    Import-PSSession $session -AllowClobber
黄色いエラーメッセージ出るけど、無視。文字化けもスルーで。もちろん、パスワードは入れる。

    Grant-CsTeamsUpgradePolicy -PolicyName UpgradeToTeams -Identity <対象ユーザーのUPN>

正常に処理されると、「Skype for Businessクライアントつかえなくなるからね！」というようなメッセージが出る。適用されたかどうかは、Teams管理センターでも確認可。


※こちらでは、一発適用されたようですが、怖くてできません。とっととやってしまいたい誘惑にかられます。うらやましい・・・
https://blog.kazuakix.jp/entry/2018/11/08/234326


## Teams Only Mode使ってみた
早速使ってみました。いまのところ、気になっているのは2点です。

- やっぱり便利なこと

    -Skype4BからのIMを、Teamsで受けられる。当たり前だけど、めっちゃ便利です。
    -Skype4Bクライアントを常時起動しなくていい。当たり前だけど、マシン負荷減ります。

- 実はこまらなかったこと

    -Skype会議に参加できない？→URLをもらっていたら、Skype4Bクライアントで参加できました。
    -Skype会議のURLを発行できない？→OWAの予定表からはSkype会議URL発行できました。

- やっぱり困ってること

    -Skypeしか使っていないユーザーにIMできない。
過去に先方からIMもらっている履歴がTeamsクライアント上に残っていれば、返信という形Skype4B宛にIM送れる。「先方との履歴がない」かつ「先方がTeams使ったことない」場合は、詰んでしまう。
もちろん、相手がTeams使っているユーザーなら悩まなくてよいのだけど・・・。


    -チャット＋デスクトップ共有ができない
会議してたらデスクトップ共有できるけど、チャット＋デスクトップ共有の機能がない。私だけかもしれなけど、画面を見せつつチャットする、という使い方をするので結構いたい。


後者はともかく、「新規に」TeamsからSkype4BにIMを送れるようになればTeams移行のハードルって一気に下がるのだけど、マイクロソフトさん何とかしてくれませんかね？
新しいビデオコーデックのAV1を使って無理やり HLSでストリーミングしてみた

ffmpeg でAV1で(無理矢理)いろいろやってみました。

# ffmpeg 4.1

AV1のcodecそのものはffmpeg 4.0でサポートされましたが、mp4コンテナに書き出すことはできませんでした。
ffmpeg 4.1 でそれが可能になりました。

# AV1のテスト映像を作る

現状AV1のエンコード速度はとてもとても遅くてライブ配信には間に合いません。
なので、あらかじめAV1でエンコードしたテスト映像を作っておくことにします。

```
ffmpeg -f lavfi -i testsrc=s=320x240:r=30 -pix_fmt yuv420p -g 30 -strict -2 -vcodec av1 -r 30 -f mp4 sample_av1.mp4
```

適当なところで `q` を入力してffmepgを止めます。

320x240の動画のエンコードの速度は1fpsくらいでした。遅い。。。！

必ず `-g` オプションでGOP(キーフレームの間隔)を指定してください。
このオプションが無いとキーフレームは最初の1回しか作られませんでした。
キーフレームが定期的に来ないと、ライブストリーミングで途中から見始めたときにデコード開始のきっかけがなくて再生できないです。
(これでけっこうハマりました。)

# HTTPサーバとブラウザの準備

なにかしらHTTPサーバを動かして、カレントディレクトリのファイルをHTTPで取り出せるようにします。
今回は簡単にpython3のライブラリを使いました。

```
python3 -m http.server
```

AV1を有効にしたFirefoxで以下のURLを開くとHLSでストリーミングされている動画を再生することができます。

`http://localhost:8000/out.m3u8`

FirefoxでAV1のCODECを有効にする方法は以下を見て下さい。
[YouTubeのAV1のテスト配信を試す](https://qiita.com/tetsu_koba/items/809a4304d9cf5677bfa4)

また、HLSを直接再生するためにFirefoxには以下のプラグインをインストールしています。
`Native MPEG-Dash + HLS Playback` Add-on 

# HLSでストリーミング

```
ffmpeg -re -r 30 -i sample_av1.mp4 -c copy -hls_segment_type fmp4 -hls_time 3 -hls_list_size 0 out.m3u8
```

HLSのメディアファイルとしてmpegtsでなくてfragmented MP4 を指定することがポイントです。
なお、`-re -r 30` のオプションをつけると入力ファイルからの読み込み速度が30fpsになるように調整してくれるので、ライブストリーミングをシミュレーションすることができます。



生成されたファイルは以下の通り。

```
$ ls -lt
total 960
-rw-r--r-- 1 koba koba  28000 11月 28 18:11 out10.m4s
-rw-r--r-- 1 koba koba    412 11月 28 18:11 out.m3u8
-rw-r--r-- 1 koba koba  54434 11月 28 18:11 out9.m4s
-rw-r--r-- 1 koba koba  54394 11月 28 18:11 out8.m4s
-rw-r--r-- 1 koba koba  54482 11月 28 18:11 out7.m4s
-rw-r--r-- 1 koba koba  54574 11月 28 18:11 out6.m4s
-rw-r--r-- 1 koba koba  50820 11月 28 18:11 out5.m4s
-rw-r--r-- 1 koba koba  47426 11月 28 18:11 out4.m4s
-rw-r--r-- 1 koba koba  43185 11月 28 18:11 out3.m4s
-rw-r--r-- 1 koba koba  36299 11月 28 18:11 out2.m4s
-rw-r--r-- 1 koba koba  30249 11月 28 18:11 out1.m4s
-rw-r--r-- 1 koba koba    792 11月 28 18:11 init.mp4
-rw-r--r-- 1 koba koba  22206 11月 28 18:11 out0.m4s
-rw-r--r-- 1 koba koba 471205 11月 28 18:10 sample_av1.mp4
```

m3u8のファイルは以下の通り。

```
$ cat out.m3u8 
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-MAP:URI="init.mp4"
#EXTINF:3.000000,
out0.m4s
#EXTINF:3.000000,
out1.m4s
#EXTINF:3.000000,
out2.m4s
#EXTINF:3.000000,
out3.m4s
#EXTINF:3.000000,
out4.m4s
#EXTINF:3.000000,
out5.m4s
#EXTINF:3.000000,
out6.m4s
#EXTINF:3.000000,
out7.m4s
#EXTINF:3.000000,
out8.m4s
#EXTINF:3.000000,
out9.m4s
#EXTINF:1.500000,
out10.m4s
#EXT-X-ENDLIST
```

#　まとめ
かなり無理矢理ですが、AV1のCODECでHLSストリーミングして、それをブラウザで再生することができました。
AV1でライブストリーミングするための残りのピースは高速なエンコーダですね。

# 参考

[Streaming conf #5](https://streaming-lab.connpass.com/event/110380/)でAV1の話をしました。

「新しいビデオコーデックの AV1 って今どうなってる？」
https://www.slideshare.net/tetsu.koba/new-video-codec-av1