OmniAICreator/Qiita-1.07M huggingface dataset:
Pointerで降順リストを作成する

#降順リストの作成
C言語で昇順リストを作成したので、その続きで降順リストのコードも書いてみました。
※昇順リストのコード: https://qiita.com/sabewe/items/f4047129e88c3d3f46ff

```descend.c
#include <stdio.h>
#include <stdlib.h>

//declare node struct
struct node{
  int data;
  struct node *link;
};

//function declaration
void display(struct node *);
int count(struct node *);
void add(struct node **, int);

int main(){
  //create pointer to point to the first node in linked list
  struct node *p;
  //set pointer to point to empty linked list
  p = NULL;

  //add nodes to linked list
  add(&p,5);
  add(&p,1);
  add(&p,6);
  add(&p,4);
  add(&p,7);
  add(&p,5);

  //display linked list
  display(p);
  //count number of nodes
  printf("No. of elements in Linked List = %d\n",count(p));
}

//add nodes to linked list in descending order
void add(struct node **q, int num){
  struct node *r,*temp,*prev;

  //if p is not pointing to any node, create first node
  if(*q==NULL){
    //create new node
    r = malloc(sizeof(struct node));
    //set node data
    r->data = num;
    //set link of new node to point to null
    r->link = *q;
    //set p to point to new node
    *q = r;
  }
  //if num is bigger than p
  else if(num > (*q)->data){
    //create new node
    r = malloc(sizeof(struct node));
    //set node data
    r->data = num;
    //set link of new node to point to address pointed by p
    r->link = *q;
    //set p to point to new node
    *q = r;
  }else{
    //set initial value of locator pointer
    temp = prev = *q;

    //loop through linked list
    while(temp!=NULL){
      //if num is smaller than previous node data but bigger than current node data
      if((num <= prev->data) && (num > temp->data)){
        //create new node
        r = malloc(sizeof(struct node));
        //set new node data
        r->data = num;
        //set new node to point to current node
        r->link = temp;
        //set previous node to point to new node
        prev->link = r;
        break;
      }

      //keep address of previous node
      prev = temp;
      //go to next node;
      temp = temp->link;
    }

    //if looped till the last node
    if(temp==NULL){
      //create new node
      r = malloc(sizeof(struct node));
      //set new node data
      r->data = num;
      //set link of new node to point to null
      r->link = temp;
      //set last node to point to new node
      prev->link = r;
    }
  }
}

//display linked list
void display(struct node *q){
  while(q!=NULL){
    printf("%d\n",q->data);
    q = q->link;
  }
}

//count the number of node in linked list
int count(struct node *q){
  int c=0;

  while(q!=NULL){
    c++;
    q = q->link;
  }

  return c;
}

```
初心者のプログラミング４

# 勉強の記録
## 勉強した内容
- ツイッターに投稿するようのボタンの追加

## 何をベースに勉強してるか
- [N予備校 プログラミング入門　授業18](https://www.nnn.ed.nico/courses/668/chapters/9514)

## 内容の詳細
-今回追加したコード

```js
  removeAllChildren(tweetDivided);
    
    // aタグを作って属性を設定する
    const anchor = document.createElement('a');
    const href =
        'https://twitter.com/intent/tweet?button_hashtag='
        +encodeURIComponent('あなたのいいところ')
        +'&ref_src=twsrc%5Etfw';
    
    anchor.astAttribute('href', href);
    anchor.className = 'twitter-hashtag-button';
    anchor.aetAttribute('deta-text', result);
    anchor.innerText = 'tweet #あなたのいいところ';

    // aタグをHTMLとして追加する
    tweetDivided.appendChild(anchor);

    // scriptタグを作る
    const scriot = document.createElement('script');
    Script.setAttribute('scr', "https://platform.twitter.com/widgets.js");
    // scriptタグをHTMLとして追加する
    tweetDivided.appendChild(script);
```

### わかったことについて
- HTMLにJSからタグを追加していくのが良く分かったと思います。


### むずかしかったよ
- 全体的に難しかった、なぜエラーが出ているのか分からないのでもう一度チェックする

## 次回やる予定のこと
- 今回の修正と次回の授業
AWS Textractでテキスト抽出やーる（Python3.6）

# はじめに
AWS Textractで画像からテキスト抽出をやってみました

# 開発環境
- Windows 10
- Anaconda
- Python 3.6
- OpenCV 4.4.0
- awscli v2

# 導入
1．awscliをインストールします
[Windows での AWS CLI バージョン 2 のインストール、更新、アンインストール](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-windows.html)を参考にAWSCLIV2.msiをインストールします

2．AWSコンソールからIAMでアクセスキーを生成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/2e018177-8c87-8faf-ecfc-d8b2c35536c7.png)

3．コマンドプロンプトでawscliの設定を行います

アクセスキー生成時に得られたAccess KeyとSecret Access Keyを入力します。
デフォルトリージョン名は、Textractが使えるap-south-1（アジアパシフィック ムンバイ）を指定します。
アウトプットフォーマットはjsonとします。

~~~console
$ aws configure 
AWS Access Key ID [None]: XXXX
AWS Secret Access Key [None]: XXXX
Default region name [None]: ap-south-1
Default output format [None]: json
~~~

- [リージョンとゾーン](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-regions-availability-zones.html)
- [設定ファイルと認証情報ファイルの設定](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-files.html)

3．IAMのアクセス権限の追加からポリシー「AmazonTextractFullAccess」を追加します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/7983eba5-ec41-273b-42bc-7ca6a766d8c0.png)

4．anaconda promptを開き、Python 3.6環境を作成します。

~~~console
$ conda create -n py36 python=3.6
$ conda activate py36
~~~

5．ライブラリをインストールします

~~~console
$ pip install boto3
$ pip install opencv-python
~~~

6．下記のコードを実行してみましょう

~~~python
import boto3
import cv2
import os.path

def process_text_analysis(image):
    client = boto3.client('textract')
    image_data = cv2.imencode(".png", image)[1].tostring()
    response = client.analyze_document(Document={'Bytes': image_data}, FeatureTypes=["TABLES", "FORMS"])
    blocks = response['Blocks']
    return blocks

def draw_blocks(image, blocks):
    height, width = image.shape[:2]
    draw = image.copy()
    for block in blocks:
        if block['BlockType'] == "WORD":
            vertices = [(int(width * block['Geometry']['Polygon'][i]['X']), int(height * block['Geometry']['Polygon'][i]['Y'])) for i in range(len(block['Geometry']['Polygon']))]
            cv2.putText(draw, block['Text'], vertices[0], cv2.FONT_HERSHEY_PLAIN, 1, (255, 255, 255), 1, cv2.LINE_AA)
            cv2.rectangle(draw, vertices[0], vertices[2], (0, 255, 0))
    return draw

filename = "338px-Atomist_quote_from_Democritus.png"
image = cv2.imread(filename, cv2.IMREAD_COLOR)

blocks = process_text_analysis(image)
print("Blocks detected: " + str(len(blocks)))

draw = draw_blocks(image, blocks)
cv2.imshow("draw", draw)
cv2.waitKey(0)
~~~

