OmniAICreator/Qiita-1.07M huggingface dataset:
【Spark DataFrame】あるカラムを横持ちから縦持ちにする（Scala）

##はじめに
SparkDfで、DFのあるカラムついて横持ちから縦持ちにする場合、ちょっと手間取ったので備忘録をかねて記録しておきます。
一応、ソースコードはScalaですがPythonとかでもそんなに変わらないはずです。


##データフレーム操作

###テストDataFrameの作成

```scala:testDf.scala
val testDf = Seq(
    (1,"a",30,20),
    (2,"a",40,30),
    (3,"c",50,40),
    (4,"d",60,50),
    (2,"d",40,70),
    (2,"d",20,10),
    (1,"a",60,90)
   )
   .toDF("Column1", "Column2", "Column3","Column4")

/*
+-------+-------+-------+-------+
|Column1|Column2|Column3|Column4|
+-------+-------+-------+-------+
|      1|      a|     30|     20|
|      2|      a|     40|     30|
|      3|      c|     50|     40|
|      4|      d|     60|     50|
|      2|      d|     40|     70|
|      2|      d|     20|     10|
|      1|      a|     60|     90|
+-------+-------+-------+-------+
*/


```


##縦持ち　→　横持ち
縦持ち→横持ちは簡単で、pivot使えば簡単にできます。
使う際は「df.groupby().pivot().count()」のように３つのメソッドを用いる必要があります。

```scala:pivot.scala
val test2Df = testDf
.groupBy("Column1") 
.pivot("Column2") //横持ちにしたいカラム
.count() //集計関数
.na.fill(0)　//ゼロ埋め

/*
+-------+---+---+---+
|Column1|  a|  c|  d|
+-------+---+---+---+
|      1|  2|  0|  0|
|      3|  0|  1|  0|
|      4|  0|  0|  1|
|      2|  1|  0|  2|
+-------+---+---+---+
*/

```


##横持ち → 縦持ち
本題で、test2Dfがあったとして、何かの事情で縦持ちにしたい場合。
横持ち　→　縦持ち　はpivotのように一発でできる機能はなくて、ちょっと一手間？加えます。
使うものは、「selectExpr()」と「stack」です。
ソースコードは以下の通り。

```scala:unpivot.scala
val test3Df = test2Df
    .selectExpr(
        "Column1",//縦持ちの基準となる対象列
        "stack(3,'a',a,'c',c,'d',d)".//縦持ちとなる元
    )
/*
+-------+----+----+
|Column1|col0|col1|
+-------+----+----+
|      1|   a|   2|
|      1|   c|   0|
|      1|   d|   0|
|      3|   a|   0|
|      3|   c|   1|
|      3|   d|   0|
|      4|   a|   0|
|      4|   c|   0|
|      4|   d|   1|
|      2|   a|   1|
|      2|   c|   0|
|      2|   d|   2|
+-------+----+----+
*/
```
という風に変形できます。最初に、縦持ちの基準となる対象列を指定してあげて、次にstack()で、縦持ちとなる元を指定します。上の場合だと、Column1に対して、a,c,dを縦持ち化しています。stack()の３は縦持ちの個数（この場合a,c,dの3つ）、"a" "c" "d"は名前、a c dは、元のその列名の数値を参照しています（単純に"name1"とか文字列を入れるみたいなこともできます）。また、列名がDefaultではcol0とかになってますので、変更したい場合は、**.withColumnRenamed("col0","〇〇")**とかを使うと変更できます。

あとは、joinをうまく使ってやると、縦持ちから横持ちへの変更ができるんじゃないかなと思います。

