OmniAICreator/Qiita-1.07M huggingface dataset:
Airflowで.sqlファイルにパラメータを設定する

# 背景

- Airflowで.sqlファイルにパラメータを使ってSQLを動的に変更させたい
- BigqueryOperatorのquery_params引数を使った方法や、DAGのparams引数を使ったやり方がある
- BigqueryOperatorのquery_params引数を使ったやり方([参照](https://qiita.com/drafts/2ace2947ba5b498d508d/edit#bigqueryoperator%E3%81%AEquery_params%E3%81%AE%E6%96%B9%E6%B3%95%E5%8F%82%E7%85%A7))がやや冗長に感じたので、今回はDAGのparams引数を使ったやり方を紹介する

# 実装

参考

https://airflow.apache.org/docs/apache-airflow/stable/tutorial.html#templating-with-jinja


```py:dag.py
from airflow.models import DAG
from airflow.utils.dates import days_ago
from airflow.contrib.operators.bigquery_operator import BigQueryOperator


default_args = {
    'owner': 'airflow',
    'start_date': days_ago(1),
    'project_id': 'some_project',
}

with DAG(
    dag_id='dag',
    default_args=default_args,
    schedule_interval=None,
    catchup=False,
    params={'project_id': 'some_project'} # これ
) as dag:

    BigQueryOperator(
        task_id='task',
        sql='query.sql',
        destination_dataset_table="hoge.fuga.hige",
        write_disposition='WRITE_TRUNCATE',
        allow_large_results=True,
        use_legacy_sql=False,
    )
```


```sql:query.sql
SELECT * FROM `{{ params.project_id }}.fuga.hige`
```

# BigqueryOperatorのquery_paramsの方法参照

https://stackoverflow.com/questions/56287061/how-to-pass-query-parameter-to-sql-file-using-bigquery-operator
Dockerfileの書き方

Dockerfileの書き方をよく忘れるのでメモ。

```dockerfile
# ベースのイメージを指定
FROM centos:7

# 環境変数を指定
ENV export http_proxy="http://hoge:0000"
ENV export https_proxy="http://hoge:0000"

# コマンドを実行
RUN yum install -y java-1.8.0-openjdk-devel

# ユーザーを作成
ARG UID=1000
RUN useradd -m -u ${UID} test

# ユーザーを切り替える
USER ${UID}

# ファイルをコピー
COPY src/image.png /var/tmp

# ファイルをコピー&解凍(圧縮ファイルの場合)
ADD src/text.gz /var/tmp

# 作業ディレクトリを切り替え
WORKDIR /var/tmp
```

↓公式のドキュメント
https://docs.docker.jp/engine/reference/builder.html
Zabbix5.4の環境構築（DB接続躓き編）

Zabbixの案件が多いことに着眼点を置いてZabbixの環境構築を行ってきた。

PCスペック
AMD Ryzen 4500U
メモリ16GB
SSD256+512GB

Linuxディストリビューション：Ubuntu20.04
Zabbix5.0とZabbix5.4
Zabbix-agent
apache2
SQLは色々試したMariaDB,Mysql,Postgre

各Webサイトを参考にしつつ、作業を進めていた。
当初はZabbix5.0での導入を進めていたが途中で5.4にアップグレード。

すると参考記事の進め方とは違う問題点がいくつか。
途中でzcatの構文があるがそこで指定されているファイルが存在しないため実行が出来ない。（No such on Directory
みたいな表示）

インストールの方法が違うと別部分に出来るのか？と思いつつgzファイルを探すと/usr/share/doc/zabbix-sql-scriptsの/mysqlと/postgresql階下にそれぞれgzファイルを発見。

これを実行していく。

後、色んなサイトよりもZabbixドキュメントがあり、そちらを参考にした方が色々見つけやすいと感じた。
Grafana と Nginx の連携

Nginx のリバースプロキシーで Grafana につなぐ方法です。

Nginx の設定

```text:/etc/nginx/sites-available/default
server {
        listen 80 default_server;
        listen [::]:80 default_server;

（省略）

        location /grafana/ {
                proxy_pass http://localhost:3000/;
        }

}
```

Nginx の設定の確認

```text
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
```

Grafana の設定

```text:/etc/grafana/grafana.ini
（省略）
domain = localhost
root_url = %(protocol)s://%(domain)s/grafana/
（省略）
```

サーバーの再起動

```bash
sudo systemctl restart grafana-server
sudo systemctl restart nginx
```
next/routerのisReadyフラグ

# isReadyとは

`next.js`のRouterオブジェクトのisReady属性は、routerが完全に[準備](https://nextjs.org/docs/api-reference/next/router)できているかどうかを表すフラグである。

```
isReady: boolean - Whether the router fields are updated client-side and ready for use. 
Should only be used inside of useEffect methods and not for conditionally rendering on the server.
```

- ルータの各項目がクライアント側で更新されてから初めて使えるようになる
- useEffect（やcomponentDidMount）でしか使わない。サーバ側レンダリングでそれの利用を避けるべき。

- 参照の仕方

```js
import { useRouter } from 'next/router';

export HogePage = () => {
  const router = useRouter();
  console.log('router:', router);
  console.log('router.isReady:', router.isReady);
  console.log('router.query?.a:', router.query?.a);
  return (
    <div>test</div>
 );
};
```

# isReadyの値

ケースバイケースで状況によってisReadyの値が変わる。


#### SSRページの場合

isReadyの値は常にtrueである。

#### CSR/SSGページの場合

| 初期ロード  | クエリパラメータ | isReady |
|:---------|------------:|------------:|
|   YES    |   あり | false  |
|   YES    |   なし | true  |
|   NO     |   あり | true  |
|   NO     |   なし | true  |

つまり、物理遷移やブラウザリロードで、かつクエリパラメータがある場合だけ、一瞬isReadyがfalseになることがある。
その時、クエリパラメータの値がもらえない。

# 利用時の注意点

初期ロード時にisReadyがfalseであれば、router.query?.aのようにクエリパラメータの値がundefinedになってしまうので、画面レンダリングでそれを直接に使わない。
遅延処理を入れてisReadyがtrueになったら、それを再描画するようにしないといけない。

- RouterReadyWaiterのようなHOCを作ったほうが良さそう
    - useEffectまたはcomponentDidUpdateでisReadyを監視して
        - falseなら、childrenを描画しない
        - trueの場合、childrenを描画する
    - childrenとしてReactコンポーネントではRouterが準備できている前提になるので、実装がラク。

# なぜこうなるのか

一瞬next/routerのバグではないかと思ったが、よく考えたらそうではない。

- SSRでない限り、事前にサーバ側で事前に用意されたプリレンダリングHTMLコンテンツをそのままクライアントに返す
    - つまり、クエリパラメータはプリレンダリングに影響しない
    - プリレンダリング結果がテンプレートと見なすなら、それを基づく更なる描画処理はクライアントでやる
    - hydration直後はrouterのクエリパラメータ無視の状態なので、isReadyがfalseになるしかない
- SSRであれば、サーバ側でレンダリングするため、子いう問題はない

# 参考記事

- [useRouter/withRouter receive undefined on query in first render](https://stackoverflow.com/questions/61040790/userouter-withrouter-receive-undefined-on-query-in-first-render)
AthenaでELBログ解析

AthenaでELBログ解析する必要があったので方法をメモしておきます。

まず前提条件としてELBでアクセスログをS3に保存するよう設定しておく必要があリます。
また、Athenaで実効するクエリ結果をS3に保存する必要があるようなので、バケットを１つ用意しておきます。

# テーブル作成
次に、下記クエリを実行してテーブルを作成します
`<S3_Backet_URL>` には アクセスログが保存されているS3のURLを指定しますが
特定の期間だけでいいのであれば、年月のフォルダまでURLで指定します。
`s3://<S3バケット>/AWSLogs/<アカウントID>/elasticloadbalancing/ap-northeast-1/2021/07/`
全ログを対象とする場合はリージョンまで指定します。
`s3://<S3バケット>/AWSLogs/<アカウントID>/elasticloadbalancing/ap-northeast-1/`

```
CREATE EXTERNAL TABLE IF NOT EXISTS elb_logs (
            type string,
            time string,
            elb string,
            client_ip string,
            client_port int,
            target_ip string,
            target_port int,
            request_processing_time double,
            target_processing_time double,
            response_processing_time double,
            elb_status_code string,
            target_status_code string,
            received_bytes bigint,
            sent_bytes bigint,
            request_verb string,
            request_url string,
            request_proto string,
            user_agent string,
            ssl_cipher string,
            ssl_protocol string,
            target_group_arn string,
            trace_id string,
            domain_name string,
            chosen_cert_arn string,
            matched_rule_priority string,
            request_creation_time string,
            actions_executed string,
            redirect_url string,
            lambda_error_reason string,
            target_port_list string,
            target_status_code_list string,
            classification string,
            classification_reason string
            )
            ROW FORMAT SERDE 'org.apache.hadoop.hive.serde2.RegexSerDe'
            WITH SERDEPROPERTIES (
            'serialization.format' = '1',
            'input.regex' = 
        '([^ ]*) ([^ ]*) ([^ ]*) ([^ ]*):([0-9]*) ([^ ]*)[:-]([0-9]*) ([-.0-9]*) ([-.0-9]*) ([-.0-9]*) (|[-0-9]*) (-|[-0-9]*) ([-0-9]*) ([-0-9]*) \"([^ ]*) ([^ ]*) (- |[^ ]*)\" \"([^\"]*)\" ([A-Z0-9-]+) ([A-Za-z0-9.-]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^\"]*)\" ([-.0-9]*) ([^ ]*) \"([^\"]*)\" \"([^\"]*)\" \"([^ ]*)\" \"([^\s]+?)\" \"([^\s]+)\" \"([^ ]*)\" \"([^ ]*)\"')
            LOCATION '<S3_Backet_URL>';
```

DBを指定しないと、defaultデータベース内に作成されました。
あまり広範囲のログを対象にするとDB容量が多くなって料金が高くなります。

# SELECT実行

特定時間帯の分ごとのページ別アクセス数を取得してみました。

```
SELECT
  datetime, 
  request_url, 
  count(*) count
FROM
(
  SELECT 
    format_datetime(parse_datetime(time,'yyyy-MM-dd''T''HH:mm:ss.SSSSSS''Z') AT TIME ZONE 'Asia/Tokyo', 'YYYY-MM-dd HH:mm') datetime,
    request_url
  FROM elb_logs
  WHERE parse_datetime(time,'yyyy-MM-dd''T''HH:mm:ss.SSSSSS''Z') 
    BETWEEN parse_datetime('2021-07-30-01:40:00','yyyy-MM-dd-HH:mm:ss') 
    AND parse_datetime('2021-07-30-01:46:00','yyyy-MM-dd-HH:mm:ss') 
)
GROUP BY datetime, request_url
ORDER BY datetime;
```
shellの比較演算子(不等号)一覧

## 比較演算子(不等号)を覚えよう
不等号の日本語も英語も覚えられないから **「ほら、あの右側が大きいやつ、こういうの（指でくの字を作り相手側に向ける）」** とか恥ずかしいやり取りをしないといけない。
特にshellとか何やらで見かける`-gt`とかの比較演算子も何年たっても覚えられないので一覧にした。


| No | 数学記号 |Cなど|shell|意味|意味(英語)|
|-:|:-|:-|:-|:-|:-|
|  1| ＝（イコール）|==|-eq|前者は後者と同じ|**eq**ual|
|  2| ≠（ノットイコール）|!=|-ne|前者は後者と同じではない|**n**ot **e**qual|
|  3| ＞（大なり）|>|-gt|前者は後者より大きい|**g**reater **t**han|
|  4| ≧（大なりイコール）|>=|-ge|前者は後者以上|**g**reater than or **e**qual|
|  5| ＜（小なり）|<|-lt|前者は後者より小さい|**l**ess **t**han|
|  6| ≦（小なりイコール）|<=|-le|前者は後者以下|**l**ess than or **e**qual|

※「大なり」は「だいなり」、「小なり」は「しょうなり」と読む。
※lessって何？ファイルとか表示するコマンドじゃないの？と思ったけど英語では「少ない」という意味らしい。
※不等号って中学1年で習うらしい。あぁ、私って中1以下……じゃなくて私小なりイコール中1。

### 参考

* https://shellscript.sunone.me/if_and_test.html （shellのifとtest）
* https://www.kobetsu.co.jp/manabi-vitamin/subject/jhs-math/article-406/ （中学1年数学）
Reactを基本からまとめてみた【1】【React Hook (useState)①】

##はじめに
####学習するに至った経緯
2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。
入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、
卒業後、フロントエンドのエンジニアを目指す事に。
Reactの学習した事を言語化し、認識の深化による備忘録として記載。

##useStateとは

コンポーネントの中で変数を扱うことができる機能。
useStateを使うとFunctionalコンポーネント(関数コンポーネント)内で状態管理を行うことができる。

```sample.js
import React, { useState } from 'react'
const hoge = () => {
  // 下記のコードで`State`宣言されています。
  const [name, setName] = useState('')
}
```
###1. useStateを使ってState宣言

```javascript
const [name, setName] = useState('')
````

- name：任意の変数名を宣言ができる。<br>　※ ここではnameにしていますが、好きな名称を設定することができる。
- setName：State変数（name）を更新するための関数を宣言している。
- useState('')：引数でState変数の初期値を設定することができる。<br>　※例えば、useState('佐藤')みたいに設定することもできる。


###2. State変数を読み出す
関数コンポーネント内で下記のように呼び出すことができる。

```javascript
import React, { useState } from 'react'
const hoge = () => {
  const [name, setName] = useState('')

  return(
    //　下記のように使うことができます。
    <p>私の名前は{name}です</p>
  )
}
```

##3.State変数を更新する
useStateで宣言した変数を更新するための関数（ここではsetState）を使ってstateを更新する。

- handleName：この関数の中でstateを更新するsetNameが更新されている。
- onChange：このイベントリスナー内で実際にhandleNameを経由して値を更新している。

```javascript
import React, { useState } from 'react'

const hoge = () => { 
  const [name, setName] = useState('')

  const handleName = (e) => {
    setName(e.target.value)
  }

  return(
    <>
      <h1>{name}</h1>
      <input onChange={handleName}></input>
    </>
  )
}
export default hoge

```

##簡単なアプリの作成を通してuseStateの理解
###1. useStateの設定
useStateを利用するためには、useStateをimportする。
コンポーネント内で状態管理を行いたい変数をuseStateを使って宣言する。

```javascript
import React, { useState } from 'react';
```
countが変数でsetCountメソッドを使ってcountの値を変更することができる。
useStateの()で設定している0がcount変数の初期値。

```javascript
const [count, setCount] = useState(0);
```

```javascript
import React, { useState } from 'react';
function Count() {
    const [count, setCount] = useState(0);

    return (
        <div>
            <h1> Counter </h1>
            <h2>カウント: { count }</h2>
        </div>
  );
}
export default Count;
```
##2. countの値の更新

クリックしたらcountの値が更新できるようにボタンを追加し、onClickイベントを追加する。
onClickの中でsetCountメソッドを使ってcountの値を更新する。

```javascript
<button onClick={() => setCount(count + 1)}>+</button>
```
countを『1』増やすだけではなく『-1』に減らすボタンも追加。


```javascript
import React, { useState } from 'react';
function Count() {
  const [count, setCount] = useState(0);
  return (
      <div>
          <h1>Counter</h1>
          <h2>カウント: { count }</h2>
          <button onClick={() => setCount(count + 1)}>+</button>
          <button onClick={() => setCount(count - 1)}>-</button>
      </div>
  );
}

export default Count;
```
+ボタンをクリックすると1増え、-ボタンをクリックすると1減るカウンターを作成完了。
ボタンを押すとコンポーネントの内で行ったカウントの増減が即座にブラウザ上にも反映される。

onClickで実行する関数を外側に定義しても動作は変わりません。
このようにuseStateを利用するとFunctionalコンポーネント内で値を保持したり、更新したりすることができる。







##参考サイト
[初心者でもわかるReact Hook のuseStateを使い方]
(https://reffect.co.jp/react/react-hook-usestate-understand)
[【React hooks】噛み砕いて解説してみた~useState編~]
(https://zenn.dev/akino/articles/3642600975681f)
Google spreadsheet | GASで"カタカナ"を"ひらがな"に変換

#アンケートで振り仮名を"カタカナ"で取得したけど"ひらがな"に変換したかった。

参考にさせていただきました！

https://www.pre-practice.net/2017/12/blog-post.html

##結果

完成

```javascript:GAS
function set_values(input){
  let values = input;  
  let hiragana_result = ""
  for(var i = 0; i < values.length; i++){
    console.log(i);
    console.log(values[i][0]);
    console.log(values[i]);
    hiragana_result = hiragana_result + henkan(kana, hira, values[i][0]);
    console.log(hiragana_result);
  } 
  return hiragana_result;
}

let hira = ["あ", "い", "う", "え", "お", "か", "き", "く", "け", "こ", "さ", "し", "す", "せ", "そ", "た", "ち", "つ", "て", "と", "な", "に", "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ", "ま", "み", "む", "め", "も", "や", "ゆ", "よ", "ら", "り", "る", "れ", "ろ", "わ", "を", "ん", "が", "ぎ", "ぐ", "げ", "ご", "ざ", "じ", "ず", "ぜ", "ぞ", "だ", "ぢ", "づ", "で", "ど", "ば", "び", "ぶ", "べ", "ぼ", "ぱ", "ぴ", "ぷ", "ぺ", "ぽ", "ぁ", "ぃ", "ぅ", "ぇ", "ぉ", "ゃ", "ゅ", "ょ", "っ", "ゔ"];
let kana = ["ア", "イ", "ウ", "エ", "オ", "カ", "キ", "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "マ", "ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ヲ", "ン", "ガ", "ギ", "グ", "ゲ", "ゴ", "ザ", "ジ", "ズ", "ゼ", "ゾ", "ダ", "ヂ", "ヅ", "デ", "ド", "バ", "ビ", "ブ", "ベ", "ボ", "パ", "ピ", "プ", "ペ", "ポ", "ァ", "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ッ", "ヴ"];
//let hankana = ["ｱ", "ｲ", "ｳ", "ｴ", "ｵ", "ｶ", "ｷ", "ｸ", "ｹ", "ｺ", "ｻ", "ｼ", "ｽ", "ｾ", "ｿ", "ﾀ", "ﾁ", "ﾂ", "ﾃ", "ﾄ", "ﾅ", "ﾆ", "ﾇ", "ﾈ", "ﾉ", "ﾊ", "ﾋ", "ﾌ", "ﾍ", "ﾎ", "ﾏ", "ﾐ", "ﾑ", "ﾒ", "ﾓ", "ﾔ", "ﾕ", "ﾖ", "ﾗ", "ﾘ", "ﾙ", "ﾚ", "ﾛ", "ﾜ", "ｦ", "ﾝ", "ｶﾞ", "ｷﾞ", "ｸﾞ", "ｹﾞ", "ｺﾞ", "ｻﾞ", "ｼﾞ", "ｽﾞ", "ｾﾞ", "ｿﾞ", "ﾀﾞ", "ﾁﾞ", "ﾂﾞ", "ﾃﾞ", "ﾄﾞ", "ﾊﾞ", "ﾋﾞ", "ﾌﾞ", "ﾍﾞ", "ﾎﾞ", "ﾊﾟ", "ﾋﾟ", "ﾌﾟ", "ﾍﾟ", "ﾎﾟ", "ｧ", "ｨ", "ｩ", "ｪ", "ｫ", "ｬ", "ｭ", "ｮ", "ｯ", "ｳﾞ"];

function henkan(input, output, text) {
  let result = "";
  let array = [];
  for (let i = 0; i < text.length; i++) {
    if (text[i] == "ﾞ" || text[i] == "ﾟ") {
      array[array.length - 1] = (text[i - 1] + text[i]);
    } else {
      array.push(text[i]);
    }
  }
  for (let j = 0; j < array.length; j++) {
    let index = input.indexOf(array[j]);
    if (index == -1) {
      result = result + array[j];
    } else {
      result = result + output[index];
    }
  }
  return result;
}
```
データサイエンス100本ノック（構造化データ加工編）をやってみた part2(P-017~P-022)

　この記事はデータサイエンスを勉強しながら、データサイエンス協会が提供する__データサイエンス100本ノック（構造化データ加工編）__を解く過程を自分用にまとめたものです。

|　チャプター　|　リンク　|　チャプター　|　リンク　|
|:------:|:----:|:------:|:---:|
|　P-001~P-016　|　[part1](https://qiita.com/pyon/items/c208e585e0cdfabc58bd)　|　P-052~P-062　|　[part6](https://qiita.com/pyon/items/5c5926241b8e6da978f3)　|
|　P-017~P-022　|　[part2](https://qiita.com/pyon/items/73bf9e8fe75f807aeea8)　|　P-063~P-068　|　[part7](https://qiita.com/pyon/items/ac8c7fb9110dbba061f5)　|
|　P-023~P-031　|　[part3](https://qiita.com/pyon/items/948ca309c7985fa2e855)　|　P-069~P-078　|　[part8](https://qiita.com/pyon/items/60daed73e5ff670e209d)　|
|　P-032~P-039　|　[part4](https://qiita.com/pyon/items/e7146bbb728979430814)　|　P-079~P-088　|　[part9](https://qiita.com/pyon/items/911e3d236fad8df13f44)　|
|　P-040~P-051　|　[part5](https://qiita.com/pyon/items/dac17fb162c99769978b)　| P-089~P-100 |　[part10](https://qiita.com/pyon/items/70c1e222914c658a480c)　|

+ ソート　`sort_values()`
+ データフレームの結合　`pd.concat()`
+ 順位付け　`rank()`
+ 長さや要素数を取得`len()`
+ ユニークな要素を抽出　`unique()`

# P-017 `sort_values()`
>P-17: 顧客データフレーム（df_customer）を生年月日（birth_day）で高齢順にソートし、先頭10件を全項目表示せよ。

```python:p017.py
df_customer.sort_values('birth_day').head(10)
```
　`sort_values('ソートしたい列名')`：引数で指定した列をソートして昇順で返す。(DataFrame型)

# P-018
>P-18: 顧客データフレーム（df_customer）を生年月日（birth_day）で若い順にソートし、先頭10件を全項目表示せよ。

```python:p018.py
df_customer.sort_values('birth_day', ascending=False).head(10)
```
　　降順で返したい場合は引数に`ascending=Flase`を指定する。

# P-019 `pd.concat()`, `rank()`
>P-19: レシート明細データフレーム（df_receipt）に対し、1件あたりの売上金額（amount）が高い順にランクを付与し、先頭10件を抽出せよ。項目は顧客ID（customer_id）、売上金額（amount）、付与したランクを表示させること。なお、売上金額（amount）が等しい場合は同一順位を付与するものとする。

```python:p019.py
df_tmp = pd.concat([df_receipt[["customer_id", "amount"]],
　　                   df_receipt["amount"].rank(method='min',ascending=False)], axis=1)
df_tmp.columns = ['customer_id', 'amount', 'ranking']
df_tmp.sort_values('ranking', ascending=True).head(10)
```
　`pd.concat([df1, df2,])`：第一引数に連結したいdfをリストで指定する。(問題では2つだが、それ以上でも可能)
　デフォルトでは縦に結合。（`axis=0`）横に結合したい場合は第二引数に`axis=1`を指定する。

　`rank()`：データを並び替えずに各要素の順位を返す。デフォルトは列、昇順。
　行に対して順位付けする場合は引数に`axis=1`を指定する。
　降順に順位付けする場合は引数に`ascending=False`を指定する。

　引数`method`：同一値に対する処理
　`method='average'`：平均順位
　`method='min'`：最小値が順位
　`method='first'`：登場順に順位付け

# P-020
>P-020: レシート明細データフレーム（df_receipt）に対し、1件あたりの売上金額（amount）が高い順にランクを付与し、先頭10件を抽出せよ。項目は顧客ID（customer_id）、売上金額（amount）、付与したランクを表示させること。なお、売上金額（amount）が等しい場合でも別順位を付与すること。

```python:p020.py
df_tmp = pd.concat([df_receipt[["customer_id", "amount"]],
                   df_receipt['amount'].rank(ascending=False, method='first')], axis=1)
df_tmp.columns = ["customer_id", "amount", "ranking"]
df_tmp.sort_values('ranking').head(10)
```

# P-021 `len()`
>P-021: レシート明細データフレーム（df_receipt）に対し、件数をカウントせよ。

```python:p021.py
len(df_receipt)
```

　`len()`：引数に指定したオブジェクトの長さや要素の数を取得する。

# P-022 `unique()`
>P-022: レシート明細データフレーム（df_receipt）の顧客ID（customer_id）に対し、ユニーク件数をカウントせよ。

```python:p022.py
len(df_receipt['customer_id'].unique())
```
　`unique()`：ユニークな要素の値(重複していない要素の値)のリストを返す。一次元のNumPy配列ndarray型。
TSC_DEADLINE disabled due to Errata

古いサーバに debian buster 入れたら起動時に一瞬だけエラーが表示される。

```
[Firmware Bug]: TSC_DEADLINE disabled due to Errata: please update microcode to version: 0x22 (or later)
```

TSC_DEADLINE が止まったところで致命的ではないという話も見かけたが、気持ち悪いので言われた通りに。

```
vi /etc/apt/source.list
# contrib non-free を追記する
deb http://deb.debian.org/debian/ buster main contrib non-free
deb-src http://deb.debian.org/debian/ buster main contrib non-free

apt update
apt install intel-microcode
```
TypeScript+ReactでのuseEffect(転何知人T+Rその6)

# [前回](https://qiita.com/botanic7arc/items/9e1ec00496559142fd2c)までの状態

React HooksとuseStateについてについて学習した。

# 今回の目的

useEffectについて

# useEffect

ページをレンダリングした後に副作用(DOMの変更、APIとの通信など)を実行する際に使う。書式は以下の通り。

 useEffect(関数);

## 手順

[前回](https://qiita.com/botanic7arc/items/9e1ec00496559142fd2c)作成したindex.tsxに以下の内容を変更、追加する。

インポート部分は以下のように変更する。

``` tsx
import { useState,useEffect } from 'react';
``` 

関数コンポーネント内部に以下の記述を追加する。

``` tsx
  useEffect(() => {
    document.title = `input is ${str} !`;
  });
``` 

以下がソースコード全体。

``` tsx
import ReactDOM from 'react-dom';
import { useState,useEffect } from 'react';


const TextOutput = ({ name }: { name: string }) => {
  return (
    <p>Hello, {name}!</p>
  );
}

const TextInput = () => {
  const [str, setStr] = useState('初期値');

  useEffect(() => {
    document.title = `input is ${str} !`;
  });

  return (
    <div>
    <form >
      <label>
        入力欄<br />
        <textarea
          value={str}
          onChange={event => setStr(event.target.value)}
        />
      </label>
    </form>
    <TextOutput name={str} />
    </div>
  );
}

ReactDOM.render(
  <TextInput />,
  document.getElementById('root')
);
``` 

いつも通り、npm startでプロジェクトを実行する。
入力値に応じてページタイトルが変更されれば成功。

![スクリーンショット 2021-08-03 171321.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792099/64e82f44-396f-bd44-3597-d364c5892064.png)

# 解説

``` tsx
import { useState,useEffect } from 'react';
``` 

useStateとuseEffectを使用するため、インポートしている。

``` tsx
  useEffect(() => {
    document.title = `input is ${str} !`;
  });
``` 
ページタイトルをstrを使用して設定している。
通常ではページ表示後にタイトル変更は起きないが、useEffectを使用することによってstrの変更が発生した場合に変更される。

# 要約

ページをレンダリングした後に副作用を実行させる場合、useEffectを使用する。

# 次

[useContext](https://qiita.com/botanic7arc/items/aaa5253fe8d7dde305d2)

# 参考文献

- [副作用フックの利用法 – React](https://ja.reactjs.org/docs/hooks-effect.html)
- [【React hooks】噛み砕いて解説してみた~useEffect編~](https://zenn.dev/akino/articles/d2e5a2bdcfa009)
- [Reactにおける「副作用」とは？ - Qiita](https://qiita.com/Mitsuw0/items/801f783ca74b062c1ed8)
Rails ログイン機能

##環境
Rails 6.1.3
Ruby  2.6.5
##ログインの仕組み
ログインページからメールアドレスやパスワードなどの情報をrailsに送信する。
送信された情報をもとに操作しているユーザを特定し、ログイン状態とする。

```routes.rb
get "login"=>"users#login_form"/login_formアクションをlocalhost:3000/loginで読み出し
```
```users_controller.rb
def login
@user=User.find_by(
email: params[:email]
password: params[:password])
end
```
フォームから入力されたemail,passwordから該当するユーザーを取得
userテーブルから入力された値に一致するユーザーを取得し、変数@userに代入
超初心者がMySQLに保存した1分足データを取得してみた

## 参考

以下で保存したデータを今度は取得してみる

https://qiita.com/emilia/items/ea854db50657020f9832

## コード

``` select_histdata_from_table.py
import mysql.connector as mydb
import pandas as pd

conn = mydb.connect(
    host='hostname',
    port='3306',
    user='user',
    password='pass',
    database='fx'
)

conn.ping(reconnect=True)
cursor = conn.cursor()

df = pd.read_sql_query(sql='select * from usdjpy where date between "2021-01-01" and "2021-03-31"', con=conn)

df['date'] = pd.to_datetime(df.date)
df.set_index('date', inplace=True)

print(df.head())

```

## 完走した感想

簡単すぎｨｨｨｨ
引数を影響を与えずにメソッドをオーバーライドしたい時は ... 記法を使う

# 概要
ruby 3.0 以降はキーワード引数の仕様変更により引数の渡し方によってはエラーになるため、メソッドをオーバーライドするときは引数を `...` にするのがいいという話です。
これは 2.7 から追加された記法で引数の転送（Arguments Forwarding）というらしいです。

```ruby
class Parent
  def self.hoge(a, b, c: 'c')
    "#{a}, #{b}, #{c}"
  end
end

class Child1 < Parent
  def self.hoge(*)
    "child1 #{super}"
  end
end

class Child2 < Parent
  def self.hoge(...)
    "child2 #{super}"
  end
end

Child1.hoge('d', 'e', c: 'f')
#=> ArgumentError: wrong number of arguments (given 3, expected 2)

Child2.hoge('d', 'e', c: 'f')
#=> "child2 d, e, f"
```


なんとなくメソッドをオーバーライドしたいけど引数には触れたくない時のイディオムのように `def hoge(*)` を使ってましたが、よく考えれば `def (*array)` のように `配列として受け取る記法` + `最後の引数を暗黙的にハッシュとして受け取る` の合わせ技だったということですね。

3.0 でもキーワード引数を使っていなければ引き続き `(*)` を使えますが、啓蒙のためにも `(...)` をしっかり使っていきたいと思います。


## 参考

https://docs.ruby-lang.org/ja/latest/doc/news=2f2_7_0.html
https://koic.hatenablog.com/entry/arguments-forwarding-of-ruby-2-7
https://techlife.cookpad.com/entry/2019/12/25/121834
SLKファイルってCSVより優秀なのでは？

Excelでファイル保存する際、
「ファイルの種類(T):」の中に「SYLK(*.slk)」という選択肢がある。
こちらでファイルを保存すると「SLK（シルク）ファイル」が作成できる。
slkファイルが優秀そうだと感じたので、まとめてみた。

###SLKファイルとは何か
SYLKとは、表計算ソフトやデータベースソフトで扱うデータ形式のことである。
SYLKは、MicrosoftがExcelを発売する前に開発・発売していた表計算ソフトの Multiplan で導入された形式で、Excelはもちろん、他の表計算ソフトでもSYLK形式を使えるようになっていることが多いファイル形式となっている。ASCIIコード（漢字はシフトJIS）だけで構成されるテキスト形式だが、セル位置や、データが数字か文字かを示す情報などを記録しているので、少々冗長だが確実にデータを交換することが可能である。


###CSVとの比較

それぞれのファイルをExcelで開いたときの挙動は以下の例のようになる。
以下に書く形式は開いたときに表示してほしい形式で、
CSVは開くときの指定をすべて「標準」としているとする。

| 記述文字（形式） | CSV | slk |
| :---: | :---: | :---: | :---: | :---: |
| 0014（文字） | 14 | 0014 |
| 2021/08/01（日付） | 8月1日<br>（2021/08/01） | 2021/08/01 |
| 8-1（文字） | 8月1日<br>（2021/08/01） | 8-1 |
| 8/1（文字） | 8月1日<br>（2021/08/01） | 8/1 | 

※csvをExcelで開いたときの表示形式の設定によって変わるが、
　関数フィールドに入っているのは（）内

CSVではセル内に入れる文字の形式を指定しないと、勝手に型変換を行なってしまい、ほしい要素が違う値に変化してしまいがちになる。
一方、slkファイルはデータの型をすべて記録して出力されるので、Excel側で勝手に変更されることがない。

###これなら、CSVを使うより、slkを使うほうがよさそう？<br>でも、slkはそんなに浸透していない…
これなら、CSVを使うより、slkを使うほうがよさそうに見えるが、slkは以下の欠点がある。

- データの中身（型）をすべて記録しているので、その分ファイル自体がCSVより2倍くらい重い
    - slkファイルをメモ帳等で確認するとわかるが、本データの前にフォントや表示設定のデータが記述されている
- Excel発売前に導入された形式のため、互換性がなくなる可能性がある
- __文字コードがShift-JISのみ__のため、文字化けを起こしやすい
    - 「ñ」「ç」のような特殊文字はもちろん非対応

###結論：そう簡単ではなかった
slkファイルは、データ型保持がある点でCSVより魅力的に感じるが、文字コードがShift-JISのみというのが致命的で、UTF-8だとまず間違いなく文字化けるのがものすごく難点。
Excel も後方互換のために対応できるようにしているような気がする…

slkファイルの上位互換（UTF対応）形式ができるといいな…

###参考
[SYLKとは何？ Weblio辞書](hhttps://www.weblio.jp/content/SYLK)
[Unity]変数共有


### ※この記事はSceneをまたがない場合における、異なるスクリプト間での変数共有を前提にしています。
---
# はじめに
Unityでゲームを作っているとどうしても他のスクリプトからあの変数とってきて値をいじりたいなってことがあると思います。public変数にすればいいんじゃない？ってなるかもしれないのですが、それだとデータが改竄されやすくなるらしい（あんまり詳しくわかっていない笑）ので、Unity初心者の方のお役に立てればという気持ちを込めて僕なりに色々試行錯誤した結果をここに残します。

# 実装方法
プロパティを使います。getter、setterと呼ばれるものを用いて他のスクリプトから変数にアクセスできるようにしてみました。

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [プロパティとは](#Chapter1)
1. [各スクリプト](#Chapter2)
1. [まとめ](#Chapter3)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# プロパティとは
そもそもプロパティって？ってなると思うのでちょこっとだけ解説していこうかと思います。
プロパティとは、オブジェクトの状態や性質を表すための　”オブジェクトが保持するデータ” を指し表す言葉です。

と言ってもはじめのうちは中々ピンとこないと思うので、、。
ここでは　”オブジェクトが保持する値の取得、その値の変更をつかさどるメソッド” のようなものだと思ってくれたら以下の理解がしやすいと思います。

<a id="#Chapter2"></a>
# 各スクリプト
**状況**： スクリプトAの変数countをスクリプトBで変更したい。

```c#:A.cs
public class A: MonoBehaviour
{
   int count; //今回他のスクリプトからアクセス・変更したい変数

   public int countProperty // ここでプロパティを使う。publicをつけます。
   {
     get { return count; }  // これがgettr。他のスクリプトから呼び出した時、returnのあとに書いた変数を返す。
     set { count = value; } // これがsettr。valueには他のスクリプトで代入された値が格納されます。（そこまで気にしなくて大丈夫。）
   }
}
```

```c#:B.cs
public class B: MonoBehaviour
{
  A script_A; //スクリプトAをこっち呼び出すために宣言。

  void Start()
    {
      this.script_A = FindObjectOfType<A>(); //スクリプトを探して上宣言した変数に代入。
      int i = script_A.countProperty; // スクリプトAから変数を持ってくる。（get）
      script_A.countProperty = 10; // 取ってきたスクリプトAの変数を変更。（set）
    }
 }

```

<a id="#Chapter3"></a>
# まとめ
今回は変数共有についてやりました。使い方はわかるけど細かいところの意味は分からない。。ってこともあると思います。僕もあまりよく分かっていなかったのですが、それでも何回か使っていくうちにわかっていくところも多かったので最初は書き写すってことも大事だと思いました。
AngularでCustomValidatorの実装

## 概要

Angularでカスタムバリデーター(Validator)作ってみたので後世の自分のために備忘録として残す。
チェックする処理はささっと書いたのでちょいあまあまかも

## CustomValidatorを作ってみた

入力した値が自然数であるかをチェックするもの。  

```typescript
import { FormControl } from '@angular/forms';

export class CustomValidator {

  /**
   * 自然数チェック
   * ※0以上(小数も許容しない)
   *
   * @param formControl フォームコントロール
   * @return nullの場合エラーなし、trueの場合無効な値である
   */
  public static integer(formControl: FormControl): any {
    let isInValid = false;
    const value = String(formControl.value); // 値は文字列として扱う

    if (value !== null && value !== '' && value !== 'null') {
      const isIntegerThanZero = !/^([1-9]\d*|0)$/.test(value);
      const isIncludeE = value.includes('e');

      // 小数チェック
      const isDecimal = value.includes('.');

      isInValid = isIntegerThanZero || isIncludeE || isDecimal;
      if (!isInValid) {
        return null;
      }
    }

    return { integer: isInValid };
  }

}

```

## 作ったCustomValidatorを使う

作成したCustomValidatorを以下のようにセットして使用する。

```typescript
import { CustomValidator } from './custome-validator/custome-validator';

fc = new FormControl(1, { validators: [Validators.compose([CustomValidator.integer])] });
```
EC-CUBEで大量のmethod非推奨ログ「doctrine/inflector」

以下のようなログが大量に出ていた。

```
User Deprecated: The "Doctrine\Common\Inflector\Inflector::classify" method is deprecated and will be dropped in doctrine/inflector 2.0. Please update to the new Inflector API.
```

ログの内容は「メソッドが非推奨になったから新しいの使えよ！」みたいな感じ。

なんでこんなに出てるんだと調べてみると「doctrine/inflector」のバージョンが`1.4.*`になってた。
1.4から非推奨になってた。

多分、`composer update`とかしたんだろう。。。

EC-CUBEの`composer.lock`では`1.3.1`
https://github.com/EC-CUBE/ec-cube/blob/8fb10d3a0151476e4d8035c039010c5e41906116/composer.lock#L1268

オイラの環境では`1.4.4`になってた。

##解決方法は二つ

###解決方法：１　doctrine/inflectorをダウングレード

1.3.*にすればいいので、バージョンを指定してダウングレード。
https://www.doctrine-project.org/projects/inflector/1.3.html

下記コマンドで一発。

```
composer require doctrine/inflector:1.3.1
```

無事にダウングレードできたら、ログは出なくなった。

###解決方法：２　EC-CUBE本体を修正

:::note warn
コメントから「ソースコードの互換性を壊す変更になる」との事なのでEC-CUBE4.0系4.1系の場合はダウングレードでの解決がよさそうです
:::

`Inflector::classify`の使用箇所を探す。
https://github.com/EC-CUBE/ec-cube/blob/4.0/src/Eccube/Entity/AbstractEntity.php

34行目と48行目にあった。

で、doctrine/inflector:1.4.*のドキュメント
https://www.doctrine-project.org/projects/doctrine-inflector/en/1.4/index.html

`src/Eccube/Entity/AbstractEntity.php`を下記のように修正した。

```diff
- use Doctrine\Common\Inflector\Inflector;
+ use Doctrine\Inflector\Inflector; 
+ use Doctrine\Inflector\InflectorFactory;
```

プロパティとオプジェクトを返すメソッド追加

```diff
+ private $Inflector;
+ 
+ private function getInflector() : Inflector
+ {
+     if ($this->Inflector === null) {
+         $this->Inflector = InflectorFactory::create()->build();
+     }
+ 
+     return $this->Inflector;
+ }
```

使用箇所を修正

```diff
- $method = Inflector::classify($offset);
+ $method = $this->getInflector()->classify($offset);
```

こんな感じで修正してみました。
これでログは増えないはず。。。

##伝えたい事。

ログの内容を見ようぜ。
一行全部、翻訳機にぶっこんで読んでも大丈夫。

パッケージ探してみようぜ。
バージョン確認してみようぜ。
パッケージのGitHubみて、それっぽいメソッド探してみようぜ。
ドキュメント探して見ようぜ。

おわり！！
【kintone】フィールドのアクセス権をまとめて設定する裏技

##はじめに
下記画像のような同じ内容の権限設定を、複数のフィールドにまとめて設定する裏技です。
kintoneのアクセス権の設定でへとへとになっている方、是非使ってみてください～！

![スクリーンショット (100).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1487043/0a2c5b7f-c1dc-5dca-dfe4-173e4376cec6.png)

##手順１
アプリの設定＞フォームタブを開きます。
そして、一括設定したいフィールドをグループフィールドにぶちこみます！！
（グループは後で削除するので、フィールド名・フィールドコードは初期設定で問題なしです）
![スクリーンショット (96).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1487043/2840d975-7764-ab45-ed65-092b40a8cde6.png)

##手順２
アプリの設定＞設定タブ＞アクセス権 フィールドを開きます。
先程作成したグループフィールドに、まとめて付与したいアクセス権を設定します。
そして、画像赤枠の「保存」を押下、その後に右上の「アプリを更新」を押下します。
![スクリーンショット (97).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1487043/f72bed08-35b9-a9e3-34d4-0ce5e36a54ed.png)

##手順３
もう一度、アプリの設定＞フォームタブを開きます。
画像のように、グループからフィールドをぽいぽい出します。
※「グループから外すと、アクセス権の設定が初期化されます。よろしいですか？」と
　ポップアップが表示されますが、下記手順を行えば初期化されません。

次にグループを削除します。
（削除せずに「フォームを保存」すると、ポップアップの警告通り初期化されてしまいます）
![スクリーンショット (99).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1487043/8461cdc8-4385-c07c-6333-9de928a4cf2e.png)

##手順４
アプリの設定＞設定タブ＞アクセス権 フィールドを開いてみると…じゃじゃーん！
グループに設定していたアクセス権が、個々のフィールドに継承されました！
手順２と同様に、「保存」を押下、その後に右上の「アプリを更新」を押下すればお悩み解決です！
![スクリーンショット (100).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1487043/390a3184-7dff-2659-a148-cd06e36b724f.png)
【Rspec】時刻に関するテスト

## はじめに
あるユーザーのログイン履歴一覧を取得するAPIを作成した際のテストにて、時刻の扱い方で躓いたため整理しておきます。

###  前提
ユーザー(accounts)テーブルと、そのログイン履歴(logins)テーブルがある時。

## やりたいこと
以下のようなデータを作成。

```ruby
let(:login1_created_at) { 4.day.ago }

let!(:login1) { create(:login, account: account, created_at: login1_created_at) }
```

この状態で`subject`を走らせ、`created_at`の値が一致することをテストしたい。

## 困ったこと
`response`で返ってきた`accountのcreated_at`と、`let!`で作成した`accountのcreated_at`が一致しない。

## 原因
これはRubyのTimeオブジェクトの仕様で`created_at`にて作成したデータはミリ秒まで含んでしまうためです。

ちなみに以下のコードで検証すると、それぞれ以下のような値が返ってきます。

```ruby
subject
res = JSON.parse(response.body)
expect(Time.zone.parse(res["data"].to_s)).to eq login1.created_at.to_time
```

- response
`Sun, 01 Aug 2021 14:58:19.000000000 JST +09:00`

- let1!
`2021-07-30 14:58:19.384332 +0900`

let1!で作成したデータはミリ秒以下の細かい数字が出てしまい一致しませんでした。

## 解決
結論、ミリ秒以下の正確な値の検証は必要ないため、以下のように`to_s`としてミリ秒以下を切り捨てることで解決しました！

```
let!(:login1) { create(:login, account: account, created_at: login1_created_at.to_s) }
# 結果
2021-07-30 14:58 +0900
```

## まとめ
時刻に関するテストでは正確に一致するのを確認するのは難しいため、以下のようなメソッドを使う方法も検討すると良いかもしれません。

- be_within
  - 指定した秒数は切り捨てられる

- travel_to
  - it の下に定義することで任意の時間を指定して、テストを走らせることができる。
【Laravel】バリデーション（条件付き必須項目）

## はじめに
Laravelでの必須項目のバリデーション設定にて、
「ここの項目に値があれば、こっちの項目を必須にしたい」
なんて思っていた際に便利なバリデーションがあったため、備忘録として残します。

## 開発環境
php 7.4.7
Laravel 8.16.1

##やりたかったこと
項目A, B, Cいずれかに値がひとつでもあれば、全て必須項目にしたい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508299/33ee0ce8-a8c7-e538-5979-ef89506a3e45.png)
## 条件付きrequired種類
・required_if
　=> "指定した項目" が "指定した値" であれば入力必須

・required_unless
　=> "指定した項目" が "指定した値" でなければ入力必須

・required_with
　=> "指定した項目" が "どれか一つでも記載" されていれば入力必須

・required_with_all
　=> "指定した項目" が "すべて記載" されていれば入力必須

・required_without
　=> "指定した項目" が "それは一つでも未記入" であれば入力必須

・required_without_all
　=> "指定した項目" が "すべて未記入" であれば入力必須


## 使い方
今回は「required_with」が適用できそうです！
例はRequestsでバリデーションを設定しています。

```PHP:Requests/Test.php
    public static function rules()
    {
        $validate = [
            'item_a' => ['required_with:item_b, item_c'],
            'item_b' => ['required_with:item_a, item_c'],
            'item_c' => ['required_with:item_a ,item_b']                   
        ];
        return $validate;
    }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508299/8135b8d8-d473-e81b-11aa-f896429dc56c.png)

問題なくバリデーションが付与されているようですね。

## デモ
![Animation3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508299/12e126ef-72f2-3e1c-c228-2f9e0ca02a6b.gif)

## 終わりに
今までifを使用して入力があれば必須というコードを書いていたのですが、見つけた時は驚愕しました。
いやぁ、知らないことばかりです...。
余談ですがデモのようにバリデーションメッセージを変更する場合は下記のようにします。

```php:Requests/Test.php
    public function attributes()
    {
        return [
            'item_a' => '項目A',
            'item_b' => '項目B',
            'item_c' => '項目C',
        ];
    }

    public function messages()
    {
        return [
            'required_with' => ':attributeは必須です。(:valuesのいずれかに記載がある場合)',
        ];
    }
```

制御系をかじっているかたなら常識なのかもしれませんが、誰かの参考にでもなれば幸いです。
未経験、業界知識ゼロからITエンジニア(SE)になって大事だと思ったこと３選。

##目次
1.はじめに
2.大事なこと3選
  ・プライドを捨てる
  ・圧倒的な量
  ・Whyを突きつける
3.結論


##はじめに
私は人生のほとんどの時間をサッカーに当ててきたので、学歴、経歴、知識はほぼ皆無の状態でエンジニアになった。
エンジニアになる前は、福祉業界で仕事をしながら社会人サッカー関西１部リーグでプレーをしていたので、バリバリの体育会系あがり。笑
こんな私がIT企業に入社して今まで(約半年間)で大事だと思ったことを３つに絞ってお伝えします。
IT業界にはあまりいない存在なのでこんな私だから出来る発信をしたいと思います！

#プライドを捨てる
ここがまず最初のミッション。
最初は自分の知識量やスキルの無さにかなり苦戦する。
仕事を頼まれても、どこから始めて良いかも分からない。
自分が全く会社の力になれていないことへの無力感を感じる。→気持ちも沈みがち。

ここで重要なのが、出来ない自分を受け入れて謙虚な姿勢で学べるか。
**自分が頑張って学ぶことは大前提**だが、周りからの見え方も重要。

ここで質問。
あなたは、助けたくなるような人間になれていいますか？
こいつに教えてやりたい！って思われるような人間ですか？
またそういう人間関係を普段から築けていますか？

ただスキルがない人と、スキルはまだないけど学びに対して貪欲な人、また普段から周りとコミュニケーションを取り周囲から愛される人とでは成長速度に雲泥の差が出る。

簡単に言うと教える側も人間ということ。
ここを意識することで、自然と自分の中でいい流れが出来てくる。
出来ない自分を受け入れて**謙虚**に**貪欲**に学びにいき、業務以外でも仲間の役に立てることはないかを徹底的に考え実践しよう！

#圧倒的な量
人によって目標が違うのでここは当てはまる方だけ聞いて欲しい。
会社に入って早く先輩に追いつきたいと考えているなら、圧倒的な量をこなすべき。
当然、最初は作業効率が低いので量をこなすのと同時に時間もかなり取られる。

会社には楽しい仕事、面倒な仕事、難しい仕事など探せば様々な仕事が存在する。
それらの仕事を全て引き受ける。（というより掴みに行く）
上司の「これ誰やる？」に即答する。

このように自分の業務関係なしに色々なところに顔を突っ込んでいくことでそれが全て自分の知見となりスキルとなる。
なので、爆速に成長したいなら圧倒的な量こなすべし！

#Whyを突きつける
ここが一番大事なところで私もまだ不十分なところ。
仕事には会社のビジョンと個人のビジョンがある。
まずは、これを自分の中で明確にすることが大事で、これがないままがむしゃらに仕事をしていても進んでいるというよりかは、さ迷っているのと同じになってしまう。

何のために仕事をするのか。
何のために今この時間を過ごしているのか。
何のためにお客さんと打ち合わせをするのか。　、、等

このように自分が行っている行動全てにWhyを突きつけることにより、ゴールへの最短ルートを行くことができ、今自分は何をすべきなのかが見えてくる。
常にWhyを考えるということは”思考”する事なのでは、ルーチンワークを行うより断然労力がいる作業になるが一番重要なことなので実践し続けよう！！


#結論
大事なことは３つ
・プライドを捨てて謙虚に学ぼう。
・自分のリミッターを覆すような圧倒的な量をこなそう。
・Whyを常に考えて思考停止しない。

でした！
エンジニアに限らず未経験から新しい挑戦する方に少しでも後押しになれたらと思います。
ここまで読んで頂きありがとうございました！
【Vue.js】エラーコードをcatch して条件分岐

## はじめに
APIを叩き、例外が発生した際にcatchでエラーコードを取得する方法についてです。

今回は、取得するエラーコードによって表示するエラーメッセージを変える必要があり、catchでのエラーコード確認方法が分からなかったため、備忘録として残しておきます。

## 実装のゴール
### エラーコードにより表示するメッセージを切り替える

- emailが重複している場合
エラーコード: `record_invalid`
メッセージ:  `このメールアドレスはすでに登録されています。別のメールアドレスをご利用下さい。`

- 既に同じユーザーが登録されている場合
エラーコード: `already_exist_error`
メッセージ:  `既に登録されています。`

### 前提
- email重複時はバリデーションエラーが返る
- 同じユーザーが存在している場合には、API側で任意の例外を発生させている


```ruby
class
    class AlreadyExistError < BadRequestError; end
    
    def create
        # paramsの値を使いユーザーを検索する処理を記述
        
        #　同じユーザーがいる場合に例外を発生させる
        raise AlreadyExistError if user
```

上記のようにすることで任意のエラーを返すことができる。

## catchでエラーを補足
```vue
try {
   # APIを叩く
} catch (e) {
   # ここでエラーコードを取得
}
```
上記のようにcatchの引数に(e)を記述することで、e の中にエラーが格納される。

## エラーコードで条件分岐
エラーコードを確認するには以下のようにすればよい。

```vue
try {
   # APIを叩く
} catch (e) {
    if (e.response.data.code === 'record_invalid') {
        store.dispatch(
          'snackbar/setMessage',
          'このメールアドレスはすでに登録されています。別のメールアドレスをご利用下さい。'
        )
        store.dispatch('snackbar/openError')
    　} else if (e.response.data.code === 'already_exist_staff_error') {
        store.dispatch(
        'snackbar/setMessage',
          '既に登録されています。'
        )
        store.dispatch('snackbar/openError')
    } else {
        store.dispatch('snackbar/setMessage', '登録できませんでした')
        store.dispatch('snackbar/openError')
    }
}
```

これでフロントでエラーコードを取得して条件分岐させることができます。
分かってみれば簡単ですね。
Background表示をマスキングする

こんなマスキングがしたい。
![Screen Shot 2021-08-03 at 16.50.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/429030/66e14324-1277-3f28-8a84-168d0b763414.png)


非表示にしたいActivity内で下記を追加する。

```kotlin
activity.window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
```

全画面で対応したい場合は`Application.ActivityLifecycleCallbacks`を実装したActivity内でこうする。

```kotlin
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
            super.onActivityCreated(activity, savedInstanceState)
            activity.window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
```
超初心者がHistdata.comから取得したローソク足データをMySQLに保存してみた

毎回CSVから読み込むよりデータベースから読み込んだほうが早そうだったのでMySQLに保存してみました。

## 参考

Histdata.comからのデータ取得は以下でやってます。

https://qiita.com/emilia/items/28e9875d273c8f052cbd

PythonとMySQLの連携は以下あたりを参考

https://qiita.com/hoto17296/items/0cfe7cdd3c47b69cc892

## Code

### テーブルを作成する

データベースはあらかじめ作成しておく。

``` sql
create database fx;
```

通貨ペアごとのテーブルにする。今回はUSDJPY、GBPJPY、AUDJPY、EURJPYの4つを選択。テーブルのインデックスはidにしておくことにした。

``` create_histdata_table.py
import mysql.connector as mydb

try:
    conn = mydb.connect(
        host='hostname',
        port='3306',
        user='user',
        password='pass',
        database='fx'
    )

    conn.ping(reconnect=True)
    print(conn.is_connected())

    cursor = conn.cursor()
    for pair in ['usdjpy', 'gbpjpy', 'audjpy', 'eurjpy']:
        sql = '''
            CREATE TABLE IF NOT EXISTS '''+pair+''' (
            id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
            date DATETIME,
            open FLOAT,
            close FLOAT,
            high FLOAT,
            low FLOAT
        ) DEFAULT CHARSET=utf8mb4'''
        cursor.execute(sql)
        cursor.execute("SHOW TABLES")
        print(cursor.fetchall())

    cursor.close()

except Exception as e:
    print(f"Error Occurred: {e}")

finally:
    if conn is not None and conn.is_connected():
        conn.close()

```

### 作成したテーブルにデータを挿入する

``` insert_histdata_to_table.py
import mysql.connector as mydb
import pandas as pd

try:
    conn = mydb.connect(
        host='hostname',
        port='3306',
        user='user',
        password='pass',
        database='fx'
    )

    conn.ping(reconnect=True)
    print(conn.is_connected())
    cursor = conn.cursor()

    for pair in ['usdjpy', 'gbpjpy', 'audjpy', 'eurjpy']:
        path = './temp_historical_data/' + pair.upper() + '.csv'
        df = pd.read_csv(path)
        df.columns = ["Date", "Open", "High", "Low", "Close", "Volume"]
        df["Date"] = pd.to_datetime(df["Date"])
        df["Date"] = df["Date"].dt.strftime('%Y-%m-%d %H:%i:%s')
        
        print(path)
        cursor.execute('truncate '+ pair)
        for index, row in df.iterrows():
            sql = '''
                INSERT INTO '''+pair+'''
                (date, open, close, high, low)
                values
                (%s, %s, %s, %s, %s)
                '''
            cursor.execute(sql, [row.Date, row.Open, row.Close, row.High, row.Low])
    cursor.close()

except Exception as e:
    print(f"Error Occurred: {e}")
    
finally:
    if conn is not None and conn.is_connected():
        conn.close()

```

## 完走した感想（激寒ギャグ）

pd.to_datetimeで作ったTimestampデータはMySQLにそのまま挿入できなかった。恥ずかしがり屋で控えめなMySQLにあわせて文字列に変換してあげるとスルスルと挿入っていった。欲しがってたんだな（馬鹿）
ubuntuでのspigotサーバーの立て方

初めての投稿に書いてみました。
# 用意するもの
* Ubuntuの入ったマシン

# サーバーを立てる
* javaをインストールする

java8以降をaptでインストールする。
※minecraft1.17以降はjava16が必要。(追記:1.18以降はjava17が必要 2022/02/10)

```console
~$ sudo apt update
## 11がjavaのバージョン
~$ sudo apt install openjdk-11-jdk-headless
```

* ユーザーを追加する

```console
## minecraftがユーザー名
~$ sudo adduser minecraft
Adding user `minecraft' ...
Adding new group `minecraft' (1003) ...
Adding new user `minecraft' (1003) with group `test' ...
Creating home directory `/home/mincraft' ...
Copying files from `/etc/skel' ...
## 新しいパスワードを入力する
New password:
## パスワードの確認
Retype new password:
## あとは飛ばしてOK
Changing the user information for test
Enter the new value, or press ENTER for the default
        Full Name []:
        Room Number []:
        Work Phone []:
        Home Phone []:
        Other []:
## 作成の確認
Is the information correct? [Y/n]
```

* 任意の場所にディレクトリを作成する

```console
## ユーザーを変更するする
~$ su minecraft
## 設定したパスワードを入力する
Password:
~$ mkdir minecraft
``` 

* spigotを用意する

```console
## 移動する
~$ cd minecraft
## buildtoolのダウンロード
~/minecraft$ wget https://hub.spigotmc.org/jenkins/job/BuildTools/lastStableBuild/artifact/target/BuildTools.jar
## ビルドするバージョンは1.16.5
~/minecraft$ java -jar BuildTools.jar --rev 1.16.5
```
* 起動用と停止用のスクリプトを作成する

```console
~/minecraft$ nano start.sh
```

```sh:start.sh
#!/bin/bash
## minecraftがscreenの名前
SCREEN_NAME='minecraft'
## spgot-1.16.5.jarがspigotのjarの名前
screen -UAmdS $SCREEN_NAME java -jar Xms2G -Xmx2G spigot-1.16.5.jar nogui
```

```console
~/minecraft$ nano stop.sh
```

```sh:stop.sh
#!/bin/bash
## minecraftがscreenの名前
SCREEN_NAME='minecraft'
if [ -n "$(screen -list | grep -o "${SCREEN_NAME}")" ]; then
    ## サーバー内にアナウンス
    screen -S $SCREEN_NAME -X stuff 'say 30秒後にサーバーを停止します\015'
    sleep 30s
    ## 停止コマンド発行
    screen -S $SCREEN_NAME -X stuff 'stop\015'
    ## 停止実行待機
    sleep 30s
else
    echo [date '+%F %T']  'server is not runnning'
fi
```

* 一度起動する

```console
~/minecraft$ sh start.sh
```

* eulaを編集する

```console
~/minecraft$ nano eula.txt[
```

```txt:eula.txt
#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/docume>#Tue Jan 05 12:07:51 JST 2021
## falseからtrueに変更する
eula=true
```

# サーバーのコンソールを開く方法

```console
## ユーザーを変更する
~$ su minecraft
## コンソールを開くminecraftがコンソールの名前
~$ screen -r minecraft
```
Google Cloud から Cloudflare への外向き通信料金

# 概要

オリジンに GCP を使う場合の、Google Cloud から Cloudflare への外向き通信料金について、調べてみました。

![image-20210803151134958](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20210803151134958.png)

# CDN Interconnect | Google Cloud

Cloudflare は Google Cloud の CDN Interconnect プログラムに参加しています。
そのため、リージョン内の CDN Interconnect を通じた通信は割引料金になります。

> [Bandwidth Alliance | Reduce Data Transfer Fees | Cloudflare](https://www.cloudflare.com/bandwidth-alliance/)
> Google Cloud is not part of the Bandwidth Alliance. Reduced egress price for Google Cloud is through the CDN Interconnect program. Google Cloud  price is based on intra-region CDN Interconnect traffic using egress rates published on https://cloud.google.com/interconnect/docs/how-to/cdn-interconnect. 

# 適用対象サービス

オリジンが GCE / GCS の場合が割引料金の適用対象です。

> [CDN Interconnect の概要  |  Google Cloud](https://cloud.google.com/network-connectivity/docs/cdn-interconnect)
> 下り（外向き）トラフィック レートは、Compute Engine または Cloud Storage を離れるデータにのみ適用されます。

# 通常料金

例えば、東京リージョンの料金は以下の通りです。

## GCE

> [ネットワーキングのすべての料金体系  |  VPC  |  Google Cloud](https://cloud.google.com/vpc/network-pricing#internet_egress)
>
> ![image-20210803152035506](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20210803152035506.png)
>
> ![image-20210803152104192](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20210803152104192.png)

## GCS

> [料金  |  Cloud Storage  |  Google Cloud](https://cloud.google.com/storage/pricing#network-egress)
>
> ![image-20210803152518262](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20210803152518262.png)

# 割引料金

ケースによりますが、割引率が高くなる条件下では半額以下になります。

> [CDN Interconnect の概要  |  Google Cloud](https://cloud.google.com/network-connectivity/docs/cdn-interconnect)
>
> ![image-20210803152613987](https://cdn.jsdelivr.net/gh/kyouheicf/image@master/uPic/image-20210803152613987.png)

# どうやったら割引が適用されるの？

自動的に適用されます。

> [How do I get Google CDN Interconnect discount using Cloudflare? – Cloudflare Help Center](https://support.cloudflare.com/hc/en-us/articles/115002374208-How-do-I-get-Google-CDN-Interconnect-discount-using-Cloudflare-)
> Customers egressing traffic to Cloudflare prefixes sourced from a GCP  project within the same region (NA, EMEA, APAC) would get discounting  applied automatically

# どこで請求の確認できるの？

請求で以下の項目が確認できれば、自動適用済みであることがわかります。

> [SKU Groups - Interconnect Egress  |  Google Cloud](https://cloud.google.com/skus/sku-groups/interconnect-egress?hl=ja)
>
> - Compute Engine
>   - *Network Egress via Carrier Peering Network - APAC Based*
> - Cloud Storage
>   - *APAC-based Storage egress via peered/interconnect network*

# 参考

- [2020年7月のGCP 利用料とアクセス数](https://1e100.4watcher365.dev/google-cloud/google-cloud-platform/gcp-wordpress-summary-report-202007/)
AR9341の内蔵Swtich

IODataのWN-G300RがHardOffで安く入手できたていたのでZRouter.orgのターゲットにしているのですが、ある時期から不安定になったため確認しています。

問題はある一定期間すると外部からの通信ができなくなる状態でした。

この問題はAdrianさんがatu関係の修正をatherosのswitchに入れてから発生したような気がしました。

atuの修正はswitchが保持しいているMAC ADDRESSテーブルをダンプやフラッシュすることができるようになりました。

```
# etherswitchcfg atu dump
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/d91c58ab-b4fa-37bc-b5e4-dd052b66728a.png)

FreeBSDではGMAC0がarge0でGMAC1がarge1になっています。

AR9341のethernetのコンフィグレーションは以下のようなパターンがあります。

1. GMAC0は使わずGMAC1だけでMAC1,MAC5をP0,P4に割り当てて使う
* GMAC0とGMAC1を使いGMAC0をP0でMAC1をP4にしてWAN,LANとして使う
* GMAC0だけを使い外部のGIGAなSWITCHに接続して使う
* GMAC0とGMAC1を使いGMAC0が外部のGIGAなSWITCHに接続して使う

1のケースでもdot1qなVLANが使えるのでWAN,LANのポートにすることは可能です。

P0とP4はEthernet Configuration (ETH_CFG)でSWAPすることもできます。

一般的には2で使うケースが多いのではないかと思われます。次に多いのは3のパターンかもしれません。

MIPSのSOCで100のSwitchを内蔵したチップはたくさんありますが、GIGAのSwitchを内蔵したチップはあまり見たことがありません。これはGigaのチップは結構電気を食うので、CPUとの同居が難しいからかもしれません。

スイッチの一般的な動作ですが、MACアドレスを知っている場合はそのポートにだけパケットを送信します。

![ARPORT.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/61bacea5-752d-f225-837d-301457aba752.png)

送信先のMACアドレスを知らない場合は全部のポートに送信します。この動作の事をFloodingというようです。

![ARPORT2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/c1f3661c-5280-9e31-4e87-ea6fa976d1a0.png)

もちろんARPリクエストのようなブロードキャストもすべてのポートに送信します。

MACアドレスのテーブルは5分で消えるようになっています。次のパケットが入ってくるまでは、消えてしまったアドレスへの送信は全ポートに送信されます。

なぜかMACアドレスのテーブルをflashしてからCPUのifのMACアドレスでパケットを送信すると、全ポートに送信されるのが正し動作ですが、CPUポートにだけ届きません。

ARPのブロードキャストは届きます。このためarpを削除してARPから始めた場合は通信できます。

本来の動きではない気がしますし、どこで設定できるような気もしますし、atuの修正とは関係ないような気もします。

謎です。

データーシートをながめていてAddress Table Function2にMAC_CLONEというフラグがありました。これはCPUポートのMACアドレスは時間が来ても消えない様な設定のように思われます。これを実装すれば回避できるかもしれません。

FreeBSDのEtherswitchは既存の仕組みを生かしてよくできていると思いますが、実機に入ってどのようなコンフィグレーションになってるのかがわからないので、ちょっと考えてみる必要があるかもしれません。

WN-G300RをZRouterでビルドしたkernelで使うと、WANとLAN 3が入れ替わっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/a03db2a3-bb69-40a5-6a36-ed6ff2324284.png)
【kotlin】同時タップでアプリが強制終了しない様にする



#状況
Androidではデフォルトでボタンの同時押しを許容する設定になっており、設定を変更したい


#解決方法
全画面に同時タップを出来ない様に反映するにはAndroidManifest.xmlのテーマに下記スタイルを適用する
画面毎ボタンの同時押し禁止を実装するには、下記を各widgetに適用すればOK

```<item name="android:windowEnableSplitTouch">false</item>```
```<item name="android:splitMotionEvents">false</item>```

[参考URL](https://www.ithack.info/button/)
k3sでプライベートレジストリー(Private Registry)を使う(containerd編)

毎度、ググっても出てこない小ネタを取り扱っております。
本記事は個人的な見解であり、筆者の所属するいかなる団体にも関係ございません。

# 0. はじめに

K3sクラスターを作るのは、Edge環境が多いですよね(よね？！)
そうなるとインターネットに出ていけない環境だったり、Dockerイメージを入れているのがGitLabのContainer Registryだったりしますよね(よね？！)。

そういうK3sクラスター向けにPrivate RegistryをK3sクラスターで利用できるようにする方法をご紹介します。

# 1. 前提
K3sバージョン: v1.19.12+k3s1
Container Runtime: `containerd(k3sデフォルトのruntime)`(※)
`(※注:Container Runtimeがdockerdの場合には以下の方法では設定できません)` 

# 2. Rancherのドキュメントに沿って設定する

ドキュメントのURLは以下の通りです。
https://rancher.com/docs/k3s/latest/en/installation/private-registry/

詳細に書かれているので注釈のしようもないのですが、今回はコンテナーレジストリーがGitLab.comでしたので以下のように記載しました。

```yaml:/etc/rancher/k3s/registries.yaml
mirrors:
  docker.io:
    endpoint:
      - "https://registry.gitlab.com"
configs:
  "registry.gitlab.com":
    auth:
      username: user@example.jp
      password: XXXXXXXXXXXXXXXXXX
```

このファイルを`/etc/rancher/k3s/registries.yaml`に保存します。

```bash
sudo cp ./registries.yaml /etc/rancher/k3s/registries.yaml
```

# 3. k3sの再起動

`registries.yaml`ファイルは、k3sが起動したときにしか読み込まれません。
なので、K3sの再起動が必要です。statusで起動したかも確認しておきましょう。

```
sudo systemctl restart k3s
sudo systemctl status k3s
```

# 4. プライベートレジストリーが設定された確認する

設定されたか`sudo k3s crictl info`コマンドで確認します。

```
$ sudo k3s crictl info
```

以下のような設定が入っていればOKです。

```json
    "registry": {
      "mirrors": {
        "docker.io": {
          "endpoint": [
            "https://registry.gitlab.com"
          ],
          "rewrite": null
        }
      },
      "configs": {
        "registry.gitlab.com": {
          "auth": {
            "username": "user@example.jp",
            "password": "XXXXXXXXXXXXXXXXXX",
            "auth": "",
            "identitytoken": ""
          },
          "tls": null
        }
      },
```

# 5. 参考

以下の情報を参考にさせていただきました。

ローカルのDocker Registoryをk3sで構築したKubernetesで利用する方法 - 仮想化通信
https://tech.virtualtech.jp/entry/2019/10/11/152522