|input|output|
|:--|:--|
|![338px-Atomist_quote_from_Democritus.png](https://upload.wikimedia.org/wikipedia/commons/thumb/a/af/Atomist_quote_from_Democritus.png/338px-Atomist_quote_from_Democritus.png)|![awsocr.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63863/e9576fba-37a9-34d0-4b2b-4c5a14738e91.png)|

お疲れ様でした。
BFFとはなんなのか？

仕事で複数のバックエンドから一つのクライアントと疎通するような構成の可能性があり、BFFが使えるかもということで調べてみました。

#対象者
* BFF(backend for frontend)の概要を知りたい方
* どういったメリット、デメリットがあるかを整理したい方

#概要
BFF(backend for frontend)とは簡単にいうと、クライアントとバックエンドの中間に位置し双方の複雑性を吸収するような作られたサーバーのことです。雑に書くとこんな感じです。

![スクリーンショット 2020-11-03 11.50.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179800/434a41ed-2370-d469-515c-908af301319a.png)

これが必要になった、背景はクライアントの端末の種類が増加したことそれに伴うロジックの増加です。
例えばweb,スマホ版web,スマホアプリ、デスクトップアプリなど複数のクライアントが登場し、それぞれにメッセージやコンテンツの出しわけを実装しようとすると、クライアント側のコードが複雑化し、冗長なコードを書かざる追えない状態になります。

そういった背景から、複数のバックエンドから情報を取得し適切な形に整形するなどのロジックを持つサーバーを導入することで、クライアントはUI/UXに注力するようにしたのがBFF登場の経緯です

#BFFのメリット
## APIの集約
シンプルにクライアントはBFFに対してのみAPIを投げればよく、複数のバックエンドに対しての対応や加工などをクライアント側が気にしなくてよくなります。また、バックエンドからの複数のレスポンスをクライアントが求める形に適切に加工して返すことができるので、クライアントの処理も少なくできます。

## バックエンドの差異の吸収
例えば、バックエンドの一部がgRPCで他がRESTで他がWebsocketなどのバックエンドの仕様や差異などを全て吸収できるので、複数の種類のバックエンドに対応できます。

## フロントエンドとバックエンドの分離
バックエンドとクライアントが直接通信を行う場合そのプロトコルは一致させないと行けませんでしたが、BFFを挟む場合はBFF <-> クライアントとBFF <-> バックエンドは全く別の通信になるので、 BFF <-> クライアントのみ GraphQLで BFF <-> バックエンドは RESTとのような柔軟な調整ができます。

## 複数にまたがる関心の分離
雑な言い方ですが、BFFに認証や、権限、キャッシュなど処理を置くことで、クライアントサイドとバックエンドサイドにまたがるような処理を取り去りシンプルな形で保つことができます。

#BFFのデメリット
## サーバーの開発と保守の手間
BFFもソースコードを持ち、実際に動くサーバーのため、それらに対する監視やデプロイなどの手間が当然増えます。

## デプロイのタイミング
BFFはバックエンドと密にやりとりをするため、例えばバックエンドのAPIの仕様変更をした場合BFFも変更して同時にデプロイしないと障害の原因になります。そういったデプロイに対して考えることが増えます。

## 通信量の増大と遅延
通信が増えるため、通信量が増大し、データを取得するために必要な経路が増えるためデータ取得にかかる時間は当然増えます。なので相応の増強や対処が必要になります。

## コードの冗長化
BFFが持つAPIとバックエンドが持つAPIの機能がほぼ一致するケース、例えばユーザー一覧の取得APIなど、機能や関心ごとが一致する場合はほぼ同じコードを書かないと行けないため、コードが冗長化します。

#実装方法
仮にバックエンドがRESTの場合BFFはroutingとHTTP Requestの機能さえ持っていればBFFは実装できます。クライアントとバックエンドの疎通の条件にもよりますが、ただ実装するだけなら難易度はそんなに高くないと思います。

複数のプロトコル対応やクライアント間をgraphQLでしたい場合は EnvoyやApolloなどサーバーを適切に考えないと行けないです。
セレクタの部分一致について

RSpecで統合テストを行っている中で下記のコードで画像の投稿がテストできることがわかりました。

```ruby

expect(page).to have_selector("img[src$='test_image.png']")
```
(``test_image.png``という名前で投稿した画像をテストしています。)

ですが、``$=``の意味が分からなかったため、調べたことを記録しておきます。


###セレクタの部分一致について
以下のように記述をすることで特定の文言を含むセレクタを指定できる


<b>前方一致 ・・・ E[foo^="bar"]</b>
foo属性の値がbarで始まるE要素

<b>後方一致 ・・・ E[foo$="bar"]</b>
foo属性の値がbarで終わるE要素


<b>部分一致 ・・・ E[foo*="bar"]</b>
foo属性の値にbarを含むE要素
<b/>

これをもとに冒頭で記述したコードを分解してみるとsrc属性の値が``test_image.png``で終わる``img要素``があるかどうかをテストしているということになります。

####参考記事
http://www.htmq.com/selector/attrvaluee.shtml
【備忘録】Rails エラー一覧 

#はじめに

今まで課題をこなしながら出会ってきたエラー文たちをまとめておきます。
といっても、解決策はケースバイケースだと思うので、こういうエラー文はこう考えるといいかもしれないです・・・程度の備忘録です。

#エラー文一覧

### Name Error 
![エラー１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664818/cc774a5c-686a-29f9-7274-72d65eb9e06d.png)

（例１）
Name Error in Books#index
undefind local variable or method 'book'
Did you mead? @book @book 
↓
ローカル変数かメゾットの'book'が定義されていない。
@bookか@booksではないのか。
**=> 考えられること**
スペルミスが多いです。もう一度指摘されているページを見てみましょう。

（例２）
Name Error in Books#index
uninitialized constant user::Relationship
24 passive-relationships.find-by(following_id:user_id).oresent?
↓
初期化されていない定数　user::Relationship
（=定義されたクラスを読むことができない）
**=> 考えられること**
こちらも、スペルミスの場合が多いです。model,view,controllerを見てみましょう。

### No Method Error

![エラー２.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664818/ba00ffd1-fe16-6b51-45bc-eaf517759f78.jpeg)

（例）
No Method Error in Books#create
undefind method 'user' for nil :nilclass
↓
メゾットエラー（定義されていないメゾットが呼び出されました）
'user'というメゾットが定義されていません。
**=> 考えられること**
①in Books#create　→ Booksテーブルにuserカラムを追加してない
②もし、Did you meadn?があるなら、それを記述しなおす。
ちなみに、カラム指定なんてしない'each'がよくこのエラー文で出てました（解決忘れてしまった）。

###Actioncontroller::UnknownFormat 


![エラー３.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664818/849b9c1f-ba49-f149-7b75-3de29d3a9030.png)

（例１）
Action Controller::Unknown Format in Homescontroller#top
↓
アクションに対するviewファイルがないエラー
**=> 考えられること**
viewファイルを作成してないのかもしれません。

（例２）
Action COntroller::Url Generation Error in Member::Addresses#index
No route matches
{action=> "edit" :controller=>"member/addresses#index" id=>nil}
missing required keys:[id]
↓
editで指定している箇所にidがないので、マッチできずに、URLの生成に失敗しました。
**=> 考えられること**
controllerのeditにidを指定してないかもしれません。

###Template is missing 

![エラー４.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664818/29bdcab6-a875-ad08-64eb-b7b504323317.png)

（例）
Template is missing
controller render:show
↓
テンプレートがありません。
**=> 考えられること**
controllerで設定しているアクションのviewが設定されていない場合があります。

###ArgumentError

![エラー５.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664818/2664e4f0-4256-1c24-7cee-6d28d6ad569f.png)

（例）
ArgumentError in Books#index
first argument in form cannot contain nil or be empty
↓
引数エラー
フォームの初めの引数はnilを含むことも空にすることもできないです。
**=> 考えられること**
例えば、絶対に値を入れないといけないログインユーザーの名前を編集画面で空白にしてアップデートする際などに、起こります。
そのため、①空白でアップデートする際にエラーメッセージを出力させる②バリデーションを書くことで解決できる場合があります。

###LoadError

![エラ−６.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/664818/c0697000-3da6-7ccd-367d-aa79dfc1f35d.png)

（例）
Load Error in Usercontroller#show
unable to autoload constant Relationship
↓
ロードエラー
定数のRelationshipを自動ロードできません。
**=> 考えられること**
私の場合、フォロー機能にてmodel の Relationshipのつづりが間違っていました。



こんな感じです。
また増えたら更新していきます。
URLの基礎知識をヒカリエの住所でわかりやすく例えてみた

URLとはホームページの置いてある場所とかファイル名とかを示す情報。
Uniform Resource Locator（ユニフォーム・リソース・ロケータ）の略。
それぞれの意味は下記の通り

- uniform（ユニフォーム）とは「一定の」
- resource（リソース）「資源」
- locator（ロケータ）は「位置座標」


## 今回使用するサンプル
今回はヒカリエの9FイベントホールのURLを使って説明します。
https://www.hikarie.jp/floormap/9F.html
この長い文字列は下記のように分裂できます。
![スクリーンショット 2020-11-03 11.47.32（3）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840696/cb322200-3167-0d20-1f71-e9578f7001f6.png)


## スキーム(https://)
どんな手段で通信するの?を決めるのがスキーム。://で表現される。
専門用語を使うと通信プロトコルというルールをここで決める。
上記の場合ではこのURLはhttpsというルールを使って通信することを表しています。
このルールをざっくり説明するとインターネットで暗号化通信を行いましょう！というもの。
これにより通信中に悪い人に盗み見しても内容がバレにくいというメリットがあります。
他にも

- ファイルにアクセスする時はfile:
- 電子メールの送信先を指定する時はmailto:

などが存在します。プロトコルの詳細は[こちら](https://qiita.com/miumi/items/8bda9389655c8aaa196c)

## ホスト(www.)
ネットワークに接続された機器やサーバーの名前のこと。
サーバーとはサービスや機能を提供する側のコンピュータのこと。
通信する際に、特定のホストを識別するために使います。
この「ホスト名」は、後ほど紹介する「ドメイン」とほとんど同じ意味です。
ホスト名もドメインも、どちらも自由に決めることができますが、ホスト名ではよく「www」が使われます。
「www」は「World Wide Web」の略で、「世界中に張り巡らされたクモの巣」を意味します。
ホスト名は、必要がなければ省略することもできます。


##ドメイン(www.hikarie.jp/)
ドメインは、IPアドレス(ネットの住所)を人間が読めるようにした文字列のことを指します。
インターネット上の住所となるので、同じドメインは1つしか存在しません。
ドメインでも「トップレベルドメイン」「セカンドレベルドメイン」「サードレベルドメイン」の3つに分けられます。

## トップレベルドメイン(.jp)
###ヒカリエの住所で例えると日本国東京都に該当します。
ドメインの中で一番右側に存在
「com」「net」「org」国や地域といった地理的な制限なしにどこからでも登録、利用できる
>gTLD（Generic Top Level Domain）

国や地域などの領域ごとに割り当てられるドメイン

>ccTLD（country code Top Level Domain）

の2種類が存在します。



## セカンドレベルドメイン(hikarie)
###ヒカリエの住所で例えると渋谷区渋谷に該当します。 
ドメインの中で2番目に属しているドメインです。
トップレベルドメインと違いセカンドレベル以降は所有者が自由な値を設定することができます。
サイトによってはcoなんかがつきます。

## サードレベルドメイン
今回は登場していないけど一応紹介。
セカンドレベルドメイン以下に属しているドメイン。
この文字列は、重複がない限り登録者が自由に設定できます。


##ディレクトリ(floormap/)
###ヒカリエの住所で例える2丁目21番1号を指してます。
サーバー内のフォルダ名と場所を指してます。
ホームページの場合、複数のページを持っていることがほとんどです。
ヒカリエは建物の中に沢山施設がありますよね?
その分ページが必要になりますよね?
そのためページを分類し、このURLがホームページ内のどの階層にあるのかを示しています。
ディレクトリが何層もある場合は、URLの中では一番左の、一番大きなディレクトリより後のものをサブディレクトリと呼びます。

## ファイル名(9F.html)
###ヒカリエの住所で例える9Fのヒカリエホールを指します。
ファイル名は、URLの末尾に配置される一番小さなファイルです。
音声認識の精度測定、単語誤り率(WER)と文字誤り率(CER)

# はじめに
これまでWatson Speech to Text (STT)の記事をいくつか書いてきました。
どうやって精度を測定するかや評価するかということをよく聞かれるので記事にすることにしました。
測定の計算式は以下の2つで、英語の場合は単語誤り率、日本語の場合は文字誤り率でよいと思います。

**単語誤り率（Word Error Rate; WER）＝（挿入単語数 ＋ 置換単語数 ＋ 削除単語数）／正解単語数**
**文字誤り率（Character Error Rate; CER）＝（挿入語数 ＋ 置換語数 ＋ 削除語数）／正解語数**

精度の評価については、こちらの論文によると
[話し言葉の音声認識の進展―議会の会議録作成から講演・講義の字幕付与へ 河原 達也](http://130.54.20.150/members/kawahara/paper/KAW-OUJ12.pdf)

* 人間が見て意味が分かるレベルは75%~
* 議会の議事録レベルは85%~
* アナウンサーの原稿読み上げレベル95%~

**音声認識の精度は、最低限75%は目指したいし、85%あれば胸をはれる認識精度だと思います。**

# 単語誤り率（Word Error Rate; WER）の測定ツール
英語の精度を測定する場合はこちらのオリジナルのソースコードをご参照下さい。
[WER-in-python](https://github.com/zszyellow/WER-in-python)

このツールを日本語へ対応するためにMeCabで形態素解析を行なった版がこちらです。
https://github.com/kchan7/WER-CER
**行毎に単語を比較するので、句点(。)読点(、)は不要なので削除して下さい。**
original_file.txtは正解とする文書、target_file.txtはSTTで書き起こした文書です。

```bash
git clone https://github.com/kchan7/WER-CER.git

pip install numpy
brew install mecab
pip install mecab-python3

python wer.py original_file.txt target_file.txt
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130492/b6736f1a-9962-55d4-6a80-336dcfc5583c.png)
単語誤り率(WER)は0.157142857142857
単語認識率=1.0-WER(0.157142857142857)=0.842857143
単語認識率は約84%となります。




# 文字誤り率(Character Error Rate; CER）の測定ツール
pythonのdifflibを使って差分を計測しています

```bash
python cer.py original_file.txt target_file.txt
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130492/4f05e4f7-f5b9-2aa6-d30d-8c374cacde98.png)

文字誤り率(CER)は0.11764705882352941
文字認識率=1.0-CER(0.11764705882352941)=0.882352941
文字認識率は約88%となります。


## 関連記事
[Watson Speech to Text (STT) 概要編](https://qiita.com/Kchan/items/9e24e5b0074bfe5f6bf2)
[Watson Speech to Text (STT) 基本操作curl編](https://qiita.com/Kchan/items/5b9231d3e93d75d4a021)
[Watson Speech to Text (STT) 挙動確認編](https://qiita.com/Kchan/items/1a81fa0be7da2e4595a8)
pathinfo関数を使って拡張子名やファイル名を取得する

##pathinfo関数とは？
pathinfo関数はファイルパスに関しての情報を配列で取得する関数。
index.phpというファイルのファイル名indexだけを取得できたり、拡張子であるphpだけを取得できる関数。
##pathinfo関数を使ってみる
```
1. <?php
2. $info = pathinfo('/fruits/melon/eat/index.php');
3. var_dump($info);
4. ?>
```
実行結果

```
array(4) {
  ["dirname"]=>
  string(17) "/fruits/melon/eat"
  ["basename"]=>
  string(9) "index.php"
  ["extension"]=>
  string(3) "php"
  ["filename"]=>
  string(5) "index"
}
```

・dirname	→ ファイル名を除くディレクトリパス
・basename	→ ディレクトリパスを除くファイル名
・extension	→ 引数で指定したファイル名の拡張子だけを抽出
・filename	→ 引数で指定したファイル名の拡張子を除いたファイル名を取得

##pathinfo関数を使う上での注意点
pathinfoを使ってファイルパスの情報を取得したのはいいけど、結果が文字化けしていて使い物にならない時は、`setlocale`関数を使ってから`pathinfo`を扱うことで改善される。

```
1. <?php
2. setlocale(LC_ALL, 'ja_JP.UTF-8');
3. $info = pathinfo('/fruits/melon/おいしい.php');
4. var_dump($info);
5. ?>
```

setlocale関数は設定が正しくできなかった場合falseを返すためそのあたりのチェック処理は必要だが、このようにすれば文字化けを改善できる可能性がある。
Material-UIの利用方法

ReactでMaterial-UIを利用する方法についてまとめます。

本記事の大部分はMaterial-UIコンポーネントのスタイルのカスタマイズ方法についての説明となっており、
特に[Hook API](#hook-apiの使い方)を利用したカスタマイズ方法を中心に説明を行います。
※簡単にですがHook API以外の方法についても[Material-UIのスタイル変更方法](#material-uiのスタイル変更方法)で触れています。

# 公式サイト
https://material-ui.com/

# インストール
```
$ npm install --save @material-ui/core @material-ui/icons @material-ui/system
```

# 利用方法
公式サイトにコンポーネントごとのサンプルがまとめられているので、利用したいコンポーネントに近いもののサンプルコードを流用しましょう。
例：https://material-ui.com/components/buttons/#contained-buttons

# Material-UIのスタイルをカスタマイズする
## Material-UIのスタイル変更方法
① Hook API<br>Material-UIのサンプルコードと同じ

```javascript
import { makeStyles, createStyles } from '@material-ui/core/styles';

const useStyles = makeStyles((theme: Theme) => createStyles({
  root: {
    backgroundColor: theme.color.red,
  },
}));

export default function MyComponent {
  const classes = useStyles();
  return <div className={classes.root} />;
}
```

② Styled component API<br>コンポーネントの"シンタックス"に直接スタイルを適用<br>コンポーネントとスタイルを直接紐づける<br>

```javascript
import React from 'react';
import styled from 'styled-components';
import Button from '@material-ui/core/Button';

const StyledButton = styled(Button)`
  background-color: #6772e5;
  color: #fff;
  box-shadow: 0 4px 6px rgba(50, 50, 93, 0.11), 0 1px 3px rgba(0, 0, 0, 0.08);
  padding: 7px 14px;
  &:hover {
    background-color: #5469d4;
  }
`;

export default function StyledComponents() {
  return (
    <div>
      <Button>Default</Button>
      <StyledButton>Customized</StyledButton>
    </div>
  );
}
```

③ Higher-order component API<br>スタイルを当てたコンポーネントを返すコンポーネント

```javascript
import React from 'react';
import { withStyles } from '@material-ui/core/styles';

const styles = {
  root: {
    backgroundColor: 'red',
  },
};

function MyComponent(props) {
  return <div className={props.classes.root} />;
}

export default withStyles(styles)(MyComponent);
```

## ピュアなCSSクラスでのスタイル変更ではダメなのか？
ピュアなCSSクラスでもスタイル変更は可能であるが、Material-UIコンポーネントはHTML構造が複雑になっているものが多く存在し、意図通りにCSSを適用するのが難しかったり、あるいは変更できない場合がある。
Hook APIやStyled component APIを利用することでピュアなCSSクラスでは変更できないスタイルを調整したり、変更を簡単に記述することができる。

## Hook APIの使い方
① インストール

```
$ npm install --save @material-ui/styles
```

② import

```javascript
import {createStyles, makeStyles} from "@material-ui/core/styles";
```

③ 関数定義

```javascript
const useStyles = makeStyles(() =>
  createStyles({
    "root": {
      padding: 0
    }
  })
);
```

### 記述ルール
1. json形式
2. キャメルケース
3. 数値はそのまま
4. クォーテーションで文字列を囲む

```javascript
const useStyles = makeStyles(() => (
    createStyles({
        "button": {
            borderColor: "#FFB549",
            color: "#FFB549",
            fontWeight: 600,
            marginBottom: "8px",
            "&:hover": {
                backgroundColor: "#FFB549",
                color: "#fff"
            }
        }
    })
));
```

## 適用方法
1. コンポーネント内で宣言
2. オブジェクト型として使える

```javascript
const MyButton = (props) => {
    const classes = useStyles();
    return (
        <Button
            className={classes.button}
        >
            {props.content}
        </Button>
    );
};
```
スマホにAWS Consoleアプリをインストールして課金状態などを確認する

AWSの課金情報はSlackに飛ばしているものの、こまめに確認することが必要だな、と実感する出来事がありました。
それは別記事で書くことにして・・・。

手持ちのSmartphone（iPhone）にAWS Consoleを入れたので、備忘として記載します。

## インストール〜認証

App Storeを起動して、**aws console**で検索。
出てきました。
**入手**を押してインストールします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/471456b8-93a6-22d6-f6ea-9231309845a4.png" alt="20201103_IMG_2941.png" width="60%">
インストールが完了したらアプリを起動します。
もちろん**Get started**です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/5253d21e-2c94-73d1-e23c-d7a10021b9d9.png" alt="20201103_IMG_2942.png" width="60%">
**Sign in to AWS**で先に進みます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/8ada6f2b-4633-51b3-0b3d-3946a96e9a13.png" alt="20201103_IMG_2943.png" width="60%">
iPhoneでFace IDが使えるからか、Face IDの使用有無を問われたので、**Yes, enable Face ID**を選択します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/5561c3ab-e29b-695e-6503-75e7de09b9eb.png" alt="20201103_IMG_2944.png" width="60%">
AWSで登録しているEmailアドレスを入力します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/0befae32-975f-baff-0cf2-97006404e15f.png" alt="20201103_IMG_2945.png" width="60%">
次はパスワードを入力します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/f63347fc-f17e-ef37-1a09-5a749eba87df.png" alt="20201103_IMG_2946.png" width="60%">
次はMFA code。
同一端末で別アプリ（Google Authenticator）起動するのめんどいな。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/986ae92e-2bb7-09f3-1cc7-a4423294c7a7.png" alt="20201103_IMG_2947.png" width="60%">
これでインストール〜認証は完了。

## ダッシュボード確認

ダッシュボードを確認します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/29168c51-5aef-ae11-005c-94b835824957.png" alt="20201103_IMG_2948.png" width="60%">
**CloudWatch alarms**、**AWS Health**、**Cost Management**が確認できる模様。
私のメインは**Cost Management**なんだけど、アラームも確認できるのね。

### Cost Management
10/28に爆上がりしてるなー。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/9430fa05-1dd5-e951-39c9-282c6ebd4a67.png" alt="20201103_IMG_2949.png" width="60%">

### CloudWatch alarms
アラーム設定見直さなきゃ。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/4edc19ae-c956-886c-526e-71e81c3f3098.png" alt="20201103_IMG_2950.png" width="60%">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/a3f340c1-e017-f283-565a-543df80f1d84.png" alt="20201103_IMG_2953.png" width="60%">


### AWS Health
何も設定してないや・・・。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/dcfd05cc-d68f-cd8e-f4ae-26ea93d7e03e.png" alt="20201103_IMG_2951.png" width="60%">

### リージョン切り替え
右上でリージョンを切り替えられます。
US East(N.Virginia)→Asia Pacific(Tokyo)へ。
CloudWatch alarmsとAWS Healthはそのリージョンの情報が、Cost Managementはリージョン関係なく表示されています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/244560/81d6ae15-f033-b91a-ce63-a222392eba9b.png" alt="20201103_IMG_2952.png" width="60%">

## まとめ
確認、大事。

Dashboardしか見てないですが、Servicesの方も見ておきます。
【Vue.js】リアクティブで動作しない時の対処法

#最初に
下記サイトのVue.jsのガイドに従って、進めていたところリアクティブが動作しなかったので調査開始。
[Vue.jsのガイド](https://jp.vuejs.org/v2/guide/)
#エラーメッセージ
    Live Reload is not possible without body or head tag.
どうやらボディタグかヘッドタグがないためリロードが動かないっぽい。
#解決策
    <body>
      <div id="app">
        {{ message }}
      </div>
  
      <script>
        var app = new Vue({
          el: '#app',
          data: {
            message: 'Vue.js 入門',
          },
        });
        </script>
    </body>
ボディタグで囲ってみたところ、無事リアクティブが動作した。
インターネットのzipファイルを解凍する

# インターネット上のzipファイルを解凍する
[気象庁](https://www.jma.go.jp/jma/kishou/know/amedas/kaisetsu.html)の[地域気象観測所一覧 [ZIP圧縮形式]](https://www.jma.go.jp/jma/kishou/know/amedas/ame_master.zip)を使用する。
zipファイルの中身は、`ame_master.csv`のみ

```python:python
import requests
import zipfile
import io
import pandas as pd

url = 'https://www.jma.go.jp/jma/kishou/know/amedas/ame_master.zip'
csv_name = 'ame_master.csv'
zip_res = requests.get(url)

with zipfile.ZipFile(io.BytesIO(zip_res.content)) as zip_f:
    csv_data = zip_f.read(csv_name)
    df = pd.read_csv(io.BytesIO(csv_data), encoding='cp932')
```
Firebaseでdeployした時にError: HTTP Error: 400, Billing account for projectの解決方法

# Reactで作ったアプリをFirebaseでdeployしようとしたら、、、

```
✔  functions: Finished running predeploy script.
i  functions: ensuring required API cloudfunctions.googleapis.com is enabled...
i  functions: ensuring required API cloudbuild.googleapis.com is enabled...
⚠  functions: missing required API cloudbuild.googleapis.com. Enabling now...
✔  functions: required API cloudfunctions.googleapis.com is enabled

Error: HTTP Error: 400, Billing account for project 'プロジェクト番号' is not found. Billing must be enabled for activation of service(s) 'cloudbuild.googleapis.com,containerregistry.googleapis.com' to proceed.
```
このようなdeployエラーに遭遇しました。今回はこのエラーの原因と解決方法を備忘録として残します。

# 解決方法
functionsにあるpackage.jsonファイルのnodeを10から8に変えます。

```
"engines": {
    "node": "10"
  },
```
こちらを以下のように変更する。

```
"engines": {
    "node": "8"
  },
```

# 原因
node 10を使用するには、クラウド機能の課金アカウントが必要です。おそらく、グーグルが無料のサービスを提供するのではなく、請求可能なアカウントに人々を移動させるためにこのような仕様にしているのだと思います。そのため、解決するにはpackage.jsonのnode 8に変更する必要があります。
何か間違いやご指摘があれば、よろしくお願い致します。
docker containerの挙動が変わらなくて困った

#はじめに
コードを変更しても、コンテナ内のアプリの挙動が変わらなくて、困ったことになっていました。その対処法についてのメモです。（キャッシュが効いていた系ではない）

#先に対処法
`Dockerfile`内でbuildした`app`(実行ファイル)を、`docker-compose.yml`で設定していたvolumesのローカルディレクトリ内にあった`app`が上書きしてました。
よって、余計なマウントをしないように変更することで解決しました。

#Problem
1. dockerコンテナで作成していたgolangのアプリがある
2. `Dockerfile`内でbuidしてappを起動するようにしている
3. 例えば、`main.go`を編集して保存する。
4. `docker-compose up -d --build` でアプリを再作成・起動する
5. あれ？変更されてないぞ！？（buildは実行されている）
6. `docker-compose build --no-cache` を試すが同じ...

ディレクトリ構成↓

```
myapp
 - docker-compose.yml
 - Go
    - Dockerfile
    - main.go
    - app
```

コード一部抜粋↓

```:Dockerfile
FROM golang:1.12.12-alpine3.9 AS build

ENV GOPATH $GOPATH:/go
ENV PATH $PATH:$GOPATH/bin
ENV GO111MODULE=on

hogehoge

COPY . /go/src/app
WORKDIR /go/src/app
RUN go build -o app main.go
CMD ["./app"]
```

```docker-compose.yml
version: '2'
services:
  app:
    build:
      context: ./Go
      dockerfile: Dockerfile
    volumes:
      - "./Go:/go/src/app"
    container_name: go_app
    ports:
      - "127.0.0.1:8080:8080"
    tty: true
    restart: always
```

#原因
原因は、試しに作成していたローカルのappでした。
コマンドの実行順序は、`RUN → volumes → CMD`となっており、コンテナ内でbuildした`app`をローカルの`app`で上書きしてしまっていました。


#対処法
##余計なファイルをマウントしない
今回であれば、セッション情報のみを永続化して保存する必要があったので、ファイルの共有はこの1ファイルで十分でした。

```docker-compose.yml
    volumes:
      - "./Go/sessions:/go/src/app/sessions"
```

##共有してはいけないファイルを除外する
開発時には、ソースコードをマウントして編集するほうが便利です。
よって、`app`だけは共有しないようにするのも一つの手だと思いました。
（[DockerでVolumeをマウントするとき一部を除外する方法](https://qiita.com/suin/items/e53eee56da23d476addc)）

#おわりに
そもそも、セッション情報の永続化を目的としてコード変更をゴニョゴニョやってるうちに、こんなことに。（本番サーバと開発サーバでの差分もあったり）
そのうち、データベース管理に移行します。（そのうち）
特定のポート番号を使用しているプロセスを絞り込んで終了させる方法

Reactで作業をするようになって次のようなメッセージに遭遇しました。コマンドを忘れてしまいそうなのでメモしました。

```Would you like to run the app on another port instead?(Y/n)
```

![スクリーンショット 2020-11-03 10.31.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294411/82318cf9-cec0-d333-1846-3185e14c3df0.png)

ここで```Y```を入力すると、別のポートでプロセスが起動されます。表示を確認するだけならポート番号が変わっても困らないこともありますが、サーバーとクライアントで割り当てポートが異なると動作確認できないことがあります。
そんな時には、特定のポート番号を占有しているプロセスを絞り込んで終了させることができます。
画像の例では、ポート3000で起動しようとして、競合が起きています。

# lsof -i:ポート番号


![スクリーンショット 2020-11-03 10.32.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294411/2afc53db-7701-1f48-f9a4-f344c3f6593c.png)

```lsof -i:3000```でプロセスを確認します。nodeがプロセスID（PID）```9165```で起動していることがわかります。


# kill -9 プロセスID

![スクリーンショット 2020-11-03 10.33.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294411/7b7c0ed5-cf94-faf2-bfe0-a9c93dc0283e.png)

```kill -9 9165```でプロセスを強制終了できます。
```lsof -i:3000```で確認すると、ポートが解放されていることが確認できます。これでポート3000を使用して作業ができます😊
![スクリーンショット 2020-11-03 10.58.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294411/8efe284a-049a-af0f-9a18-20a836be4897.png)
【JavaScript ~コールバック関数~】勉強メモ⑧

JavaScriptちゃんと学習中。
ほぼ自分の勉強メモです。
過度な期待はしないでください。

コールバック関数
-

- そもそも関数とは？

　関数とは、「いくつかの処理をまとめたもの」、定義の仕方として「function 関数名( )」と書き、
　その後ろの中括弧「{ }」の中にまとめたい処理を書くことで、関数を定義することが出来る。
　また、関数を呼び出すときに一緒に値を渡すことで、引数に追加情報が与えられ、
　関数の中でその値を利用することが出来る。
　[【JavaScript ~関数について~】勉強メモ③](https://qiita.com/k-yasuhiro/items/52e3ebb79bb0248a7c5e)を見ればもう少し理解出来るかも。
<br>

- 関数呼び出しの際に渡される引数の種類

　`function 関数名(引数);` の引数の部分には、数値はもちろん、文字列や
　trueとfalse（真偽値）が渡される。
　そして、JavaScriptでは引数に関数を渡すことが出来る。
　その引数に渡される関数を<b>コールバック関数</b>と呼ぶ。
<br>

- コールバック関数

　コールバック関数とは、引数に渡される関数のこと。

```.text:書き方
const 定数名 = (引数名) => {
  // 処理内容を記載
};

定数名（関数名）；
     // 関数(名)を引数（名）に渡す
```
<br>

　関数は、関数名の後ろに( )をつけると呼び出され、( )をつけなければ関数そのものを指す。
　よって、呼び出し方と渡し方で書き方が異なる点に注意。

```.js:例
const name = () => {
  console.log("Aki");
};

const call = (callback) => {
  console.log("元気？？");
  // 引数に渡したcallbackを呼び出し
  callback();
};

// 関数nameを引数に渡して関数callを実行
call(name);
```

```.text:出力結果
元気？？
Aki
```
<br>

コールバック関数を直接定義
-
- 引数で関数を定義した場合

　流れ、①引数で関数を定義して関数callを呼び出す
　　　　②関数をcallbackに代入

```.js:例
const call = (callback) => {
  console.log("元気？？");
  // 引数に渡したcallbackを呼び出し
  callback();
};

// 関数nameを引数に渡して関数callを実行
call(() => {
  console.log("Aki");
});
```

```.text:出力結果
元気？？
Aki
```
<br>

コールバック関数の引数
-
- 引数を渡す

```.js:例
const call = (callback) => {
  callback("Aki");
};

// 関数callの引数の中で引数を取る関数を追加
call((name) => {
  console.log(name);
});
```

```.text:出力結果
Aki
```
<br>


- 複数の引数を渡す

```.js:例
const call = (callback) => {
  callback("Aki", 20);
};

// 関数callの引数の中で2つの引数を取る関数を追加
call((name, age) => {
  console.log(`${name}は${age}歳です。`);
});
```

```.text:出力結果
Akiは２０歳です。
```
<br>

過去投稿記事
-
[【JavaScript ~変数・定数、if文・switch文~】勉強メモ](https://qiita.com/k-yasuhiro/items/1fb63f0881faf67bcb29)
[【JavaScript ~for文、配列、オブジェクトについて~】勉強メモ②](https://qiita.com/k-yasuhiro/items/6625942cc901feb6bfdf)
[【JavaScript ~関数について~】勉強メモ③](https://qiita.com/k-yasuhiro/items/52e3ebb79bb0248a7c5e)
[【JavaScript ~クラスやインスタンス、メソッドについて~】勉強メモ④](https://qiita.com/k-yasuhiro/items/52e3ebb79bb0248a7c5e)
[【JavaScript ~ファイルの分割について~】勉強メモ⑤](https://qiita.com/k-yasuhiro/items/209eda27bbc61ae961a1)
[【JavaScript 読み込み】勉強メモ⑥](https://qiita.com/k-yasuhiro/items/fe3fc2bd7b14fe4f3968)
[【JavaScript ~配列のメソッド~】勉強メモ⑦](https://qiita.com/k-yasuhiro/items/279b7bb59f112f917c51)
[【JavaScript ~HTMLを置き換え、ダイアログボックス~】勉強メモ⑨](https://qiita.com/k-yasuhiro/items/7a72bf38d78b141d6fac)
[【JavaScript ~イベント~】勉強メモ⑩](https://qiita.com/k-yasuhiro/items/b0d4037d0edaae38a495)
[【JavaScript ~イベント(入力内容を取得)とDateオブジェクト~】勉強メモ11](https://qiita.com/k-yasuhiro/items/fa586154cf7064eb7058)
[【JavaScript ~Mathオブジェクト~】勉強メモ12](https://qiita.com/k-yasuhiro/items/b931a15fb71322ee3cca)
AWS EC2インスタンスからのS3のバケットとElastiCasheのRedisへ疎通確認をする

# 目的

- 疎通確認方法のメモを記載する。あくまでも方法の一つを紹介するものであり本方法で確認できないもしくは不十分な場合もある。

# 詳細

1. S3疎通確認
    1. EC2インスタンスにssh接続して下記コマンドを実行する。当該インスタンスから接続可能なS3バケットの一覧が表示されるので一覧の中に接続先のバケット名があればインスタンスからバケットが見えていることになる。

        ```terminal
        $ aws s3 ls
        ```
1. ElastiCasheのRedis疎通確認
    1. EC2インスタンスにssh接続して下記コマンドを実行する。当該インスタンスからElastiChaseのRedisへログインすることができればインスタンスからElastiCasheのRedisに通信ができていることになる。

        ```terminal
        $ redis-cli -h ElastiCasheのエンドポイント
        ```
Field Service Lightning（現 Salesforce Field Service）の勉強メモ

記載中。。

###Field Serviceとは？
フィールドサービスとは、顧客からの問い合わせを受け、点検・修理・工事・配送など、現場に赴いて作業を行う業務のことです。
→その一連の業務の流れをサポートするのがSalesforce Field Serviceと理解しました。

###Field Service概要
※以降、Field Serviceは、FSと略します。
■登場人物
・顧客　→　問い合わせる人（例：エアコン購入したい）
・サポートセンター（例：一次受付、作業指示）
・ディスパッチャー（例：要員割り当てする人）
・サービス要員（例：ドライバーなど、実際に指示を受けて作業する人）
■利用の流れ
【機能紹介】Salesforce Field Service の『現場で使える』機能のご紹介より抜粋
https://www.youtube.com/watch?v=oUg7BQInhdQ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450298/0800d0cc-ef41-0aba-6785-3d068913f9fe.png)
■FSの代表的な機能
・FS固有のモバイル（サービス要員が主に現場で利用。）
　→オフライン操作（オンラインになったら自動で送信・どこでも作業可能）、チャット機能、バーコードスキャン（納入商品をバーコードでスキャンし、番号など保存）、地図（位置情報）や経路、電子サイン、サインした結果も含めて作業報告をPDF化
https://www.youtube.com/watch?v=w8qysEZNQQ8&feature=youtu.be

・スケジュール管理（サポートやディスパッチャーが利用）
　→自動でスキルなど鑑みてサービス要員に割り当て、地域（テリトリー）を定義、キャンセルや緊急度高いイベントが発生したら自動スケジューリング
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450298/465b5fba-f1ae-3bf2-d8a1-3b3e044cc057.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450298/0f7c4f83-68b3-03d5-6e91-32658a6ddb12.png)

■定義されているオブジェクト
https://trailhead.salesforce.com/ja/content/learn/modules/field_service_basics/field_service_basics_intro?trail_id=field_service
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450298/a28e70a2-4e2d-e0e7-7253-8bb6d10f49a7.png)


###参考サイト
https://qiita.com/xuwenzhen/items/cf7d6352520a9890f35b
https://qiita.com/shunkosa/items/a0f0470ccea9645c513f
https://base.terrasky.co.jp/articles/C2UYT
MPAndroidChart y軸に単位を表示する方法

MPAndroidChartでy軸に単位を表示する方法を紹介したいと思います。
あと自分が忘れないために記録用として。

１．まずは、ValueFormatterクラスを継承したクラス作成し、getFormattedValueメソッドをオーバーライドする。

    class MyYAxisValueFormatter : ValueFormatter() {
        override fun getFormattedValue(value: Float): String {
            //valueはy軸の目盛りの値。最後にString型にして、自分の付けたい単位足す。
            return value.toInt().toString() + "冊"
        }
    }

２．chartのaxisLeft.Formatterに先ほど作ったクラスのインスタンスを代入する。

     chart.axisLeft.valueFormatter = MyYAxisValueFormatter()


実行すると....
![Screenshot_1604367947.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498428/5d0e322d-3e7f-b595-6e4e-5c74ccb052a0.png)

y軸に単位が表示されました！！
JavaScript オブジェクトのメンバ名一覧に「Object.keys( obj )」を使うのは古いらしい

JavaScript メモ

オブジェクトのメンバ名一覧表示するコード

```JS
const obj = {
  hu: 0,
  ji: 1,
  ko: 2,
  lp: 3
}

// 少し前の記憶だと、オブジェクトのメンバ一覧は、こう書いていた、、、
for ( const k of Object.keys( obj ) )
    console.log( k )

// いつのまにか、以下のように in で短く書けるようになっていた、、、
for ( const k in obj )
    console.log( k )
```

すなわち、`Object.keys( obj )`の出番は無くなった、ということ。

いつのまに。

ってか、なんでわざわざ`Object.keys( obj )`を使っていたんだろう？？　for-in って、サポートされたのいつだっけ？

知らなかった。また、ひとつ賢くなった。

そういえば、`forEach()` も使わなくなったよね。

追記：
「いや、for-in のほうが古いよ」 というコメントをいただき、調べてみたら、たしかにそうでした。なんで、知らなかったの？＞俺

以上。「ちゃんと勉強しろよな＞俺」なエントリでした。



ちなみに、「`for` の `const` を省略しても動くじゃん。めんどいし、見にくいから、すっきり書こうよ」って思っているひとへ。

省略すると `var` 扱いになるので `k`のスコープが広くなり、危険度が上昇します（少しだけどね）。

```JS
for ( k in obj )
    console.log( k )

console.log( k ) // const ありならエラーになるが、省略すると、、、、見えちゃう
```

趣味で書き捨てる短いコードなら、まぁいいけど、、癖がついて仕事で使っちゃうとヤバイので、プロなひとは省略不可！ってことで。

省略したら、`const`扱いにしてほしかったなぁ、、、（ぼそり
Lobeで学習したモデルとTensorFlow Servingを使ってREST APIを作る。

下記の記事でLobeでの学習と学習したmodelをエクスポートしてPythonから利用する方法を説明しました。

[マイクロソフトが公開した機械学習モデルの訓練を容易にできる「Lobe」を試してみた。](https://qiita.com/tkinjo1/items/5cfe561b8765add0b7d0)
[Lobeで学習したモデルをPythonで利用する方法](https://qiita.com/tkinjo1/items/bbcb77fb0f4b8fe79a81)

今回はTensorFlow Servingと組み合わせてREST APIを作ってみたいと思います。

## TensorFlow Servingを動作させる下準備
Lobeでの学習とmodelのエクスポートは上に記載した記事をご覧ください。

いろいろと試したところTensorFlow Servingを動作させるにはmodelが入っているディレクトリの名前が数字である必要があるようです。
（この辺りはまだはっきりと掴めていないのでご存知の方がいれば教えていただけるとありがたいです。）

そのため、Lobeからエクスポートしたmodelディレクトリの中に数字を名前にしたディレクトリを作成し必要なファイルをコピーもしくは移動します。

## DockerでTensorFlow Servingを立ち上げる

TensorFlow Servingを使用する一番簡単な方法はDockerを使う方法です。
docker hubに公式のImageがアップされているのでこれを使用します。
[tensorflow/serving](https://hub.docker.com/r/tensorflow/serving)

今回使用するmodelのディレクトリは下記のようになっています。
TensorFlow Servingでは１というディレクトリにあるmodelを使用します。


```
── sample_model
    ├── 1 # 新たに作成しsample_model配下の必要なファイルをコピーもしくは移動する
    │   ├── saved_model.pb
    │   └── variables
    │       ├── variables.data-00000-of-00001
    │       └── variables.index
    ├── example
    │   ├── README.md
    │   ├── requirements.txt
    │   └── tf_example.py
    ├── saved_model.pb
    ├── signature.json
    └── variables
        ├── variables.data-00000-of-00001
        └── variables.index
```

dockerの起動は下記のように行います。

```
# docker run -t --rm -p 8501:8501 -v "`pwd`/sample_model:/models/sample_model" -e MODEL_NAME=sample_model tensorflow/serving
```

modelが読み込まれ正常に動作がスタートすると下記のメッセージが表示されます。

```
2020-11-03 01:04:51.142942: I tensorflow_serving/model_servers/server.cc:387] Exporting HTTP/REST API at:localhost:8501 ...
[evhttp_server.cc : 238] NET_LOG: Entering the event loop ...
```

このメッセージが出力されていればTensorFlow Servingの起動は完了です。

## RESTで画像分類を試す
ここまでの手順でRESTで画像分類を試す下準備は整いました。
TensorFlow Servingの動作を確認するためGETリクエストを送ってみます。

```
import requests
import json

url = 'http://localhost:8501/v1/models/sample_model'
res = requests.get(url)
print(json.loads(res.text))
```

下記のようなレスポンスが返ってくれば問題なく動作しています。

```
{'model_version_status': [{'version': '1', 'state': 'AVAILABLE', 'status': {'error_code': 'OK', 'error_message': ''}}]}
```

予測のリクエストはPOSTで行います。

```
import json

import cv2
import numpy as np
import requests

# 予測リクエストURL
url = 'http://localhost:8501/v1/models/sample_model:predict'

def predict(image_path):
    image = cv2.imread(image_path, cv2.IMREAD_COLOR)
    image = image.astype(np.float32) / 255
    image = image.tolist()

    headers = {"content-type": "application/json"}
    body = {"inputs": [image]}
    r = requests.post(url, data=json.dumps(body), headers=headers)
    r_json = json.loads(r.text)
    return r_json['outputs']['Prediction'][0]
```

predictに画像ファイルを引数として与えて予測結果を得ることができます。

```
predict_label = predict('画像ファイル')
print(predict_label)
```

ここまでで最低限動く環境は作ることができます。

コードはgitにアップしてありますのでよければご覧ください。
[lobe_py](https://github.com/tkinjo1985/lobe_py)

[FastAPIと組み合わせてREST APIを作ってみました。](https://github.com/tkinjo1985/lobe_fastapi)
Ubuntu20.04 で  Web Bluetooth

最近はデスクトップPC でも BLE を使う環境が揃ってきている。
今回は、Ubuntu20.04 で Web Bluetooth を使えるようにするお話。


# 環境
- Ubuntu 20.04 AMD64
- blueZ 5.53
- chrome バージョン: 86.0.4240.111（Official Build） （64 ビット）
- chromium バージョン: 86.0.4240.111（Official Build）snap （64 ビット）

# 対応状況

「Implementation Status」
https://github.com/WebBluetoothCG/web-bluetooth/blob/master/implementation-status.md

によると、

「Linux is partially implemented and not supported. The chrome://flags/#enable-experimental-web-platform-features flag must be enabled.」

とあります。
また、

「Linux: Requires Kernel 3.19+ and BlueZ 5.41+ installed. Read How to get Chrome Web Bluetooth working on Linux.
Note that Bluetooth daemon needs to run with experimental interfaces if BlueZ version is lower than 5.43: sudo /usr/sbin/bluetoothd -E」

ともありました。以前、Ubuntu16.04 で試したときはBlueZのバージョンが低くてうまくいきませんでした。さて、今回はうまくいくかな・・・

さて、
https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API
によると、firefox は未対応ということです。

# 設定作業

## 状況確認

本来は、ペアリングしようとすると以下のようなダイアログが出ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/817837d2-377e-3610-7bc2-8b716f820b8f.png)


このサイトは Windows10 や Android 上の Google Chrome で出ることを確認できていますが、Linux 上の Google Chrome では出てきませんでした。

## blueZ

まず、bluez を確認してみます。

```

$ sudo apt install bluez
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています                
状態情報を読み取っています... 完了
bluez はすでに最新バージョン (5.53-0ubuntu3) です。
以下のパッケージが自動でインストールされましたが、もう必要とされていません:
  libfprint-2-tod1 libpython2-stdlib libpython2.7-minimal libpython2.7-stdlib
  python2 python2-minimal python2.7 python2.7-minimal
これを削除するには 'sudo apt autoremove' を利用してください。
アップグレード: 0 個、新規インストール: 0 個、削除: 0 個、保留: 11 個。


```
既に入っていました。バージョンも 5.53 なので問題無さそうです。

## Google Chrome

次に、

chrome://flags
を開き、
 #enable-experimental-web-platform-features
をEnabledにします。



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/b7e34d53-5015-80b5-fb55-bc33a0217f6d.png)


以下のURLだと直接アクセスできて便利でした。

chrome://flags/#enable-experimental-web-platform-features

Google Chrome だとそれだけで Web Bluetooth が使えるようになりました。

## Chromium browser

Chromium でも試してみました。
chrome://flags/#enable-experimental-web-platform-features
にアクセスして上記と同じように Enable にします。ブラウザ再起動後、試してみるとこちらもOKになりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/efcad104-4bd9-06fe-cc3f-647e5ddf96d4.png)
GoogleAPISDKのサイズが原因でgit pushに失敗した場合の対処方法

<h2>発生した問題</h2>
<p>GitHubで公開するために、iOSの制作物をgit pushしようとしたところで問題が発生しました。発生している問題は、下記のリンクで発生していた問題と全く同じでした。<br /><a href="https://qiita.com/kanaya/items/ad52f25da32cb5aa19e6">GitHub に100MB超のファイルを置く -- git push に失敗してからの対処方法</a><br />GooGleMapsとGoogleAdsのAPIを使用するためのSDKファイルのサイズが問題でエラーになってしまいました。</p>
<blockquote>remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.</blockquote>


<h2>解決法を試すも、改善せず</h2>
<p>リンクにあった解決法を試しましたが、手順通りに行っても改善しませんでした。</p>

<h2>解決した方法</h2>
<p>私は手順書にある、非推奨の「手動ダウンロード」を行ってSDKをインポートしていました。そこで、下記のリンクを参考に推奨のCocoaPodsを使用してインポートし直し、ダウンロードしたファイルは削除しました。<br /><a href="https://developers.google.com/maps/documentation/ios-sdk/start?hl=ja">Maps SDK for iOS>Guides>Getting Started</a><br /><a href="https://developers.google.com/ad-manager/mobile-ads-sdk/ios/quick-start?hl=ja">Mobile Ads SDK>iOS>Get Started</a></p>

<h2>無事git pushが完了</h2>
<p>推奨のCocoaPodsを使用せずに非推奨の手動ダウンロードを使用したのは初歩的なミスかと思いますが、自分なりに解決したのでご参考に対処法を載せてみました。何かのお役に立てれば幸いです。</p>
windows 10 tips タスクバーにピン留めしたアイコンからでも右クリック2回で管理者権限で起動できる

#ピン留めしたアイコンを2回右クリック
![click2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126919/e29eaed4-2138-c19e-6271-747edc30684c.png)

もちろん管理者権限で起動できるものやユーザーに権限がある場合ですが。。。
写真はPowershell_ise(x86)です。
<ol>
<li>ピン留めしたアイコンを右クリックします。
<li>するとプログラム名とタスクバーからピン留めを外すが出てきます。（これ以外にも出てくるときがあります）
<li>つぎに本体のアイコンを再び右クリックします。
<li>すると、管理者として実行が出てきます。
<li>クリックすると管理者権限で起動できます。
</ol>

#ピン留めしたアイコンを常に管理者権限で起動することが設定できます
上記の図の下の方にプロパティがあります。
ここをクリックすると、デスクトップのアイコン同様、管理者権限として起動等が選択できます。

#理由
このタスクバーのアイコンはフォルダみたいなところに入っています。
いうなればデスクトップにフォルダを作り、そのなかにショートカットのアイコンを入れている、というやり方をしていて、その
フォルダがたまたまタスクバーということです。
##ピン留めには履歴機能が付加される
このため、タスクバーのショートカットアイコンも、同じことができるということです。
でもちょっと違います。例えばノートパッドのアイコンを登録してしばらく使ってみます。
![click3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126919/fcd078ad-cd28-4638-2eb4-6a8dabab6da0.png)
すると、左クリックでメモ帳が起動できるだけでなく、右クリックで履歴が出ます。
さらによく使うものをピン留めできます。
またメモ帳の場合編集したものが出てきます。
このためtxtではなくvbsやiniも出てきます。
面白いのはこうしておくと起動ではなく編集になるということです。
多分、クリックするときにvbsに関連付けられているWscriptがあるとすると
`Wscript.exe ...vbs`
ではなく
`notepad.exe ...vbs`
という意味に変わっているようです。
##ピン留めのノートパッドのアイコンの右クリックはエクスプローラの編集の機能にあたっている
これはExplorerでもそうです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126919/592497ed-d135-843b-e162-97158822195f.png)
今、Windows10でexplorerを開き、vbsを選択しています。
すると、開く、下に、編集がでてきます。
編集をクリックするとメモ帳によってVBSが開き、編集することができます。しかし、VBSが起動することはありません。
以前は「アイコンを右クリック>>編集を選択」が主流でしたが、たまに間違うので、初心者にはこの方法を勧めています。
エクスプローラの機能がタスクバーにピン留めしたアイコンの履歴機能にあるようです。
ただ、エクスプローラーの履歴は、定期的に保存された当該データ（ファイル）の過去の分の意味です。
企業でバックアップされている場合か、Onedrive等を除いて、個人で使うには今一つな機能です。上記で述べてきた「メモ帳で最近編集したテキストやini、VBSのコードという意味ではないのでご注意ください」
ESXi6.7 7.0　USBネットアダプタの増設

簡単にメモするものです。

下記のリンクでドライブをDWして増設できます。
https://flings.vmware.com/usb-network-native-driver-for-esxi

先ずネットワークアダプタのチップセットを確認する。
リストに入っているものはサポートしている。
次はドライブをダウンロードしてESXiホストにアップロードする。
ドライブをインストールすると、ESXiホストは再起動が必要なことを注意
森田賢二さんのJavascript練習問題集を解いてみた Act.1

この記事はエンジニア芸人の森田賢二さんが作成した[Javascript練習問題集](https://gist.github.com/kenmori/1961ce0140dc3307a0e641c8dde6701d)を解く過程における気付きや学びを記録したものである。

問１
[Object.assign](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)について問うた問題。

```
Object.assign(target, ...sources)

// 別解
const c = {...a, ...b}
// このようにスプレッド構文を使うことによっても、マージ可能。どちらもshallow copyである。
```

### To Do
・ 戻り値はtargetオブジェクトってどういうことかを理解する
・ prototypeとは何かを理解する

ではまた〜
第七回Try English LT! for engineers 発表資料まとめ

# Try English LT! for engineersとは？

エンジニア向けの英語のみのLTイベントを行なっています！
発表テーマはエンジニア向けの内容であれば自由です。
英語でLTをやるとなると、中々ハードルが高く、 参加者が海外の人ばかりだと、なおさら難しく感じると思います。
ちょっと自信はないけど、 英語のLTにチャレンジしてみたいという人にぜひ発表してもらいたいです。
英語が好きなエンジニア同士で交流できるイベント運営ができればと思っています。

第七回イベントページ
https://try-english-lt.connpass.com/event/191090/

## 発表資料
・Nobu 
The story of creating a bubble tea map with React and TypeScript
ReactとTypeScriptでタピオカマップを作った話
https://docs.google.com/presentation/d/1gMzCPNT5gTx4ka0R25G6MstxSgXWukhdbkRcyHReXGs/edit#slide=id.p

・にし さん
Developing 'FLAPTALK' by Firebase
Firebaseを使ってFLAPTALKを開発した話
https://speakerdeck.com/takec24/developing-flaptalk-by-firebase

・nayokoNayoko さん
Tips for Online English Lesson
オンライン英語学習のtips
https://speakerdeck.com/naoko3in4/tips-for-online-english-lesson

・みのる さん
Making Simple IoT Bot with Azure, Obniz, Ngrok
シンプルな IoT Bot を作ってみた with Azure, Obniz, Ngrok
https://www.slideshare.net/MinoruIto3/making-iot-line-bot-with-azure-obniz-ngrok?ref=https://try-english-lt.connpass.com/event/191090/presentation/

・坂本 さん
A loose move abroad to Estonia, even with a TOEIC score of 500.
TOEIC500点でもできる、エストニアでのゆるい海外移住
https://www.beautiful.ai/player/-ML2M6-wse-wOm4GhxDE/20201101_LT

・かきそふと さん
Accounting for engineers
エンジニアの会計
https://kakisoft.github.io/slide/accounting-for-engineers/export/index#/

・Steve Kasuya さん
Multi-factor authentication mechanism for my DIY smart lock
SuicaとQRコードの2段階認証スマートロック作ってみた
https://docs.google.com/presentation/d/1FBlhmc9KgZoF7sohwLJfHedVoo3kS8eR8QucI-sA-k4/edit#slide=id.ga697998844_0_90

・szskr さん
Build Dynamic! Intro to Linkers and Loaders
https://drive.google.com/file/d/1P35YM65NJLSj1Ry98Cz7zh6mOueVyfxf/view?usp=sharing
2020.11.03~ Node.jsで調べたこと

Node.js v12.18.0

# 2020.11.03(火)
## 相対パスを絶対パスに変換
`path.resolve()`を使う

> The path.resolve() method resolves a sequence of paths or path segments into an absolute path.
> [Path | Node.js v12.19.0 Documentation](https://nodejs.org/docs/latest-v12.x/api/path.html#path_path_resolve_paths)

> ```javascript
const path = require('path');
let absolutePath = path.resolve('./test.txt');
// => /Users/.../test.txt　（絶対パス）
```
> [[NodeJS]相対パスを絶対パスに変換する方法 | YongJin Kim's Blog](https://yongjinkim.com/nodejs%E7%9B%B8%E5%AF%BE%E3%83%91%E3%82%B9%E3%82%92%E7%B5%B6%E5%AF%BE%E3%83%91%E3%82%B9%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/)


## 共通処理を別ファイルでモジュール化

> ```javascript
module.exports.add = function (v1, v2) {
    return v1 + v2;  
};
module.exports.minus = function (v1, v2) {
    return v1 - v2;  
};
```
> [[NodeJS] モジュール定義について学ぶ - YoheiM .NET](https://www.yoheim.net/blog.php?q=20150101)

### 共通処理内で再帰的に自分自身を参照する場合

`module.exports.function名()`で参照できる 

```javascript
module.exports.readdirRecursively = (dir, files = []) => {
    const dirents = fs.readdirSync(dir, { withFileTypes: true });
    const dirs = [];
    for (const dirent of dirents) {
        if (dirent.isDirectory()) dirs.push(`${dir}/${dirent.name}`);
        if (dirent.isFile()) files.push(`${dir}/${dirent.name}`);
    }
    for (const d of dirs) {
        // 参考：https://qiita.com/potyosh/items/b37a172219d16ce09435
        files = module.exports.readdirRecursively(d, files);
    }
    return files;
}
```

[node.jsでexportsした関数を定義したファイルから呼び出す - Qiita](https://qiita.com/potyosh/items/b37a172219d16ce09435)


## メインプロセスとレンダラープロセスの通信

>```render_process.js
// レンダラープロセスでやりとりするipcRenderer
const {ipcRenderer} = require('electron');
// synchronous-messageチャンネルで文字列"ping"を同期通信で送信、戻り値を取得
var ret = ipcRenderer.sendSync('synchronous-message', 'ping');
// 戻り値"pong"が出力される
console.log(ret);
```

>```main_process.js
// メインプロセスでやりとりするipcMain
const {ipcMain} = require('electron');
//synchronous-messageチャンネルの受信処理
ipcMain.on('synchronous-message', (event, arg) => {
  // "ping"が出力される
  console.log(arg)
  // 呼び出し元の戻り値に文字列"pong"を設定
  event.returnValue = 'pong'
})
```
>[【Electron連載】第４回 基本編－メイン／レンダラープロセスの話 - Qiita](https://qiita.com/nullpointer_t/items/83cc14225b677f0d72fa#%E5%90%8C%E6%9C%9F%E9%80%9A%E4%BF%A1)
> [ipcRenderer | Electron](https://www.electronjs.org/docs/api/ipc-renderer#ipcrenderersendsyncchannel-args)
> [ipcMain | Electron](https://www.electronjs.org/docs/api/ipc-main#ipcmainonchannel-listener)
アクションにルートを割り当てる設定とは・・・

超初心者の私が・・・laravelの参考書で躓いた話です。



まず、Laravelの青本第2版で

アクションにルートを割り当てる設定が必要です

とありました。

よくわからなかったので、その下に書いてあるコードをとりあえず書いてみました。

Route::get('hello','HelloController@index');


はい・・・ターゲットが存在しません
エラーで表示されませんでした


ここでアクションにルートを割り当てるとは何か、調べてもなかなか出てきません。
今思えば、初級の中の初級で説明するまでもない事でしょう。

HelloController@index（あくしょん）に
App\Http\Controllers\ルートを割り当てる。

Route::get('hello','App\Http\Controllers\HelloController@index');

そうするとこうなりますね、なるほど・・・。


これで解決できました。
初心者過ぎてルートをどこに書けば良いのかわからなかった、
というお話でした。
モデルの複数名定義を行うNameError (uninitialized constant User::Reserf):

##環境
MacBook Air2020
Ruby2.7.1
Rails6.0.3.4
MySQL

##はじめに
reserveモデルの複数形定義が、自動でreserfになる模様
エラーメッセージにて発覚しました。

```
NameError (uninitialized constant User::Reserf):
```
##解決法
以下のように、複数形を定義することで解決できました。

```config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.irregular "reserve", "reserves"
end
```