##参考リンク
[How to Pivot and Unpivot a Spark DataFrame](https://sparkbyexamples.com/spark/how-to-pivot-table-and-unpivot-a-spark-dataframe/)
[Rust] IntegerとFloatの両方でmaxが必要なとき

#TL;DR

i32にもf32にもmaxを使うような関数max<T>(a:T, b:T)を作るときは，ジェネリック境界にPartialOrdを使いましょう．

# max2dをジェネリックにしたい

2次元の行列からmaxを計算する関数があったとしましょう．

```rust
use ndarray::*;
use std::f32;

pub fn max2d_f32(input: &Array2<f32>) -> Array1<f32> 
{
    input.fold_axis(Axis(1), f32::MIN, |m, i| (*m).max(*i))
}
```

これをi32でも動くようにしたい．

```rust
pub fn max2d_i32(input: &Array2<i32>) -> Array1<i32> 
{
    input.fold_axis(Axis(1), i32::MIN, |m, i| (*m).max(*i))
}
```

これだと，使うとき分岐しなきゃいけないし，f64にも対応したいってときに関数が増えていく．

そこで，ジェネリックにするために

```rust
use ndarray::*;
use num_traits::bounds::Bounded;

fn max<T:PartialOrd>(a:T, b:T)-> T { if a > b {a} else{b}}
pub fn max2d<T>(input: &Array2<T>) -> Array1<T>
where T: Clone + Copy + Bounded + PartialOrd
{
    input.fold_axis(Axis(1), T::min_value(), |m, i| max(*m, *i))
}
```
と書けばOK.

maxはTがOrdという前提なんだけど，f32はNanがあるためにPartialOrd，という点に注意して書けば，
大丈夫！

```rust:動作確認用
use ndarray::*;
use ndarray_rand::RandomExt;
use ndarray_rand::rand_distr::Uniform;
use std::f32;
use std::i32;
use num_traits::bounds::Bounded;

pub fn max2d_f32(input: &Array2<f32>) -> Array1<f32> 
{
    input.fold_axis(Axis(1), f32::MIN, |m, i| (*m).max(*i))
}

pub fn max2d_i32(input: &Array2<i32>) -> Array1<i32> 
{
    input.fold_axis(Axis(1), i32::MIN, |m, i| (*m).max(*i))
}

fn max<T:PartialOrd>(a:T, b:T)-> T { if a > b {a} else{b}}
pub fn max2d<T>(input: &Array2<T>) -> Array1<T>
where T: Clone + Copy + Bounded + PartialOrd
{
    input.fold_axis(Axis(1), T::min_value(), |m, i| max(*m, *i))
}

fn main() {
    let a = Array::random((10000, 1000), Uniform::new(0., 10.));
    let b = Array::random((10000, 1000), Uniform::new(0, 100));

    println!("{:8.4}", max2d_f32(&a));
    println!("{:8.4}", max2d(&a));
    println!("{:8.4}", max2d_i32(&b));
    println!("{:8.4}", max2d(&b));

}

```

必要なジェネリック境界が減ると，もっとスッキリするんだけど．
AWS re:Invent 2019でワークショップに参加してきた


本記事は、[サムザップ Advent Calendar 2019 #1](https://qiita.com/advent-calendar/2019/sumzap1) の12/5の記事です。

## はじめに

2019/12/1〜12/6の間、ラスベガスで開催されていたAWS re:Invent2019に参加してきました。
この記事では参加したセッションの中から、クラウド環境でのオペレーションの自動化に関するワークショップ「Intelligently automating cloud operations」の紹介をします。ワークショップというのは一般的な講義形式のセッションとは異なり、自分で手を動かして学習するセッションのことです。



## ワークショップについて

+ 各テーブル6〜7名、x 20テーブルくらい
+ ワークショップ全体の時間は2〜3時間

+  用意された復数の課題を様々なAWSサービスを使って解決していく
 + 各課題に取り組む前に10分程度の講義がある
 + 講義後に共有されるドキュメントに従って作業を進めていく
+ ワークショップで使うAWSアカウントは運営側で用意してくれた
+ 作業用のノートPCは各自持ち込み

+ 作業中の不明点は会場にいる運営メンバーか、同じテーブルの人に相談する
+ お互いの詰まったところを助け合う以外に複数人での作業はなく、ほぼ個人作業

参加するワークショップによっては自分でAWSアカウントを用意する必要があったり、
個人ではなくチームで課題に取り組むなどの違いがあるようです。



## ワークショップでやったこと

用意された以下３つの課題を進めました。



### AWS HEALTH ABUSE EVENTS

管理しているEC2インスタンスがDoS攻撃を受けているシーンを想定。
AWS Personal Health Dashboardに届いたインシデント報告をCloudWatch Eventsで検出し、Lambdaを使ってSNS経由でSMSを送信しつつ、攻撃を受けているEC2インスタンスの停止アクションの自動実行を実装しました。

### Automatic Region Failover 

マルチリージョン(us-east-1,2)で動いているシステム。各リージョンにNLBがあり、Webサーバが復数台ぶらさっている構成で片方のリージョン(us-east-1)のトラフィックパターンに異常が発生したシーンを想定。

クロスリージョンフェイルオーバーのオペレーション自動化はこんな感じの流れでした。

+ CloudWatchのCanaryResponseTimeメトリックでAnomaly Detectionを有効にする
  - CloudWatchアラームを作成し、事前に作成済のSNSをターゲットに指定する
+ lambdaにクロスリージョンフェイルオーバーさせる関数を追記する
+ 意図的にトラフィック異常を発生させるためのSSMドキュメントを作成する
  - tcコマンドでレイテンシ遅延を発生させる処理が書かれていた
+ SSMのRunコマンドでus-east-1のインスタンスにレイテンシ遅延を発生させる
  + しばらく経過するとCloudWatchがアラーム状態に変化する
+ Lambdaが作動して、トラフィックがus-east-1からus-east-2に切り替わる
  
  

このシナリオではNLB + Webサーバのシンプルな構成だったけど、データストアを含む構成だと実環境に近くてもっと面白そうです。あとは地味にtcコマンドが便利なので社内の障害試験で今後使っていきたいです。



### AUTOMATED EBS USAGE FORECAST AND LIMIT INCREASES

AWSアカウント内にあるすべてのEBSボリューム数を自動集計し、AmazonForecastでデータセットを作って、今後の使用量を予測する。更にEBSの制限緩和も自動化する。みたいなシナリオで面白そうだったんですが、ここまでの2つの課題で結構時間を使ってしまって最後まで進められませんでした...



## 最後に

DoS攻撃を受けたときやリージョン障害が起きたときに限らず、何か問題が発生したときに行う作業が決まっているのであれば、オペミス防止や復旧時間を短縮するためにも自動化すべきだと思いました。サムザップでも今後更に自動化を進めていきます。

明日は @leosuke さんの記事です。
SI歴13年のおっさんがG’s ACADEMYに通ってみた

#はじめに

G's ACADEMY Advent Calendar 5日目を担当します！

G's ACADEMY FUKUOKA Dev3期を今年の10月に卒業しました。

結論からいうと最高でした。

2020年からは、13年勤務した今の職場を退職し、心機一転、フリーランスになる予定です。

現在引き継ぎ作業中…

ここでは4つ書いていこうと思います。

・SIerとして何をしてたのか
・なぜジーズアカデミーに行くようになったのか
・ジーズアカデミーでの体験　
・これから

#SIerとして何をしていたのか

メインは、業務ERPパッケージ（○○奉行とか☓☓大臣とか）の導入や顧客に合わせたカスタマイズを行っていました。

プログラムは主に以下の言語と製品を利用していました。
・C#.net、VB.netを使ったWindowsアプリケーション
・Excel、AccessのVBAを使ったアプリケーション
・DBはSQLServer、Oracle

これらを使って、主に利用者が入力する画面・出力する帳票やデータなどを作って、パッケージをより使いやすいものにカスタマイズしていました。

業務知識としては、
・販売管理
・人事・給与・就業管理
・会計ちょこっと

なにかこのあたりでお困りごとあれば、ご連絡くださいw

#なぜジーズアカデミーに行くようになったか

入社して10数年したあるとき、ずっと胸の内に**理由のわからない不安感・もやもや感**を感じていました。

振り返ってみると

- 入社を決めた動機は、**職場が福岡でなんとなくかっこよさそうなIT企業**だった。
- 入社後はトラブル続きだったが、必要に迫られて**強制スキルアップ**
- この時代で学んだ事は**他人は素直に信じない。**必ず自分の目で確かめる・納得する（闇）
- 中堅になってきたあたりは、仕事に慣れ、うまく立ち回る事に楽さを感じ、ぬくぬく。給与も上がっていきぬくぬく。
- マネージャーになってきた今は、自身のスキルアップは年々止まり、あまり興味がない管理業務に携わる。**つらみが増していく**
- **体重が毎年3Kg増え続ける**

自分がやりたい事をまったく考えてなく、**会社にいて、わいてくる状況に流される生活**だったように思います。

理由のわからない不安・もやもやは、日々の生活が**直感的に自分がおらず、充実できていないと感じている**からだとわかりました。

**何かを変えたい**

その思いで、Google先生に色々聞いてみて、ジーズアカデミー福岡にたどり着きました。

**世界を変えるGEEKになる**

このワードが刺さりました。
物理的に世界の様を変える事だと思いますが、**自分の状況を変える事で周りの見え方が変わってくる**
そういう意味で、**世界を変える事ができるのでは？**とも思いました。

自分のやりたい事を見つける
さらにWeb開発の知識も身につけられる
これまでの知見とも連携できる

めちゃめちゃ今の自分にマッチしてるなぁと思い、行く事を決断しました。

# ジーズアカデミーでの体験

### 授業・課題フェーズ
タロー先生の授業は、分かりやすく、新鮮な事ばかりで楽しく学ぶ事ができました。
準備もしっかりなされていて、本当にすごいと思います。。。

FirebaseやLaravelといった最新のプラットフォームやフレームワークを知ったことは、Web開発未経験者の自分でも、Web開発の敷居を下げてくれて、もっと色々知りたく、挑戦したくなりましたなりました。

### 卒業制作フェーズ

**一番苦しみました…**

新しい何かを作る。0から1を生み出す。
企画を作成する際のアドバイスとして

- なぜ**それをやるのが自分**なのか(WhyMe)が大事
- 自分のこれまでの課題感や、自分が長く続けてる事、やってて楽しかった事。そこから**オリジナリティ**が生まれる
- ただの流行りやなんとなく儲かりそうだからでは、投資家や周りを納得させる事ができない。何か周りから言われたときに心が折れて継続できない。

もともとこのWhymeが薄い人生だったので、永遠の自分探しの旅でしたが、整理すると

- 最新のテクノロジーを使って行きたい
- 多様性を受け入れようとする社会に貢献したい

この２つをミックスして「愛の証明アプリ　ラブエビデンス通称らぶえび」という企画になりました。携帯の家族割、パートナー割のようなサービスや保険のサービス、相続など受ける際に、二人の関係を不正なく証明できるようなものを想定しました。

実際に制作したのは、チーム制作で別の方の案のものを制作する事となったのですが、この0から1を生み出すという体験は、なかなか味わえないものでした。


#最後に
SIerでプログラムをしている人でも、十分に払うお金以上の価値がある場所でした。
結果、ジーズアカデミーで得たものは

- Web開発スキル
- アイデアをまとめるスキル
- 新しい仲間
- 次へ踏み出す勇気

新しい仲間・スキル・勇気をもらって、自分は独立し、フリーランスになることを決めました。
自分のやりたいことをするため、より多くのスキルを身に着け、自分にも、仲間にも貢献できる人になっていきたいと強く思います。

**何かを変えたいと思っている人！説明会にぜひ来てください！**

2020年1月からは、G's Baseに入居しますので、同期のみなさん、G'sに来たら連絡ください！

以上、ダラダラと書いてしまいましたが
来年もよろしくおねがいします。
4システムを並行開発した話

## はじめに

約1年半かけて大規模な4システムを並行開発しました。役割としてはチームリーダーでしたが、その時にやったことを簡単にまとめます。
※並行開発といっても、スケジュール感の実態は各システムの開発開始と終了が被っている形です。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23101/5427dbf3-e273-d21d-6d4b-0e3afb805389.png)


## 概要

* アーキテクチャ：4システムとも同じアーキテクチャ
* チームの人数：10人～15人
* 開発フェーズ：設計～実装～UT（要件定義までは完了している）
* メンバー特性：要件定義を行ったメンバーは1～2人
* その他：システム毎に担当するメンバーを割り振る（多くても1人2システム担当） 

## やったこと

#### チームの中で必要となる役割を明確にしておく

10人～15人に対してリーダー1人の体制はとてもじゃないですが回りません。

そのため、各システム毎にサブリーダー的な役割を定義し裁量を渡し、判断に困った時だけ相談してもらう、という形をとることでリーダーがいなくても自然と回るような形を目指しました。

これは割とうまく回ったと思っていて、自分がいなければ何も進まない、ということはあまりありませんでした。

#### まずは1システム作りきり振り返る

ベースとなるアーキテクチャは決まっていたので、それを実装に落とし込んでとりあえず1システム作りきることを目指しました。
ここではかなり紆余曲折がありましたが、最初なのでそれは仕方ないことだったのかなと思います。
大事なのは1システム作りきったあとは、振り返りをしっかり行いその時の反省点をもとに次に作るシステムがより円滑に進むよう改善することです。

#### 次に作るシステムはファストトラッキングして実装がよりスムーズに進む段取りを

要件定義が終わっていても、設計、実装段階で要件、仕様を確認するケースは多々あります。最初の3システムは、実装中に要件、仕様を確認しなければならないケースが多々あり、そのための確認コスト（コミュニケーションコスト）やそれに付随する仕様変更対応でかなりのコストがかかりました。
そのため、4システム目はファストトラッキングで、要件定義ドキュメントを読み込み、要件、仕様の曖昧な所を洗い出し、実装の本格着手が始まる前に全て整理しておくことを実践しました。そのおかげで、他の3システムに比べて設計、実装段階でのコミュニケーションコストが圧倒的に下がり、開発終了までのスピードがかなり早かったと思います（そもそも4システム目で初めてその改善をすること自体が遅いのですが）。
他には、設定ファイルやメッセージ系のファイル、共通部品系はポーティングして使い回しが可能だったため、そのあたりの整備を最初に行うことで、後から合流してくるメンバーは自身の担当機能のみに注力できるような形を作る、といったことを実践しました。

#### 3か月ごとの要員計画

実装と並行して、（完成している機能から順に）結合テストもテストチームによって進められていました（フェーズが重なっていることはいったん置いておきます）。こうした背景から、実装以外に障害対応担当者の割り振り、障害対応といったタスクも発生しました。また、担当しているシステムの実装が終わったら次のシステムの実装を、、、といった具合に、メンバーが流動的に動けるような体制を取る必要がありました（そうしないとカオスな状況になるためです）。そのため、3か月ごとに要員計画を行い、誰がどのタイミングで何をやっているかを、明確にし共有することでチーム内のカオスな状況を防ぐことを目指しました。

## さいごに

こういったシステム開発のリーダー経験はほとんどありませんでしたが、個人的には以下が結構重要なのでは？と感じることができました。

1. 流動性が求められるものほど流動性をより高めるために、やらなければならないこと、担当者、対応期間、を整理し常に問題があった時の対応方法を考えておく
2. ファストトラッキングで何をやるかでその後の良し悪しが変わってくるので、タスクは慎重に検討する

以上です。
grubでkernel起動

#概要

grubが入ったフロッピーイメージでkernelの様なものを起動する。
grubは、レガシーを使用。

#開発のイメージ

①コンパイルする。
②フロッピーイメージにコピーする。
③qemuで実行する。

#cygwinは、elfを吐かない。

grubは、elf形式のkernelが必要。
cygwinの標準のgccでは、elf形式は、扱わない。使えない。

#開発環境

debian 6.0.7 sqeeze
grub 0.96

#multiboot_headerが必要。

```
.text
.globl _start
.code16
_start:
	movb 	$0x41, %al
	movb 	$0x0e, %ah
	int 	$0x10
	jmp		.

```

上記を以下でコンパイル。

```
$ gcc -c -nostdlib hello.S
$ ld hello.o
```

以下で、フロッピーイメージにコピー。

```
$ mount -t vfat -o loop fd.img /mnt
$ cp a.out /mnt/boot/nano.elf
$ umount /mnt
```
qemuで実行。

```
$ qemu -fda fd.img
```

以下で、grubでブート。

```
$ root (fd0)
$ kernel /boot/nano.elf
$ boot
```

以下のエラーが出る。
error 13 invalid or unsaported excutable format

![Screenshot-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/66fb4c36-5476-0944-3593-74e167e927cd.png)

multiboot_headerのエントリーポイントが必要。

#bios callは、無理。

grubは、kernelを、プロテクトモードに持っていくようで、biosが呼べない。

#kernalの様なもの。

```
.text
.globl _start
.globl multiboot_header
.code32

_start:
	movl 	$0xb8000, %ebx
	movl 	$12, %ecx
	movl 	$msg, %esi
loop:
	movb 	(%esi), %al
	movb 	$2, %ah
	movw 	%ax, (%ebx)
	addl 	$2, %ebx
	addl 	$1, %esi
	subl 	$1, %ecx
	jne 	loop
	jmp 	.

.data
multiboot_header:
	.align 4
	.long	0x1badb002
	.long	0x0	
	.long	-0x1badb002-0x0
msg:
	.ascii "Hello world!"
```

以下のMakefileで、コンパイル。

```
all: test4.elf
test4.elf: hello.o
	ld -e_start -Ttext=0x100000 -otest4.elf hello.o
hello.o: hello.S
	gcc -c -nostdlib hello.S
```

#結果

ビデオラムにhello worldを吐くだけ。

![Screenshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/ace1e469-52c0-1d91-a4a8-d6cc00d8347a.png)


以上。
なぜgossを使うのか？

本投稿は [goss advent calender 2019](https://qiita.com/advent-calendar/2019/goss)参加記事その1です。

gossとは
-------------
gossとは、公式サイトにQuick and Easy server validationとあるように
サーバの検証(テスト)をすばやく簡単に行うためのツールです。
https://github.com/aelsabbahy/goss

ツールのセットアップ方法や使い方などの方法論へ入る前に
なぜこのgossというツールを使うのか、その考え方を書いておきたいと思います。

サーバのレイヤでもテストは必要
-------------------------
いわゆるサーバエンジニアやインフラ系、基盤系と呼ばれる区分で筆者も仕事をしていますが
アプリケーション開発担当に比べ、テストに対する考え方が薄いと感じることがあります。

何らか構築をしたり変更を行った場合にもちろんテストを実施するのですが、その方法は手作業であることが多いです。
(あくまで筆者の主観です。もちろん何らか自動化されていらっしゃるチームもあるかと思います)

またインフラテスト仕様として纏めたりもしますが、一度実施したテストを再度行うことはあまりなく、変更された箇所を確認するものに留まり、リグレッションテストを行うケースは稀です。

テスト項目を再構成させるのは手間
-----------------------
運用に入ると何らかの形で正常性確認や監視を行い、インフラテスト仕様として纏めたものから重要度の高い物を抜粋して実施や設定を行うことが多いです。

ここで「間引く」ことを改めて考えるより、インフラテスト仕様をそのままツールで再実行できればどこに異常が発生したのかを特定しやすくなるため、gossを使ってテスト項目を作りテストを何度でも実行(それも短時間で)する意義があります。

またさらに発展した使い方として、作成したテスト項目をある程度の単位で分けておくことで、別のシステムで再度利用することも可能になったり、
スケジュール実行や監視トリガーによるイベント実行を行ったりもできます。

類似ツールとの違い
----------------------
ここまではgoss以前よりあるserverspecと同様になります。
https://serverspec.org/

テストに対する考え方はここまでserverspecと同様とですが、敢えてgossを選ぶ唯一の大きな理由はgossにはサブコマンドとしてadd/autoaddがあり、gossを動かしているサーバの状態からテスト項目を生成することが可能になっています。

一から全てテスト項目を記載しなければならないserverspecと比較して、テスト項目作成は大幅に楽です。

半面欠点としてserverspecと異なりテストできるOSがLinuxに限定されます。(2019/12月現在)

またgoss自身にはserverspecのようにリモートのサーバをテストすることはできません。
これはsshであったりansibleのような別のツールを組み合わせて対応する必要があります。

さいごに
---------------------
筆者としてはこのツールはもっと利用されて欲しいと思いますし、そのためにはもっと情報が広がって欲しいと思っています。
そしてよりこのgossというツールが開発され、良いツールになればと考えています。

色々な使い方やもっとこうした方が良くなるという情報が集まりgoss開発者の方々へフィードバックできれば良いなあと考えています。
私が最近勉強して知った言葉まとめてみた3

## はじめに
最近学んだ言葉を自分なりに、まとめてみたいと思います。わかりやすく伝えられるように、頑張りますので、良かったら読んでみてください。

## Capybara
UIのテストを行うためのライブラリ。gemでインストールし、テスト内容をspec/featuresに記載する。テストを実行すると、自動でブラウザを開いてそのブラウザ上で、テストコードの通りに動いてくれます。

## Selenium
Webアプリケーションのテストを自動化するために使われているツール群。RSpec+CapybaraのテストをSeleniumとChromeのヘッドレスブラウザを使って実行してました！ユーザーの動きを、再現してくれます。

## ヘッドレスブラウザ
GUI(画面)で操作するのではなく、CUI(文字)で操作できるブラウザ。CUIで操作が出来るので、プログラム、スクレイピングなどに利用されることが多いです。

##Ajax
Webブラウザ内で、非同期通信を行いながらインターフェースの構築を行うプログラミング手法。
画面を遷移せずに、HTMLを更新することができるので、サーバ負荷の減少、ユーザビリティの向上にもつながる。1部の情報のみをリクエストするので、それ以外の部分は変わることがない。
[引用サイト](https://qiita.com/zakiyamaaaaa/items/bdda422db2ccbaea60d9)

## さいごに
調べて、わかりやすくまとめることも1つのアウトプットだと私は思います。自分の言葉を人がどう感じ取るかを考えると、必然的に言葉を噛み砕いてより分かりやすくしようと努力するので、自分の中の理解もより深まります。1番やりやすいアウトプット法だと思うのでおすすめします！！
Binder環境でxvfbを使う

# Binderでxvfbが簡単に使えることに気がついた

昨日は「ヘッドありアプリを含むワークフローでxvfbを使うのがむずい」と[xvfbをディスる記事](https://qiita.com/kozo2/items/3d076998e8f3710d01ab)を書いておいて速攻で手のひら返しになります。
[Binder](https://mybinder.org/)という

- (GitHubなり何なりの)ノートブックリポジトリ(≒ワークフローリポジトリ)を
- 無償で誰もが利用可能なR, Python, Julia のお試しノートブックサーバ(JupyterだけでなくRStudioも利用可能)として公開する

サービスがあります。
(最近はshiny, bokehのダッシュボードアプリのお試し公開用の場としても有用そうです。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7591/bf842bf6-b04a-3ddc-7879-87e9a13701df.png)

Figure credit: [Juliette Taka, Logilab and the OpenDreamKit project](https://opendreamkit.org/2017/11/02/use-case-publishing-reproducible-notebooks/)

ひさしぶりにBinderをいじってみると

- 簡単に設定ファイル書けるやん(Dockerfile要らんやん)
- Rも3.6が使えるやん(ちょい前は使えなかった，BioconductorはRのVersionが新しくないと使えない)
- xvfbもインタラクティブにシェルから実行できるやん
- CIでxvfbを使おうとするのに比べたら全然楽やん

となったのでBinder環境でxvfbを使う方法を紹介します。

# X410+Dockerに対するRCy3のAuthorの反応

昨日の[X410+Docker記事](https://qiita.com/kozo2/items/3d076998e8f3710d01ab)のRCy3の使い方を「どうすかね？」とRCy3のAuthorに聞いてみると

「うーん，何か違うんだよねえ。何つうの? "リンクを踏むだけで使える"?，"学生教育用"? 的な環境をイメージしてるんだけど...」

という反応で

「まあそうだわな」

となり，やはりBinderだわな，となってBinderでxvfbを使おうとするに至った次第です。
(そしてそれをアドベントカレンダーとして使いまわすに至った次第です。)

# Binderでxvfbを使うには

まず当然ですがxvfbのDebianパッケージをaptでインストールする必要があります。

最近のBinderではほとんどの場合でDockerfileを書く必要が無く，設定するのが非常に簡単になっています。
下記のような環境構築用設定ファイルを**リポジトリのトップ**か**リポジトリのトップ/binder**フォルダー
下に配置し，https://mybinder.org/ でリポジトリに対するビルドをかければ利用可能になる，という具合です。

- apt.txt : aptでinstallするパッケージ名を列挙します。
- environment.yml : condaでinstallするパッケージ名をYAMLで書きます。
- requirements.txt : pipでinstallするパッケージ名を列挙します。
- install.R : RパッケージをinstallするRスクリプトを書きます。
- runtime.txt : 使用するRのバージョン情報などを書きます。
- postBuild : Binder環境のビルド後に実行するシェルスクリプト

他にも設定ファイルはありますが，ほとんどの場合 上記で事足りると思います。

xvfbをインストールしたいのでapt.txtは下記になります。
ここではCytoscapeをxvfbといっしょに使うためにopenjdk-8-jdkもインストールします。

```
xvfb
openjdk-8-jdk
```

その他のファイルも加えると[このリンク](https://github.com/nrnb/gsod2019_kozo_nishida/tree/master/binder) のような感じになります。

すると [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/nrnb/gsod2019_kozo_nishida/master?urlpath=rstudio) のようなRStudio環境の利用が可能になります。(RStudioの画面に到達する[インスタンスが立ち上がる]までは結構時間がかかります)

RStudioに行き着いたら**Terminal**タブで下記のコマンドを実行します。

```
xvfb-run bash ~/cytoscape-unix-3.7.2/cytoscape.sh
```

これでCytoscapeがバックグラウンドで立ち上がります。
Cytoscapeの画面を見ることはできませんがCytoscapeはREST APIを持っているのでRからワークフローを実行しスクリーンショットを保存することができます。
簡単な例として，RStudioの**Console**タブで下記のようなワークフローを試してもらえます。

- [STRING](https://string-db.org/)というデータベース用の追加機能(App)をインストールし
- タンパク質名で相互作用ネットワークを取得し
- 画像を保存

するって感じです。

```{r}
installApp('stringApp')
string.cmd = 'string protein query query="SORCS2" species="Homo sapiens"'
commandsRun(string.cmd)
exportImage('string','PDF')
```

# まとめると

- Binder簡単，有用，すばらしい
- Binderでxvfb簡単に使えました(使うの難しい，は思いこみだったかも)
- こんな感じでヘッドレス版が無いアプリを含むワークフローをBinder環境で実行できそう

という感じです。

以上。
font suitcase （mac で使われる拡張子がないフォント）を .ttf に変換する件


今どきフォントといえば OpenType か TureType みたいなところがありますが、（特に MS Office をインストールしてついてくるフォント群には）拡張子のないフォントが見つかります。

![スクリーンショット 2019-12-05 6.17.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221785/0037ce01-7ef6-a385-ead1-b2bc30b8da2d.png)

で、拡張子のないフォントなんですが、このままだと利用できないアプリケーションがそれなりの数あります（そもそもこの記事を書こうとした理由が、LuaLaTeX で fontspec を利用した時に、Harrington が使えなかったからです）。それでは困るので、.ttf なファイルに変換しよう、と言うお話です。

# やるべきこと

[DfontSplitter](https://peter.upfold.org.uk/projects/dfontsplitter) 入手して、起動します。

![スクリーンショット 2019-12-05 6.26.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221785/2802b102-1620-6f79-0afb-62101043b878.png)

上の枠に変換したいフォントを D&D し、Destination Folder に出力するディレクトリを指定して、Convert をクリックするだけです。

[この記事](https://qiita.com/thorikawa/items/f45b376bd41242869f9f)によれば、昔は dfont というファイルを抽出しなければならなかったようですが、今ではそのようなことをしなくても大丈夫です。

# そもそも font suitcase って何

私もよく知りません、が、実体はディレクトリであることは確かなようです。が、`cd` はできないようです。`cat <font suitcase>/..namedfork/rsrc` のように、`cat` で情報を抽出することはできるっぽいです。

何か分かり次第追記していきたいんですが、いかんせん検索しても情報が得にくい……。
[Vue.js] dataオブジェクトの返し方

# 概要

```javascript:()&nbsp;=>&nbsp;({})
Vue.component('button-counter', {
  data: () => ({
    count: 0
  }),
...
```

ネットでVueについて調べていた時のこと。
上記のような書き方を見つけて、？？？となった。

# いまはむかし

```javascript:es6以前
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  methods: {
    hoge: function () {
...
```

今となってはこう書くことはないが、勉強し始めのころは、functionを使って書いていた。

```javascript:es6以後
Vue.component('button-counter', {
  data: () => {
    return {
      count: 0
    }
  },
  methods: {
    hoge() {
...
```

es6を覚えてからは、functionをアロー関数で書くようになり、これでしばらく満足していた。

# なにそれ


```javascript:?
Vue.component('button-counter', {
  data: () => ({
    count: 0
  }),
  methods: {
    hoge() {
```

なんだこの波かっこを丸かっこで囲む書き方は...
returnはどこ行った...？
そんなことを思いつつ、MDNで調べてみた。
調べてみると、[アロー関数のページ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions)にお目当てのことが書いてあった。

>高度な構文
>// object リテラル式を返す場合は、本体を丸括弧 () で囲みます:
>params => ({foo: bar})

オブジェクトを返す場合には、丸かっこで囲ってあげれば、書いたまま返してくれるようだ

ほかのプロパティと同じ段にデータが記述されて、可読性も向上！
いい感じ！

# 感想
MDNは最強！
知っているつもりのことでもMDNを見直すことで、再発見があるかもと思い知らされた...
【Rails】bundle installをしたら大量のファイルを作る問題

# 初めに
bundle installをした際にgithubDesktopに新しいファイル生成10000ファイルのようにたくさんのファイルを生成したことはあるでしょうか。この問題に時間を使ってしまったので書き残します。

# 結論
bundle installをした際に大量にファイルが生成されるのは問題ではないです。正常な挙動です。本来、railsのgemのシステムは大量のファイルの上で成り立っておりそのファイルの数を普段見ることはないですがgitの管理下になっていることが問題と言えるでしょう。

# 原因
私は普段はgem fileにgemを追加した後に`bundle install`とターミナルに打つことでinstallをしているのですが、qiitaの記事を真似していて`bundle install --path vendor/bundle`と入力しました。この後に続く表記はbundle installしたファイルをどこに保存するのかということを指定しているもので本来は毎回このように打つことが正しいようです。ただしこのように打ったことでgithubの管理下に置かれてしまったらしく、省略して`bundle install`をしても大量のファイルがgithubDesktop上に表示されるようになりました。

#解決
`.bundle`と`vendor/`を一度ディレクトリから消すことで解決できます。
もう一度`bundle install`をしたら元の挙動に戻ると思います。
Javaで作る自動販売機のサンプル

#目的
Javaを使ってプログラミングの経験を積むこと

#対象者
Javaの基本を学んだ初学者

#達成目標
フローチャートと機能詳細を元にプログラミングができる。

#フローチャート
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/190809/e8be6c8c-a893-80d2-f230-147a33a3327e.png)

#機能詳細
・商品リスト初期化
商品は以下の3つ固定とする。
コーラ100円
オレンジジュース120円
水80円

・入金
1円単位で入金可能とする。
購入可能な最低金額が入金されるまで入金を促す。
（今回の場合、水の80円以上）

・商品選択
入金額の範囲で商品を表示する。
商品名で選択する。

・販売
選択した商品を提供する。

・課金
入金額から購入金額を引く。
釣銭を返す。


#サンプルコード
```
package vm;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		// 商品の初期化
		Map<String, Integer> items = new HashMap<String, Integer>();
		items.put("コーラ", 100);
		items.put("オレンジジュース", 120);
		items.put("水", 80);

		// 購入最低金額の場合、追加入金
		int deposit = 0;
		int minSaleAmount = -1;
		Scanner scanner = new Scanner(System.in);
		do {
			// 入金処理
			System.out.println("お金を入れて下さい。");
			deposit = deposit + scanner.nextInt();

			// 金額チェック（最低購入金額）
			int loopCount = 0;
			for (String itemKey: items.keySet()) {
				if(loopCount == 0 || minSaleAmount > items.get(itemKey)) {
					minSaleAmount = items.get(itemKey);
				}
				loopCount++;
			}
		} while(deposit < minSaleAmount);

		// 商品選択
		System.out.println("商品を選択してください。");
		Map<String, Integer> availablePurchases = new HashMap<String, Integer>();
		for (String itemKey: items.keySet()) {
			if(deposit >= items.get(itemKey)) {
			System.out.println(itemKey + ":" + items.get(itemKey) + "円");
			availablePurchases.put(itemKey, items.get(itemKey));
			}
		}

		// 販売
		String itemName;
		while(true) {
			itemName = scanner.next();
			if (availablePurchases.containsKey(itemName)){
				break;
			}
			System.out.println("商品名の指定が誤っています。商品名を指定し直してください。");
		}
		scanner.close();
		System.out.println(itemName + "です！");

		// 課金機能
		deposit = deposit - availablePurchases.get(itemName).intValue();
		System.out.println("おつりは、" + deposit + "円です。");


	}

}
```


クラス分けしたバージョンを作成しました。
https://qiita.com/TakumiKondo/items/9f222f44c973bb2eaa06
【Mac】 OSアップデート後、podコマンドでエラーが発生した際の対処法

###１．経緯・事象

SwiftUIを試してみたいと思い、macOSとXcodeをアップデートしたんですが、新規プロジェクトを作成して`pod init`したら以下のようなエラーが出て動かない！！

```terminal
$ pod init
-bash: /usr/local/bin/pod: /System/Library/Frameworks/Ruby.framework/Versions/2.3/usr/bin/ruby: bad interpreter: No such file or directory
```

このあたりのterminalでコマンド入力してあれやこれやと操作するところの知識が薄弱で、どうしたものかと思いましたが解決法が意外と早く見つかり、素直に解決してくれたので一応備忘録として残します。
（今後OSのアップデートの度に起きる可能性がありそうなので。）

###２．対応

cocoapodsを最新化(インストールし直し)する。

```terminal
$ sudo gem install -n /usr/local/bin cocoapods

...
...
...

29 gems installed
```

なんやかんやとインストールされて、正常に完了したようなので改めて`pod init`したら無事、フォルダにpodfileが作成されました。

###３．参考

こちらのエントリを参考にさせていただきました。

[ゆるtech。＞【CocoaPods】pod installしようとしたら「No such file or directory」エラーが発生した](https://shigu493.com/%E3%80%90cocoapods%E3%80%91pod-install%E3%81%97%E3%82%88%E3%81%86%E3%81%A8%E3%81%97%E3%81%9F%E3%82%89%E3%80%8Cno-such-file-or-directory%E3%80%8D%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E7%99%BA%E7%94%9F/)
AWS CognitoのGo SDKを使ってみた

AWS cognitoの[ドキュメント](https://docs.aws.amazon.com/sdk-for-go/api/service/cognitoidentityprovider/)に関する自分用メモです。
[サンプルコード](https://github.com/Khmer495/cognito_test_go_sdk)

*管理者権限を使用するにはAWS CLIによる認証情報の設定が必要です。詳しくは[こちら](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-configure.html)を参照してください。

## サインアップ
### [AdminCreateUser](https://docs.aws.amazon.com/sdk-for-go/api/service/cognitoidentityprovider/#CognitoIdentityProvider.AdminCreateUser)
管理者権限でユーザーを作成。
ユーザー名と、電話番号またはメールアドレスのどちらか一方を設定。
ユーザーはSMSやemailにより一時パスワードを与えられる。
ユーザーはFORCE_CHANGE_PASSWORD状態になり、初回signin時にパスワードを変更する必要がある。
DesiredDeliveryMediumsで一時パスワードをSMSとemailのどちら（または両方）に送信するか設定可能。
TemporaryPasswordで一時パスワードを指定可能。指定しなかった場合はcognitoが自動生成。


### [SignUp](https://docs.aws.amazon.com/sdk-for-go/api/service/cognitoidentityprovider/#CognitoIdentityProvider.SignUp)
通常権限でユーザーを作成。
ユーザー名、パスワード、電話番号またはメールアドレスのどちらか一方を設定。
ユーザーはUNCONFIRMED状態になり、有効化するために認証が必要。
認証リンク or コードがSMS or EMAILでcognitoから送信される。
認証方法はcognitoのユーザープール作成時の設定に依存する。

# 認証

### [AdminConfirmSignUp](https://docs.aws.amazon.com/sdk-for-go/api/service/cognitoidentityprovider/#CognitoIdentityProvider.AdminConfirmSignUp)
管理者権限により、リンクやコードによる認証を無視して認証を許可する。


### [ConfirmSignUp](https://docs.aws.amazon.com/sdk-for-go/api/service/cognitoidentityprovider/#CognitoIdentityProvider.ConfirmSignUp)
SignUp後、コードによる認証に使用する。

# サインイン
### [AdminInitiateAuth](https://docs.aws.amazon.com/sdk-for-go/api/service/cognitoidentityprovider/#CognitoIdentityProvider.AdminInitiateAuth)
一般的なサインイン。
AccessToken、ExpiresIn、IdToken、RefreshTokenが返ってくる。

### [InitiateAuth](https://docs.aws.amazon.com/sdk-for-go/api/service/cognitoidentityprovider/#CognitoIdentityProvider.InitiateAuth)
Adminの場合と同じ。

# 削除

### [AdminDeleteUser](https://docs.aws.amazon.com/sdk-for-go/api/service/cognitoidentityprovider/#CognitoIdentityProvider.AdminDeleteUser)
管理者権限によりユーザーを削除。

### [DeleteUser](https://docs.aws.amazon.com/sdk-for-go/api/service/cognitoidentityprovider/#CognitoIdentityProvider.DeleteUser)
通常権限でユーザーを削除。
サインイン時に取得できるAccess Tokenが必要。
ラズパイでpython3にopencvを入れたらエラーが出た【対処法】

opencvをインストールもアンインストールも上手く出来ず困っていたところなんとかインストールすることが出来たので，経緯を書きます。

# エラー状況

```
pi@raspberrypi:~ $ python3
Python 3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/pi/cv2/__init__.py", line 3, in <module>
    from .cv2 import *
ImportError: /home/pi/cv2/cv2.cpython-37m-arm-linux-gnueabihf.so: undefined symbol: __atomic_fetch_add_8
```

# opencvの入れ方
まず[Raspberry Pi + Python 3 に OpenCV 3 をなるべく簡単にインストールする](https://qiita.com/masaru/items/658b24b0806144cfeb1c)をもとに

```
$ sudo apt-get install libhdf5-dev libhdf5-serial-dev libhdf5-103
$ sudo apt-get install libqtgui4 libqtwebkit4 libqt4-test python3-pyqt5
$ sudo apt-get install libatlas-base-dev
$ sudo apt-get install libjasper-dev
```
によりopencvで用いるライブラリをインストールします。
ここで`sudo apt-get install libhdf5-dev libhdf5-serial-dev libhdf5-103`はバージョンにより変更があるようで
エラーが出る場合は`sudo apt-get install libhdf5-dev libhdf5-serial-dev libhdf5-***`の***を変えると突破できるかもしれません。




次に

```
sudo pip3 --default-timeout=1000 install opencv-python
```
によりopencvを入れます。

この状態で実行すると上記エラーが出てしまいました

# エラーの突破方法
[こちら](https://github.com/piwheels/packages/issues/59)で同じエラーがあったので
張ってあった[リンク](http://www.yoyojacky.com/)の先を実行しました。
まずターミナルでこちらを実行

```
LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1
```
次にvimで編集

```
vim.tiny .bashrc
export LD_PRELOAD=/usr/lib/arm-linux-gnueabihf/libatomic.so.1 #この一文を追加
```
vimは`a`で編集モード`ESC`からの`:wq`で保存して終了です。
ミスしたら`ESC`からの`:q!`で保存せず終了。

最後に

```
source .bashrc
```
で適用させます

確認してみます

```
$ python3
Python 3.7.3 (default, Apr  3 2019, 05:39:12) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'4.1.1'
```
たしかにインストール出来ています。
OSMのpbfをparquetに変換する

# 目的

OSMの全球データをSparkあたりで処理したいな、のための前処理です。
Apache Parquetというデータ形式に、Open Street MapのPBF形式から変換します。

# osm-parquetizerを使う

世の中にはだいたい同じようなことを考えてはるか先に進んでいる先人がいるものです。
OSMのPBFをSparkで処理できないか調査していたら、[adrianulbona/osm-parquetizer](https://github.com/adrianulbona/osm-parquetizer)というリポジトリを見つけました。まさに、PBF形式を、Apache Parquetというカラム志向のデータ形式に変換するものです。
今回は、これを使ってみましょう。

環境はUbuntu19.10を手元のVMに用意しました。
aptで必要なものを入れます。

```
apt install default-jdk git maven
```

そして、Githubからリポジトリをクローンします。

```
git clone git@github.com:adrianulbona/osm-parquetizer.git
```

ビルドします。

```
cd osm-parquetizer/
mvn clean package
```

試しに、東京だけを切り出したPBFファイルで試してみます。

```
java -Xmx768M -jar target/osm-parquetizer-1.0.1-SNAPSHOT.jar ../../Tokyo.osm.pbf
```

```
suguru@thermosphere:~/repo/osm-parquetizer$ ls -lh ~
total 136M
drwxrwxr-x 3 suguru suguru 4.0K Nov 28 17:51 repo
-rw-rw-r-- 1 suguru suguru  30M Nov 22 19:26 Tokyo.osm.pbf
-rw-r--r-- 1 suguru suguru  78M Dec  4 17:03 Tokyo.osm.pbf.node.parquet
-rw-r--r-- 1 suguru suguru 493K Dec  4 17:03 Tokyo.osm.pbf.relation.parquet
-rw-r--r-- 1 suguru suguru  29M Dec  4 17:03 Tokyo.osm.pbf.way.parquet
```

変換されています。
実際にやってみたところ、Java実行時の最大ヒープ量を設定する-Xmxで768MByteを指定しないと、OutOfMemoryで完了しませんでした。
思った以上にメモリを食いそうなので、全球データが実行できるか不安です。

# 今後

ParquetをSparkで処理するのをやってみる。
ラバーダック・デバッグの再考とイチオシツールの紹介

今年はソフトウェアのデバッグ手法の一つ、ラバーダック・デバッグについて自分なりの考えを広げられた一年でした。本来の手法とは異なってる可能性がありますが、ひとつの考え方として見てもらえれば幸いです。

## ラバーダック・デバッグ？

[ラバーダック・デバッグ - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%A9%E3%83%90%E3%83%BC%E3%83%80%E3%83%83%E3%82%AF%E3%83%BB%E3%83%87%E3%83%90%E3%83%83%E3%82%B0)

> ラバーダック・デバッグ とは、ソフトウエア工学におけるコードのデバッグ手法である。ラバーダック・デバッグは、The Pragmatic Programmer[1]という本で紹介された、プログラマーがラバーダックを持ち歩きアヒルちゃんに向かってコードを1行ずつ説明することによりデバッグを行うという話が由来である。この手法には、他にも多くの別名があり、しばしば様々な無生物が用いられている。

私はこの手法を、自分の考えを整理することに使えるデバッグ手法、と捉えています。でも実際におもちゃに向かってブツブツと話す行為は確実に迷惑です。ではどうやってラバーダック・デバッグをすればいいでしょうか？

## より現実的なラバーダック・デバッグ

ラバーダックの代わりとなる道具をいくつか紹介します。

### Slack

いつのまにか随分と使われてますね。特に分報という運用ルールとラバーダック・デバッグの相性は良いです。ほぼ自分しか書き込まない場所をラバーダックと捉えて、考えやコードの断片などを書き殴ります。
ある程度は時と状況などを弁える必要はありますが、声に出して話しかけるのと違って読み返すことができます。タイミングが良ければ他の人からレスポンスがもらえるかもしれません。
IRC みたいですね。

メリット: お手軽
デメリット: ある程度パブリックに向けて発信しているということを意識する
デバッグ効率: ★★☆

### 植物

全然関係ないことをしてるときにふと妙案が浮かぶ、そんな経験を誰しもしているのではないでしょうか。思考する環境をちょっと変えたいときに便利なのが植物です。観葉植物のお世話をしながら物を考えるのは、なかなかにいい体験です。和みます。今ではこのモンステラは私にとって相棒のようなものです。

![monstera1024.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4162/040c4d91-7763-00fc-2c3b-f16c92cebae0.jpeg)

メリット: 植物に気が向くようになる
デメリット: 植物を用意する（植物を貰うことありますよね？）
デバッグ効率: ★☆☆（慣れるとただの風景になる）

### 普段とは違う場所で作業する

ラバーダックどころの話ではないですが、思考中のことをステークホルダに話してみたい、でもそういう人が自分の近くに来ることはあまりない、ということはないでしょうか。
この問題に対して辿り付いた手段がスタンディングワークです。上記の手段と違い、環境整備が難しいですが私にとって一番効果が高いです。呼び止めたい人が視界に入ったらすぐにでも会話を始められます。
かれこれ1年間継続しているんですが、私の性格には意外と合ってるようです。

メリット: 目立つ、体力を使う
デメリット: 目立つ、体力を使う
デバッグ効率: ★★★

## まとめ

このように見方を変えればラバーダック・デバッグの応用方法が見つかるものです。来年もまた色々な手段を探していきます。

## おまけ

ラバーダックの話だけだとアレなのでイチオシのツールを一つ紹介します。

### [Amazon Athena](https://aws.amazon.com/jp/athena/)

動作明快。処理が早い。ただ JSON 関数の挙動が Presto と違うっぽい。例えばあるカラムに JSON が入ってて集計したい。jq だとこんな感じ。

```
$ echo '[{"name": "item1", "count": 1}, {"name": "item2", "count": 2}]' | jq '[.[] | .count] | add'
3
```

まず Athena で各オブジェクトを扱うためにキャストが必要そうで ROW が使えそう、のはずなんだけど Presto のサンプルのクエリが通らない…
[6.12. JSON Functions and Operators — Presto 0.229 Documentation](https://prestodb.io/docs/current/functions/json.html#cast-to-json)

```sql
--- Error running query: SYNTAX_ERROR: line 1:170: Cannot cast json to row(v1 bigint,v2 varchar,v3 boolean)
SELECT CAST(JSON '{"v1":123,"v2":"abc","v3":true}' AS ROW(v1 BIGINT, v2 VARCHAR, v3 BOOLEAN));
```

これは Athena が使っている Presto のバージョンが古いせいらしい。
[Cast json object to row not working · Issue #12949 · prestodb/presto](https://github.com/prestodb/presto/issues/12949)

そこでバッドノウハウを考えた。map で値を varchar にキャストしてから integer にキャストし直すとアラ不思議。意図通り集計できる。
お試しあれ。

```sql
select sum(reduce(
  cast(JSON '[{"name": "item1", "count": 1}, {"name": "item2", "count": 2}]' as array<map(varchar, varchar)>),
  0, (s, x) -> s + cast(x['count'] as integer), s -> s))
;
-- 3
```
C++20 指示付き初期化子

# はじめに
こちらはC++ Advent Calendar 2019の4日目になります。
参加登録がnullptrだったので遅れてではありますが投稿させていただきました。

昨日は @s51517765 さんの [C++で競プロ入門、について書きます。](https://s51517765.hatenadiary.jp/entry/2019/12/02/073000) でした。
ありがとうございました。
明日は @Fuyutsubaki さん です。
よろしくお願いいたします。

# 指示付き初期化とは

C++20で集成体初期化の際にデータメンバを指定する構文が追加されました。

### struct(class)

```c++

struct A
{
    int x = 0;
    int y = 0;
    int z = 0;
};

A a{.x = 1, .y = 2, .z = 3};
// 指定がないメンバはデフォルトコンストラクタが呼ばれる
A b{.y = 2,}; 
// 順序が宣言順と違うとコンパイルエラー(C言語ならOK)
// A c{.y = 2, .x = 1, .z = 3};
```

### union

共用体でも使用は可能です。
ただし、一つのみの指定です。

```c++

union B
{
    int i;
    float f; 
};

B a{.i = 1};
// 複数指定するとコンパイルエラー
// B b{.i = 1, .f = 1.0f};
```

### ネストされた指示付き初期化

```cpp

struct A
{
    int x = 0;
    int y = 0;
    int z = 0;
};

struct B
{
    A a;
};

B a{ .a{.x=1, .y = 2, .z= 3} };

// コンパイルエラー　C言語ならOK
// B b{ .a.x = 1 };
```

# コンストラクタ定義時について

コンストラクタが定義されていた場合は指示付き初期化は使用できない。
その場合の動作についてはわからなかった。(未定義動作？)
gccだとコンパイルは通ってコンストラクタが呼ばれた。
clangだとコンパイルエラーになった。

```c++

struct A
{
    int x = 0;
    int y = 0;
    int z = 0;
    
    A(int,int,int):
      x(3),
      y(3),
      z(4)
    {}
};

// エラー???
// A a{.x=1, .y = 2, .z= 3}; 
```

# 指示付き初期化のメリット

１．メンバ変数名が指定されているので構造体の定義を見なくてもぱっと見でわかりやすい

```c++

// それぞれの値はなんだろう？？
A a{1, 2, 3}; 
// わかりやすい
A a{.x=1, .y=2, .z=3}; 
```

２．うっかりメンバ変数の宣言順序を入れ替えてもコンパイルエラーで気づける

```diff

struct A
{
    int x = 0;
-   int y = 0;
    int z = 0;
+   int y = 0;
};

// y=2が入っているつもりが3になってた
A a{1, 2, 3}; // OK

// 指示付き初期化なら宣言順が変わればコンパイルエラーなのでミスに気づける
A a{.x=1, .y=2, .z=3}; // Error 順序おかしい！
```
 
# 参考

https://ja.cppreference.com/w/cpp/language/aggregate_initialization

# まとめ

- C++20で指示付き初期化ができるようになった
- C言語とは違いがあるので注意
GoでFortniteのデータを取得するパッケージを軽い気持ちで作りはじめたら結構きちんと作ってしまった

# まえがき

<(_ _ )> テスト書いてないじゃんってツッコミはやめてください。1日1つ何かしら作るっていうAdvent Calendarで作ってるものなので。後のせで書きます。

# 動機

「 `Hey Google、 Fortniteの結果を教えて` 」ってやつ作ろうと思ったけど、そもそも結果ってどうやって取るんだろうってなったので作ってみた。

海外で他にも作ってる人いたけど、自分の使いたいところしか対応してなかったり、メンテされてなかったりしたので自作しました。

# そうそうに諦めたこと

ゲーム中の通信を解析して、それを元に公式のAPIを非公式に叩きにいくこと。
PCとかだったらその手のものが結構あってGithubとかにもライブラリがあがっているのだけれど、Nintendo Switchに関してはコミュニティには出回ってなくて商用（？）でつくられたものぐらいしかSwitchにデータが取れそうになかったので。

# 利用したAPI

Fortnite Tracker
https://fortnitetracker.com/site-api

Tracker Network(TRN)が提供しているFortniteのAPI
TRNはAPIは利用していなかったものの他のゲームでランキングとかお世話になってたことがあったので

#　作ったもの

Relese: https://github.com/usk81/go-fortnite/releases/tag/v0.0.1
Page: https://github.com/usk81/go-fortnite

## ご利用前に

トークンを取得してください。
会員登録（無料）して、フォームを埋めていったら簡単に取得できます。
情報は結構適当でよさそう
https://fortnitetracker.com/site-api

## インストール

```
go get -u github.com/usk81/go-fortnite
```

## 例

```go
s, _ := New(nil, "your access token")

// 統計情報の取得
//   例.
//   Platform: gamepad (nintendo switch)
//   nickname: Shady Grove
result, err = s.BRPlayerStats("gamepad", "Shady Grove")

// 対戦履歴
result, err = s.MatchHistory("player account id")

// ストアの情報
result, err = s.Store()

// チャレンジ情報
//   note: API自体が生きてなさそう。取得できる情報が少し古かった
result, err = s.Challenges()
```

## 注意

APIさわってると、fortnite特有のコードが含まれているのですが、以下のような意味らしいです。
自分のライブラリでは、一部置き換えを行ってますが、全て置き換えできているわけじゃないので、使われる方がいたら参考にしてください

| code | 意味 |
|---|---|
| p2 | Solo |
| p10 | Duo |
| p9 | Squad |

## CLIで動かせるサンプルも作ってみた

本当に動くの？って突っ込まれそうなので、簡単なCLIも作ってみた。
ゴミ混ざるのが嫌なので、別リポジトリにしました

https://github.com/usk81/go-fortnite-cli

```
// 統計情報の取得
go run main.go -t your-token stats gamepad player-nick-name

// 対戦履歴
go run main.go -t your-token history player-account-id

// ストアの情報
go run main.go -t your-token store

// チャレンジ情報
go run main.go -t your-token challenges
```

これでスマートアシスタントやchatbotにつなぎ込みができるヽ(=´▽`=)ﾉ
Postmanで301リダイレクトをテストしてみる

この記事は、[ZOZOテクノロジーズ #4 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech4) 5日目の記事になります。
昨日は、@tkani さんの「[spring-graal-nativeでSpring BootをGraalVM native imageにしてみる](https://qiita.com/tkani/items/37878d268db54d581a1b)」でした。

本記事では、Postmanを利用した301リダイレクトのテストについてご紹介します。

#はじめに
Webサイト開発でURLが変わることってときどきありますよね。
「旧URLでのアクセスがある程度想定されるので301リダイレクトさせてください。」
といった感じで。そんなときは手軽にテストケースが作成でき、回帰テストにも利用できるPostmanをおススメです。

#301リダイレクト
301リダイレクトについておさらいします。
[WSC](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)と[WikipediaのHTTPステータスコード](https://ja.wikipedia.org/wiki/HTTP%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89)をみると、301 Moved Permanentlyと記載されています。つまり恒久的な移動です。
恒久的とは「いつまでもその状態が続くさま」になります。[goo国語辞典より](https://dictionary.goo.ne.jp/word/%E6%81%92%E4%B9%85%E7%9A%84/)

#Postmanとは
[Postman](https://www.getpostman.com/)はPostman,Incが開発し、800万人の開発者と40万以上の企業が利用しているAPIのテストツールです。800万人の開発者が利用しているってかなりすごいですね。
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221056/030fa659-d5a8-635d-2efe-0f02b9603699.png)](https://www.getpostman.com/)


#テスト
301リダイレクトのテストにはいろいろな方法があると思いますが、Postmanを利用して301リダイレクトのテストをしてみます。

##設定
まずSettingsにあるAutomatically follow redirectsをOFFにします。
こうすることでStatus codeが301の場合を判断できるようになります。
※DefaultはONで設定されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221056/b3f648b1-7d62-af2a-0024-bd9f2ac16815.png)


##テストパターン
ここでは下記の簡易的なダミーURLをテストパターンとして利用します。
aaaがbbbに変わった想定ですね。

・301リダイレクト対象のURL
https://xxxx.jp/aaa/bbb/?ccc=1
・301リダイレクト先のURL
https://xxxx.jp/aaa/eee/?ccc=1

##テストケース
テストケースには下記の2つをチェックするテストコードを記述します。

 - Status codeが301か？
 - 指定のURLへリダイレクトしているか？
 
```js
console.log("Redirect URL:[" + pm.response.headers.get("Location") + "]" )

pm.test("Status code is 301", function () {
    pm.response.to.have.status(301);
});

pm.test("Body matches string", function () {
    pm.expect(pm.response.headers.get("Location")).to.include("/aaa/eee/?ccc=1");
});
```
Postmanの画面イメージはこんな感じです。
URL入力欄に「 https://xxxx.jp/aaa/bbb/?ccc=1 」を入力し、Sendボタンをクリックすることでテストが実行されます。PSAAと表示されればテスト結果はOKです。
※画像はダミーURLのためFAILと表示されていますので、ご注意ください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221056/924f4da2-3cb4-5821-df18-ef2e9ab60bad.png)

##便利機能
左下のPostman Consoleをクリックすることでコンソール画面を表示し、ログの確認が可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221056/7f9e0e18-f30e-bc67-09f5-2b1b7375aa6c.png)

ログはconsole.log("xxxx")とコーディングすることでコンソール画面に出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221056/7ccd5336-60ff-c9c4-e3e5-2f4afd42d588.png)

#おわりに
Postmanを利用するととても簡単にテストが実施できましたね。
複数のテストケースを連続して走らせることも可能なため、ちょっとしたテストをするときはとても便利です。
慣れてくるとUI上でのテストが煩わしくなってきますので、そんなときはPostmanのコマンドラインツールである[Newman](https://learning.getpostman.com/docs/postman/collection-runs/command-line-integration-with-newman/)がおススメです。


明日の[ZOZOテクノロジーズ #4 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/zozo_tech4) 6日目は、@yskurさんの投稿になります。
removeChildでリストを消す【備忘録】




```HTML
<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <title>Document</title>
</head>

<body>
  <ul id="lists">
    <li>要素1</li>
    <li>要素2</li>
    <li>要素3</li>
    <li>要素4</li>
    <li>要素5</li>
  </ul>
  <input type="button" value="削除" onclick="remove()">
  <script>
    function remove() {
      let parentElement = document.getElementById('lists');
      let elements = parentElement.getElementsByTagName('li');
      let removeIndex = elements.length - 1;
      parentElement.removeChild(elements[removeIndex]);
    }
  </script>
</body>

</html>

```
discord.jsでDiscord Botを作ってみた

# 概要
discord.jsでbotを作ったので、それを書き留めときます。
インストールは、Ubuntu mateを想定したものを紹介致します。
他のOSを使っている方に関しては、調べてください

# Node.js＆npmのインストール方法
コマンドラインを開き下のコマンドを実行します。

```Ubuntu
sudo apt install -y nodejs npm
```

Windowsの場合
[Node.jsの公式サイト](https://nodejs.org/ja/download/)へ行きインストーラをダウンロードします。


# discord.jsのインストール方法
こちらもコマンドラインを開き下のコマンドを実行します。

```shell:Ubuntu
sudo npm i discord.js
```

```shell:Windows
npm i discord.js
```

# 実際に書いていく
まずは、discord.jsを読み込むコードを書きます。

```javascript:index.js
 const discord = require("discord.js");
 const client = new discord.Client;
```

簡単な返事機能のコードを書きます。
ユーザーがこんにちはと送信した場合に、メンション付きで返事をします。
messageイベントにはmessageプロパティが含まれているので、
messageの内容、つまり`message.content`がこんにちはだった場合の処理を書いてあげれば、返事をしてくれます。

```javascript:index.js
//続き
client.on("message", message => {
  if (message.author.bot || !message.guild) return
  if (message.content === "こんにちは"){
    message.reply('さん、こんにちはー');
  }
});
client.login("Botのトークン")
```

`if (message.author.bot || !message.guild) return`でBot自身のメッセージとDMといったGuild以外からのメッセージに反応しないようにしています。

Botのトークンのところには、[Discord Developer Portal](https://www.google.com/url?sa=t&source=web&rct=j&url=https://discordapp.com/developers&ved=2ahUKEwjTgb-hgp3mAhXnDaYKHblNB54QFjAAegQICBAD&usg=AOvVaw01ITZetBJJYDSkFaoUknfh)
にある、自分のBotの右にあるBotへ行きtokenの下にあるcopyを押して、index.jsの`Botのトークン`のところへ貼り付けます。

# 実際にBotを起動してみる
Botを起動させるためにはコマンドラインで下のコマンドを実行します。

```
node index.js
```

ちゃんとBotがONLINEになっていれば成功です。
色々な種類のコードを書いていく予定ですので、よろしくお願いします。
code-server ローカル環境篇(5) DBなども含めたMVC環境を用意してみよう (2)

これは、2019年 code-server に Advent Calender の 第4日目の記事です。今回も、code-server って何だろう?と言う事を解説していきます。


(1) [code-server って何?](https://qiita.com/kyorohiro/items/35bab591cd4a6b975c80)
(2) [Dockerで独自のcode-server 環境を作って見る](https://qiita.com/kyorohiro/items/d991f6fbf77a425525c5)
(3) [VSCode の Plugin を 利用してみる](https://qiita.com/kyorohiro/items/11a13d32c8748f3d7002)
(4) [DBなども含めたMVC環境を用意してみよう (1)](https://qiita.com/kyorohiro/items/4ed279dd91e39321ed20)
(5) [DBなども含めたMVC環境を用意してみよう (2)](https://qiita.com/kyorohiro/items/94c75a13ddccc5f39d85)
(6) [DBなども含めたMVC環境を用意してみよう (3)](https://qiita.com/kyorohiro/items/71a8b6ce3cbb9b36019a)
(7) [DBなども含めたMVC環境を用意してみよう (4)](https://qiita.com/kyorohiro/items/106ebb7003072a8dc989)
(8) [DBなども含めたMVC環境を用意してみよう (5)](https://qiita.com/kyorohiro/items/a019e4ab6dcda55896e0)
(9) [DBなども含めたMVC環境を用意してみよう (6)](https://qiita.com/kyorohiro/items/287364f03ed7a88f714e)
(10) [おまけ](https://qiita.com/kyorohiro/items/f96d27bba9fb23c0a097)


(NEXT->) [オンライン環境篇 1日目 作業環境を整備する](https://qiita.com/kyorohiro/items/603d6ee693fc2300079e)

(..) ローカルで、DBなどの環境も含めて構築するには
(..) オンライン上に置くには?
(..) K8Sなどの最近の流行りの環境と連携するには?
(..) Code-Serverを改造して、より良くしたい


前回の続きで、flaskを動作させてみます。

# 今回の何?
### Terminalが便利。Bashがオススメ

VSCodeなので、Terminalを利用できます。
Linuxをcliで操作できます。なので、別途sshを立ち上げたり

```
dockeer-compose exec -it app bash
```

などして、別途　Terminalを立ち上げる必要もなくなります。
私は、bashが使いバレているので、最初に

```
bash
```

と入力するようにしています。


# Flaskとは
軽量のWebFrameworkです。超簡単にWebページを作成する事ができます。

https://github.com/pallets/flask


# Flask環境を作成してみよう!!

<img width="1250" alt="Screen Shot 2019-12-05 at 0.55.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54192/893618d8-85f6-7a5f-8653-c8f75098c7f6.png">

前回の続きから初めてみます。


requirements.txt を作成します。

```:requirements.txt
flask
```

コマンドライン上で、

```
pip install -r requirements.txt 
```

コードを書いてみます。

```python:main.py
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello, World!"

app.run("0.0.0.0",port=8080)

```

実行してみましょう!!

```bash

$ python main.py 
 * Serving Flask app "main" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
```

http://127.0.0.1:8080/ をブラウザーで開くと、Hello,World! と表示されます

<img width="1251" alt="Screen Shot 2019-12-05 at 1.11.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54192/5fd15ad1-8d58-80c8-09b2-6435f6d3888e.png">



# 次回

db の 環境を作成してみます

# PS

## ソース
https://github.com/kyorohiro/advent-2019-code-server
たった20行のコードでスプレッドシートをコピーするメニューを追加する

この記事は[Ateam Brides Inc. Advent Calendar 2019](https://qiita.com/advent-calendar/2019/a-t-brides)　11日目の記事です。

# 完成図

![copy.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/ff8e8f80-d863-e99b-aad4-9501bf2ecd22.gif)

# はじめに

スプレッドシートを、定例の議事録として使うことがあるかと思います。
（表計算アプリの使い方じゃない！という点は、今回多めに見てください＞＜）

その際にテンプレシートをコピーして、今日の日付にして…と、毎回繰り返していると手間に感じることも多いはずです。
メニューにテンプレコピーボタンを用意し、簡単に今日の日付のシートをコピーする機能をGoogle Apps Scriptで20行で実装します。

# スプレッドシートの準備

GoogleDriveからスプレッドシートを新規作成します。
もしくは既存のものでも構いません。

「テンプレ」というシートを用意し、適当に文字を打ち込みます。

次にメニューのツール→スクリプトエディタを開きます。
おもむろに以下コードを貼り付けて保存します。

# コード

```javascript
function onOpen() {
  SpreadsheetApp.getActiveSpreadsheet().addMenu('テンプレート', [{name: 'シートのコピーを作成', functionName: 'copySheet'}]);
}

function copySheet() {
  var templateFileName = "テンプレ";
  var today = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd');
  var spreadSheet =  SpreadsheetApp.getActiveSpreadsheet();
  var templateSheet = spreadSheet.getSheetByName(templateFileName);
  var distSheet = spreadSheet.getSheetByName(today);
  
  if (!templateSheet) { // テンプレシートの名前が変えられたり削除された場合のエラー
    Browser.msgBox(templateFileName + "という名前のシートがありません。");
  } else if (distSheet) { // 既にファイル生成済み
    Browser.msgBox("既に今日の日付のシートが存在するため、コピーできません。");
  } else { // 新シート追加
    distSheet = templateSheet.copyTo(spreadSheet).setName(today);
    spreadSheet.setActiveSheet(distSheet);
  }
}
```

# 権限ダイアログを承認

スプレッドシートにアクセスする権限を承認する必要があります。
「関数を選択」メニューからonOpenを選び、2つ左の三角を押すと実行できます。
初回は承認ダイアログが出るので、

![4.png](https://qiita-image-store.s3.amazonaws.com/0/199678/5db8a72d-1f6d-57fc-c9a8-935277f7b08c.png)

Googleアカウント選択画面で自分のアカウントを選んだ後、もし以下のようなウィンドウが出た場合は
*詳細→xxxxx（安全ではないページ）に移動→テキストボックスに「次へ」と入力→許可* で許可されます。
![5.png](https://qiita-image-store.s3.amazonaws.com/0/199678/493be6dc-9ae1-a689-c51b-581a49f36caa.png)

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

# 最終確認

スプレッドシートを再読込します。
メニュー一番右に「テンプレート」というメニューが追加されているので選びます。

![copy.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199678/ff8e8f80-d863-e99b-aad4-9501bf2ecd22.gif)

無事に、今日の日付でシートがコピーされているはずです。
ちなみに間違えてテンプレシートを消したり名前変更してしまった場合にダイアログを出したり、既に今日分のシートがある場合にダイアログを出す処理も実装されています。

ちなみにテンプレ以外にもう1シート以上ある場合は、テンプレシートを非表示にすることも可能です。すると日々テンプレシートがシート一覧に出なくなるのでスッキリします。

# 私たちのチームで働きませんか？
![alt](https://brides.a-tm.co.jp/wp-content/uploads/2019/11/ATEAM_BRIDES.jpg)
[エイチーム](https://www.a-tm.co.jp/)は、インターネットを使った多様な技術を駆使し、幅広いビジネスの領域に挑戦し続ける**名古屋の総合IT企業**です。
そのグループ会社である**株式会社エイチームブライズ**では、一緒に働く仲間を募集しています！

- [ハナユメ サイト開発グループ](https://jobs.qiita.com/employers/brides-a-tm/development_teams/138)
- [HIMARI開発チーム](https://jobs.qiita.com/employers/brides-a-tm/development_teams/147)
- [技術開発部](https://jobs.qiita.com/employers/brides-a-tm/development_teams/148)

上記求人をご覧いただき、少しでも興味を持っていただけた方は、まずは**チャットでざっくばらんに話**をしましょう。
技術的な話だけでなく、私たちが大切にしていることや、お任せしたいお仕事についてなどを詳しくお伝えいたします！

[Qiita Jobs](https://jobs.qiita.com/)よりメッセージお待ちしております！
Linux標準教科書メモ1

## 基本ソフトウェアと応用ソフトウェア

コンピュータ上で動くソフトウェアは大きく二つに分けられる。基本ソフトウェアと応用ソフトウェアの二つです。基本ソフトウェアはWindows, mac OS, LinuxなどのOS(オペレーションシステム)を指し、応用ソフトウェアはOS上で動くWord, Excel, Power Pointなどが応用ソフトウェアとなる。

## 基本ソフトウェア(OS)の役割

基本ソフトウェアは応用ソフトウェアが動作するのに必要な「部品」を提供したり、ハードウェアの持つ「資源」
を管理する役割を持っています。
「部品」とは応用ソフトウェア（例えばWordとか）をユーザーが操作する際に必要なウィンドウ、メニューやカーソル、ファイルを開いたり保存したりする際のメッセージなどです。OSがないと応用ソフトウェアを作るのに毎回これらの機能を作る必要が出てきて開発に多大なコストがかります。なのでOSは応用ソフトウェアで使う共通機能を提供しています。
ハードウェアの「資源」とはCPUやメモリなどのことで、OS上で動いてる応用ソフトウェアに対して必要な分を分けて与えています。またコンピュータは同時に一つの処理しか出来ないため、動いている応用ソフトウェアを管理して処理のタイミングを高速で切り替えることで複数のアプリを動かしています。
雑にpuppeteerでページをクローリングして画像を保存するやつ

## 雑にpuppeteerでページをクローリングして画像を保存するやつ

最近クローラを書いていたのでメモ
https://adventar.org/calendars/4021

puppeteerでクローリングする際はimgタグのsrcをダウンロードしなくても、 `response.buffer()` の画像を保存するだけで良い。
SPAのサイトとかも読み込むだけで良いので楽にダウンロード出来る。

とりあえずwikipediaのみかんの画像を集める。

```javascript
const fs = require("fs");
const path = require('path');
const puppeteer = require('puppeteer');

// 画像を保存するPATHの設定
const SAVE_DIR = path.resolve(__dirname,"images" );

const USER_AGENT = "user agent"
const sleep_time = 1000;
const time_out = 10000;

const headless = true;

// このregexにmatchするものだけ保存する
const download_image_path = "^https:\/\/.*wikimedia.org\/wikipedia/commons.*\.jpg";

// クローリングするページ
const urls = [
    "https://ja.wikipedia.org/wiki/%E3%82%A6%E3%83%B3%E3%82%B7%E3%83%A5%E3%82%A6%E3%83%9F%E3%82%AB%E3%83%B3",
    "https://ja.wikipedia.org/wiki/%E5%86%B7%E5%87%8D%E3%81%BF%E3%81%8B%E3%82%93",
    "https://ja.wikipedia.org/wiki/%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%97%E3%83%95%E3%83%AB%E3%83%BC%E3%83%84"
];

(async () => {
    // 起動処理
    let browser = await puppeteer.launch({ headless: headless });
    const page = await browser.newPage();

     // UAを設定する
     await page.setUserAgent(USER_AGENT);

     // pageのレスポンス時のListenerを設定する
     const saveFile = async response =>{
         const url = response.url();
         if (response.request().resourceType() === 'image') {
             response.buffer().then(async file => {
                 const fileName = url.split('/').pop();
                 if(  url.search(download_image_path) != -1){
                     const files = fs.readdirSync(SAVE_DIR);
                     const filePath = path.resolve(SAVE_DIR, fileName);
                     const writeStream = fs.createWriteStream(filePath);
                     writeStream.write(file);
                 }
             },error => {
                 console.error(error)
             })
         }
     }
     // Listenerを設定
    await page.on('response', saveFile);

    // クローリングを行う
    for (const url of urls) {
        console.log(`get page :${decodeURI(url)}`)
        try {
            // ページに遷移する
            await page.goto(url, {waitUntil: 'networkidle0', timeout: time_out});
            await page.waitFor(sleep_time);
        } catch (error) {
            // ページが取得できなかった際はページをファイルに書き出す
            console.error(e);
            fs.appendFile("missing_pages.txt",`${url}\n`);
        }
    }
    await browser.close();
})();
```

```
$ node index.js

get page :https://ja.wikipedia.org/wiki/ウンシュウミカン
get page :https://ja.wikipedia.org/wiki/冷凍みかん
get page :https://ja.wikipedia.org/wiki/グレープフルーツ
```

こんな感じで集まる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103878/953fcbb5-a64a-04b8-1b38-c37c30a93c45.png)