OmniAICreator/Qiita-1.07M huggingface dataset:
Ubuntu20.04にpyenvとPipenv導入した時のメモ

# はじめに
タイトル通り、WSL2上でUbuntu20.04でpyenvとpipenvによるPython開発環境を作ろうと色々な記事通り進めたところいくつかハマったので備忘録

### 目標
ローカルを汚さずにPython自体のバージョンをpyenvで、プロジェクトごとの管理をPipenvで出来る状態

### 環境
* Win10(WSL2)
* Ubuntu20.04
* zsh(他のシェル使ってる方は適宜読み替えてください)

# 目次
1.[pyenv導入](#1-pyenv)
2.[Pipenv導入](#2-pipenv)

#1. pyenv
まず[こちら](https://dotblogrc.blogspot.com/2020/06/pyenvpipenv.html?m=1)を参考にpyenvのインストール

```
$ sudo apt update
$ sudo apt install -y --no-install-recommends build-essential libffi-dev libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev git
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
```

次にpyenv関連のパスを通す

```
$ echo -e "# pyenv paths" >> ~/.zshrc
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
$ echo 'eval "$(pyenv init -)"' >> ~/.zshrc
$ source ~/.zshrc
```

と、ここでトラブル発生(この後は試行錯誤の過程を書いてるだけなので必要ない場合は[ここ](#自分の環境での結論)まで読み飛ばしてください)

```
WARNING: `pyenv init -` no longer sets PATH.
Run `pyenv init` to see the necessary changes to make to your configuration.
```

ググっても解決策出てこないので、表示に従ってとりあえず``pyenv init``してみることに

```
# Load pyenv automatically by appending
# the following to ~/.zshrc:

eval "$(pyenv init -)"

# And the following to ~/.profile:

eval "$(pyenv init --path)"

# Make sure to restart your entire logon session
# for changes to ~/.profile to take effect.
```

何やら「.zshrcだけじゃなくて.profileに``eval "$(pyenv init --path)"``追記しといてね」的な事らしい

```
$ echo 'eval "$(pyenv init --path)"' >> ~/.profile
$ source ~/.profile
```

無事エラー表示が消えたと思いきや新しいシェルを立ち上げるとまだエラーが出る...
ここでさっきの``WARNING: `pyenv init -` no longer sets PATH.``を見直してみると「``pyenv init -``する必要ないよ」ということなのでは...?と思い、.zshrcから``eval "$(pyenv init -)"``を消してみるとエラーも消えてパスも通った!!

公式にも特にこういったやり方は見当たらなかった(見落としてるだけかも)のでなんか知ってる方居たらコメントお願いします

### 自分の環境での結論

```
$ sudo apt update
$ sudo apt install -y --no-install-recommends build-essential libffi-dev libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev git
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
$ echo -e "# pyenv paths" >> ~/.zshrc
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
$ source ~/.zshrc
```

なんかエラー出るなと思ったら``eval "$(pyenv init -)"``を消してみるといいかも

#2. Pipenv
基本的には``pip install pipenv``で良いんですが、自分の環境だと``command not found``になっちゃいました
結論から言うと、root権限でinstallすればおｋでした
ついでに、仮想環境をプロジェクト直下に作るよう設定

```
$ echo -e "# pipenv property" >> ~/.zshrc
$ echo 'export PIPENV_VENV_IN_PROJECT=1' >> ~/.zshrc
$ source ~/.zshrc
```
【Rails】ログイン前ユーザーのアクセス制限機能

#はじめに
deviseを利用したログイン前のユーザーのアクセス制限機能の実装についての備忘録です。
初学者のため間違った知識を公開している可能性もあるため遠慮なく指摘して頂ければと思います。
　　
　　
#手順
基本的には下記の記述を任意のコントローラーに追加することで実装できる。

```ruby:application_controller.rb
before_action :authenticate_user!
```
　　
　　
一部のページをアクセス制限の対象から除外する場合は以下のように記述する。

```ruby:application_controller.rb
before_action :authenticate_user!, except: [:top]
```
　　
　　
このようにオプションとして`except: [:アクション名]`を使用することでトップページだけはログイン前でもアクセス可能になる。
Vuw CLIを導入する


（１）node.js
>https://nodejs.org/ja/

こちらのサイトへ行って
![20210508-074901.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960935/59b6cf0b-2aa2-afac-c38f-7552edfb1090.png)

以下を展開してインストールします。

![20210508-075115.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960935/46b8da54-02cc-f848-a9f1-6ca4b0d65a12.png)

次にターミナルを開きます。


```vuejs
MacBook-Air projects % vue create udemy-vuejs
```

今回はこちらを選択
![20210508-075700.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960935/522c713b-4226-8e7f-8adc-9bb75f94e939.png)



```vuejs
Vue CLI v4.5.12
? Please pick a preset: Default ([Vue 2] babel, eslint)
```

```vuejs
🎉  Successfully created project udemy-vuejs.
👉  Get started with the following commands:

 $ cd udemy-vuejs
 $ npm run serve
```

終了したら終わりです。
sshのログインで“WARNING: POSSIBLE DNS SPOOFING DETECTED!”というメッセージがでてログインできない。

## sshでログインできない

ラズパイのOSをインストールしなおしてsshしようとしたら、以下のようなメッセージが出てログインできませんでした。

```
PS C:\Users\toshi> ssh raspberrypi.local
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@       WARNING: POSSIBLE DNS SPOOFING DETECTED!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
The ECDSA host key for raspberrypi.local has changed,
and the key for the corresponding IP address 192.168.10.14
is unknown. This could either mean that
and its host key have changed at the same time.
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ECDSA key sent by the remote host is
Offending ECDSA key in C:\\Users\\toshi/.ssh/known_hosts:1
ECDSA host key for raspberrypi.local has changed and you have requested strict checking.
Host key verification failed.
PS C:\Users\toshi>
```

~/.ssh/known_hostsの1行目に同じホスト名（raspberrypi.local）で前回の情報が残っているようなので、1行目を削除する。

そのあとsshで接続するとログインできました。
Google Apps ScriptとPythonでQiitaのContribution数増加や閲覧数を記録する


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/729623e7-7bfa-8405-d6af-ba7de40f8f67.png)


↑ この赤いところを日々記録してみる、という話。

## GAS

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/19f3cb6f-9506-860a-c752-43544b126656.png)
HTML上の `<span class="css-mf9wc5">23856</span>` を取得したいということになります。API としては以下があるのだが、ユーザー単位でContribution数を取るというのはできない。

https://qiita.com/api/v2/docs

だったので、HTMLから直接走査しました。
Contribution とは...

https://help.qiita.com/ja/articles/qiita-contribution

自分の日々のモチベーションだったりをこれで維持したいという目論見です。


### 全体

端的に以下 Google Apps Script（GAS）を書きました。

```JavaScript:Code.gs
function main () {
  var now = new Date(); //現在日時を取得
  var time = Utilities.formatDate(now, 'Asia/Tokyo', 'yyyy/MM/dd HH:mm:ss');
  let count = countContribution();
  write2SpreadSheet(time, count);
}

function countContribution() {
  const URL = 'https://qiita.com/e99h2121';//mypage
  let responseDataGET = UrlFetchApp.fetch(URL).getContentText();
  let myRegexp = /<span class=\"css-mf9wc5\">([\d]+)<\/span>/; //★ `<span class="css-mf9wc5">nnnnn</span>`  
  let contribution = responseDataGET.match(myRegexp);
  let out = contribution[0].replace("<span class=\"css-mf9wc5\">","").replace("<\/span>",""); //★ 数字部分のみに置換
  return out;
}

function write2SpreadSheet(today, count) {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  // 書き込みタブの名前と揃える。
  var sheet = ss.getSheetByName('count');
  var row = sheet.getLastRow();
  var lastval = sheet.getRange(row,2).getValue();
  var plus = count - lastval; 
  sheet.appendRow([today, count, plus]);
}
```

で、記録。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/616601da-f67e-30d4-c8f5-38e37b50a7f7.png)


2時間おきの時間実行で仕掛けるとこんな感じになる。
（実際は1日1回程度で良い）

### OrganizationのContribution数も

同じ要領で、Organization の数値をとってSlackに通知しているとの話。

https://qiita.com/naoqoo2/items/427fbdc077d6b3a296e4

### 注意点

色々応用できそうだなあと思いつつ、お約束としては以下。

https://qiita.com/nezuq/items/c5e827e1827e7cb29011

https://qiita.com/n_oshiumi/items/b4efd1f40ec0a1b77376


## あるいはPythonでCSVに落とす

```py
import requests
import json

url = 'https://qiita.com/api/v2/authenticated_user/items'
headers = {"content-type": "application/json",
           "Authorization": "Bearer {Token}"}
params = {
    'page'     : 1,
    'per_page' : 100,
}

res = requests.get(url, params=params, headers=headers)
list = res.json()

print("==========================================================")
# ヘッダ出力
print("\"title\",\"page_views_count\",\"likes_count\"")

for item in list:
    item_id = item['id']
    title = item['title']
    likes_count = item['likes_count']
    item_url = item['url']
    
    url = 'https://qiita.com/api/v2/items/' + item_id
    res = requests.get(url, headers=headers)
    json = res.json()
    page_views_count = json['page_views_count']

    print("\"" + title + "\",\"" + str(page_views_count) + "\",\"" + str(likes_count) + "\",\"" + item_url + "\"")

``` 

### 参考

https://qiita.com/Naoto9282/items/252c4b386aeafc0052ba


以上を踏まえつつ、アウトプットを楽しむ仕掛け諸々のお話でした。
参考になればさいわいです。
【C#】DateTimeの初期化　文字列で初期化したい時

Datetmeの初期化のTips
#通常の初期化#
newでコンストラクタに引数を渡す

```c#
// yyyy-mm-dd
DateTime dt1 = new DateTime(2021, 4, 15);
// yyyy-mm-dd hh:mm:ss.fff
DateTime dt2 = new DateTime(2021, 4, 15, 23, 59, 59, 999);
```

#文字列で初期化したい場合#
文字列で初期化をしたかったのだが
普通にコンストラクタの引数に入れてしまうとエラーになる

```c#
DateTime dt = new DateTime("2021/04/23"); // エラー
```

##DateTime.Parse()##
Parseという便利なメソッドがあった

```c#
// yyyy-mm-dd
DateTime dt3 = DateTime.Parse("2021/04/16");
// yyyy-mm-dd hh:mm:ss.fff
DateTime dt4 = DateTime.Parse("2021/04/16 23:59:59.999");
```

結果

```
2021/04/16 0:00:00
2021/04/16 23:59:59
```
Athenaのパーティションでは大文字禁止！

「Athenaのパーティションでは大文字禁止！」

これを知らずに、ハマりまくりました。（厳密には禁止というか、使えないよ、ということです）
みなさん、同じ溝にハマらないでください。という思いで記載します。

Athenaではパーティションに、大文字（キャメルケースなども）が使えないらしいです。

詳細：
https://docs.aws.amazon.com/ja_jp/athena/latest/ug/msck-repair-table.html


> Amazon S3 パスを小文字に変更する
Amazon S3 パスは小文字にする必要があります。S3 パスがキャメルケースの場合、MSCK REPAIR TABLEは、パーティションを AWS Glue データカタログに追加しません。たとえば、S3 パスがuserIdのパーティションは AWS Glue データカタログに追加されません。

つまり、HIVE形式にして

userId=123 といったパーティションは使えないので
userid=123 とフラットケースにしなければなりません。

テーブル作成中にもエラーが出ないし、特にメッセージもなかった（はず）。

MSCK REPAIR TABLEしても、ALTER TABLE ADD PARTITIONをしても、
クエリが０件でヒットする形に、、、なります。


データの形式がおかしいのか、色々回り回った挙句、
ふと、パーティションを小文字にしてみたらデータがロードされた。

他にも、パーセントエンコーディングが必要な文字などは注意が必要だったりします。
基本英数字小文字でs3パスを構成するのが無難のようです。
IntersectionObserver　APIを使った遅延読み込み 実装方法の概要メモ

## IntersectionObserverAPIを使用するメリット
監視したい対象要素がIntersection（交差点）に達した時だけイベントを走らせてくれるのでパフォーマンスが良い。
（画面スクロール位置を読み込み続けるようなことが避けられる）

https://developer.mozilla.org/ja/docs/Web/API/Intersection_Observer_API


## IntersectionObserverAPIの概要
画面の上部が指定した地点に達するたびにコールバック関数を呼び出す。


### 実装方法の概要
例として、header要素が画面から見えなくなったらスティッキーナビゲーションを表示する方法を記載します。

①監視したい要素を取

```javascript:例
const header = document.querySelector('.header');
```

②オブジェクトを記載
条件を記述します。

```javascript:例
const options = {
  root: null,   //viewport全体を監視対象にする場合null
  threshold: 0,   //0~1で指定。左の例だと、監視対象の要素の0%が画面から見えたら(監視対象の要素が見えなくなったら)③で記載するAPIが走るイメージ
}
```

③コールバック関数を記載
実行したいことを記載します。
引数にはAPIから返ってきたオブジェクトが配列で入る。
console.log(entry[0])でみてみると、スクロールのタイミングごとにisIntersecting=trueだったりfalseだったりする。
isIntersecting=trueだと、監視対象の要素がoptionで指定したthresholdに当てはまっているような意味。それを利用して実装する。

```javascript:例
const callbackfunc = function(entry, observer) {
  // isIntersecting=falseだったら（監視対象要素が見えなくなったらステッキーナビをつける）
  if(!entry[0].isIntersecting) document.querySelector('nav').classList.add('sticky'); 
  // isIntersecting=trueだったら（監視対象要素が見えてたらステッキーナビを外す）
  if(!entry[0].isIntersecting) document.querySelector('nav').classList.remove('sticky'); 

  observer.unobserve(entry[0].target);  //一度実行したらそれ以降ターゲットが交差してもイベントを走らせないようにする
}
```

④IntersectionObserverAPIのコンストラクターを実行し、インスタンス化

```javascript:例
let observer = new IntersectionObserver(callbackfunc, options);
```
第一引数に③で記載したコールバック関数、第二引数に②で記載したオブジェクトを渡します。

⑤IntersectionObserverAPIのobserveメソッドに監視したい要素を渡す

```javascript:例
observer.observe(section);
```
【Python】pip installでERROR: Command errored out with exit status 1

##環境
* Windows 10 Home
* Python 3.9.4(64bit)

##実行したコマンド
作成したパッケージのひな型を仮想環境へインストール

>```txt:hogehoge.txt
Click==7.0
Pillow==6.2.1
：
```


>```
>python -m pip install -r hogehoge.txt
```

##エラー
>```
Requirement already satisfied: Click==7.0 in c:\users\huga\workspace\.venv\lib\site-packages (from -r hogehoge.txt (line 1)) (7.0)
Collecting Pillow==6.2.1
  Using cached Pillow-6.2.1.tar.gz (37.7 MB)

>            ：

 >   Running setup.py install for Pillow ... error
  ERROR: Command errored out with exit status 1:

>            ：

>   RuntimeWarning: Pillow does not yet support Python 3.9 and does not yet provide prebuilt Windows binaries. We do not recommend building from source on Windows.

>            ：

>   Please see the install instructions at:
       https://pillow.readthedocs.io/en/latest/installation.html
```

##何が悪かったか
<font color="Red">Pillow does not yet support Python 3.9</font>とある通り、Pythonとライブラリ（Pillow）のバージョンに不整合が生じているっぽい。

エラー文に記載されているURLにアクセスして確認↓
>![スクリーンショット (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1430919/24e2e7df-0242-a57d-fd09-9dbf2631118e.png)

##対処
Pillowのバージョンを修正

>```txt:hogehoge.txt
Click==7.0
Pillow==8.0
：
```

##おわりに
エラー文ちゃんと読もう。
SpringBoot+Groovyで簡単なアプリケーションを作成してみる

書籍「Spring Boot2 プログラミング入門（秀和システム）chapter2 Groovyによる超簡単アプリケーション開発」を参考にしながらWebフォームを作成した際に困った点をなどを記述する。

## Groovyでアプリ作成した理由

普段Javaのコードを書いている環境で実行できる簡単なスクリプト言語として気になったので手を出してみた。<br>
Javaの文法を周到しつつインテンドを気にしなくていいPythonのような感じがした。
<br>
ごく簡単な個人開発レベルのアプリ制作には使えそうな気がする。

## 環境
OS:macOS
JDK:14

## Spring Initializrからひな形をダウンロード
参考にした書籍内ではとにかく動くものを作成するためにSpringBootCLIを利用しているが、ある程度実践的な形を想定してSpring Initializrを利用した。以下は各種設定

[Spring Initializr](https://start.spring.io/)

![spring initializer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646506/d8af815c-2c70-d6bc-2e0c-ce6a341c0994.png)

* Project:Maven
* Language:Groovy
* SpringBoot:2.4.5
* Java:11
* Dependencies:SpringWeb,Groovy Templates(いらないかも),Thymeleaf

## ソースコード
実際に編集するのはひな形のmainの中のみ。書籍とも重複するのでGitHubリポジトリを添付し説明は割愛します。以下はリポジトリのリンク

[SpringBoot+Groovyで簡単なアプリケーションを作成してみる](https://github.com/Reo-Satooooo/groovyapp)

## テンプレートの利用
Groovyには専用のHTMLテンプレートがあり書籍内でも紹介されてはいるが、Thymeleafの利用が推奨されている。Groovyテンプレートは書籍内で紹介があったため導入したが、正直Thymeleafの方が格段にわかりやすい。

## 困った点
Grabアノテーションを使用際に以下のようなエラーが発生した。

```
エラー:Groovyc: While compiling groovyapp: Cannot @Grab without Ivy, please add it to your module dependencies (NoClassDefFoundError: org/apache/ivy/plugins/resolver/DependencyResolver)
```

書籍内ではGrabアノテーションを使用してThymeleafの読み込みを行っているが、今回はSpringBootCLIを利用していないため依存関係を追加する手間がないEnableAutoConfigurationアノテーションを使用することで自動読み込みさせることにした。

[Spring Boot で『Thymeleaf』 テンプレートを使う](https://cloudear.jp/blog/?p=799)
AWSとWordPressを使ってWebサイトを構築しよう⑩

<h2>■DNS名(ドメイン名)を割り当てる</h2>
今回はインスタンスにDNS名を割り当てていきます。
DNS名はドメイン名とも言い換えられますが、そもそもDNS名とは何でしょうか？

#目次
[1.DNSとは](#1-dnsとは)
[2.DNSを有効化しよう](#2-dnsを有効化しよう)
[3.【おまけ】（nslookupコマンドで名前解決を確認する）](#3-おまけnslookupコマンドで名前解決を確認する)

#1. DNSとは
まず<b>ドメイン</b>とはインターネットの世界で言う住所のようなもので下記のようなものです。
https://www.google.com/

普段私たちはこのようなドメイン名を検索バーへ入力して目的のWebサイトを閲覧しますが、実はインターネットの中でこのドメイン名をIPアドレスに変換して目的のWebサイトを表示してくれています。

この変換してくれる仕組みを<b>DNS(Domain Name System)</b>と言い、DNSサーバーが仕組みを提供します。
またドメイン名をIPアドレスに変換することを<b>名前解決</b>と言います。

##2. DNSを有効化しよう
前回はWebブラウザからパブリックIPを使用してWebサーバーへアクセスしました。
今回はWebサーバーにドメイン名を付与してパブリックIPからではなく、ドメイン名でアクセスできるようにしていきます。

まず検索バーに「VPC」と入力して、表示された「VPC」をクリックしてください。
![スクリーンショット 2021-05-08 2.25.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159544/3fe02bda-7312-e46d-60c9-e8c4519c86e4.png)

左メニューから「VPC」をクリックし、以前作成した「VPC」を選択してください。
そして右上にある「アクション」をクリックし、「DNSホスト名を編集」をクリックしてください。
![スクリーンショット 2021-05-08 2.26.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159544/7ce849ef-98a5-cb48-1274-5b54c58ed3ae.png)

「DNSホスト名」の下に「有効化」と書かれたチェックボックスにチェックを入れて、「変更を保存」をクリックしてください。
![スクリーンショット 2021-05-08 2.28.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159544/5d735444-e787-b0a1-a213-f60901c18317.png)

これでインスタンスにDNS名が付与されたので確認してみましょう。
検索バーに「EC2」と入力し、「EC2」をクリックしてください。
![スクリーンショット 2021-05-08 2.30.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159544/d809afcd-0d8e-8f94-5cce-dc2e534349d8.png)

左メニューから「インスタンス」をクリックし、作成したインスタンスを選択してください。
すると画像下２つの赤枠内に「パブリック IPv4 DNS」と「プライベート IPv4 DNS」という項目があり、下記のようにDNS名が設定されたのが分かります。
<b>・パブリック IPv4 DNS：ec2-3-112-29-31.ap-northeast-1.compute.amazonaws.com</b>
<b>・プライベート IPv4 DNS：ip-10-0-1-10.ap-northeast-1.compute.internal</b>
パブリックDNS名はインターネットからアクセスできるDNS名で、プライベートDNS名はVPC内でアクセスできるDNS名です。

![スクリーンショット 2021-05-08 2.32.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159544/13e5a10f-efb1-85a5-4b1b-d5241f857021.png)

ではパブリックDNS名を使ってWebブラウザからアクセスできるか確認しましょう。
Webブラウザの検索バーにパブリックDNS名を入力してアクセスしてください。
すると下記のようにApacheのデフォルト画面が表示されて、無事パブリックDNS名からもアクセス出来たことが分かります。
![スクリーンショット 2021-05-08 2.39.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159544/b664e849-70d9-bf44-9bb5-105fa71b2812.png)

#3. 【おまけ】（nslookupコマンドで名前解決を確認する）
最初にドメイン名をIPアドレスに変換することを名前解決と言いました。
今回DNS名を付与させて名前解決ができているので、実際にコマンドプロンプト上で名前解決ができているか確認しましょう。

ここではMacのターミナルを使用した方法を紹介します。(Windowsでも同じコマンドは使用できます)
ターミナルを開き下記コマンドを入力します。
※パブリックDNS名はご自身のを入力してください。

```
nslookup ec2-3-112-29-31.ap-northeast-1.compute.amazonaws.com
```
すると下記のように「Name：ec2-3-112-29-31.ap-northeast-1.compute.amazonaws.com」に対して「Address: 3.112.29.31」が返ってきています。
これはパブリックDNS名が名前解決されて、WebサーバーのパブリックIP「3.112.29.31」が表示されたということになります。

```
Server:		192.168.10.1
Address:	192.168.10.1#53

Non-authoritative answer:
Name:	ec2-3-112-29-31.ap-northeast-1.compute.amazonaws.com
Address: 3.112.29.31
```

</br>
今回はここまで。
次回はプライベートサブネットを作成していきます。

次の記事はこちらから
<a href="https://qiita.com/uchino_infra/private/2f2c63e639fcc0494f82">AWSとWordPressを使ってWebサイトを構築しよう⑪</a>
Railsのpolymorphicに触れる

## 何をしたのか
今までpolymorphicについてなんとなくで使っていたので、理解を深める目的でpolymorphicを触れました。

## 今回の使用例

今回は下記の図のmodelを使います。
`Comic(漫画)`と`Novel(小説)`がそれぞれ`Author(著者)`という子モデルを持っているという状態です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057421/bc759ab8-0059-a335-cf88-1ffb374d760b.png)

それぞれのモデルのコードは下記のようになっています。

```ruby:author.rb
class Author < ApplicationRecord
  belongs_to :bookable, polymorphic: true
end
```

```ruby:comic.rb
class Comic < ApplicationRecord
  has_many :authors, as: :bookable
end
```

```ruby:novel.rb
class Novel < ApplicationRecord
  has_many :authors, as: :bookable
end
```

それぞれ下記のように値が入っているとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057421/ded79b76-613c-5698-2747-09cb27de6286.png)


Authorから親モデルを取得したいときは下記のようなコードで取得できます。

```ruby
Author.find(1).comic.title
Author.find(3).novel.title
↓
# 両方ともbookableで取得できます。
Author.find(1).bookable.title
#=> ワンピース
Author.find(3).bookable.title
#=> 白夜行
```

ここでpolymorphicが便利なのはAuthorの親モデルがComicかNovelかを気にせず`bookable`で取得できるところにあると思います。

## 参考
https://railsguides.jp/association_basics.html#%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91
パスワード付PDFを一括作成する

諸事情で全社員に届け出住所を確認してもらう必要が生じました。
普段はGoogle Apps Scriptを利用してメールの差し込み配信をしているのですが、住所をメール本文ベタ打ちで送るのはいかがなものかという話に。そこで、住所を記載したPDFにパスワードをかけて、各人に送付することにしました。
パスワードは各人で異なります。

あらかじめ、ファイル名、住所、パスワードを記載したExcelファイルを用意しておきます。中身は下表のような内容です。

| 氏名 | ファイル名 | 住所 | パスワード |
|:-:|:-:|:-:|:-:|
|富田鈴花|tomita|神奈川県○○市|suzuka|
|松田好花|matsuda|京都府○○市|konoka|
|金村美玖|kanemura|埼玉県○○市|miku|

パス付きpdfを作成するコードをPythonで書いて行きます。
最初に必要なモジュールを読み込み。

```Python
import os
import glob
import xlrd
import reportlab
from reportlab.pdfgen import canvas
from reportlab.pdfbase.pdfmetrics import registerFont
from reportlab.pdfbase.ttfonts import TTFont
import PyPDF2
```

準備したExcelファイルを読み込みます。

```Python
setting_sheet = xlrd.open_workbook(r'C:\hogehoge\settings.xlsx').sheet_by_name('Sheet1')
max_row = setting_sheet.nrows
```

ファイルの中身をリストに格納していきます。

```Python
name = []
address = []
password = []
for i in range(max_row):
    name.append(setting_sheet.cell_value(i,1))
    address.append(setting_sheet.cell_value(i,2))
    password.append(setting_sheet.cell_value(i,3))
```

作業に使うフォルダを指定。pyファイルが入っているフォルダの下に「pdf」を作っておいたので、そこにパスワード無しのpdfファイルを保存する形をとります。

```Python
target_folder = os.getcwd() + r'\pdf'
```

住所は日本語なので、日本語を扱えるフォント（Meiryo UI）を読み込んでおきます。

```Python
registerFont(TTFont('meiryo',r'c:\windows\fonts\meiryo.ttc'))
```

ひとまずパスワードがかかっていないpdfファイルを作成します。

```Python
for j in range(1,max_row):
    generated_pdf = target_folder + '\\' + name[j] + '.pdf'
    pdffile = canvas.Canvas(generated_pdf)
    pdffile.saveState()
    pdffile.setFont('meiryo', 20)
    pdffile.drawString(100, 800, address[j])
    pdffile.save()
```

PyPDF2を使って作成済みpdfファイルにパスワードをかけ、別ファイルとしてexportフォルダに保存します。

```Python
    src_pdf = PyPDF2.PdfFileReader(generated_pdf)
    dst_pdf = PyPDF2.PdfFileWriter()
    dst_pdf.cloneReaderDocumentRoot(src_pdf)
    dst_pdf.encrypt(password[j])
    export_file = target_folder + '\\export\\' + name[j] + '.pdf'

    with open(export_file,'wb') as f:
        dst_pdf.write(f)
```

ファイルを作成したら、[Google Apps Scriptでメールを一括配信](https://qiita.com/neko22/items/c91da4f71cc7a286737b
)すれば完了です。
Android Studioで適当にアップデートをしたらビルドが失敗するようになった

#0. 環境
- Android Studio 4.2 Stable
- Macbook Air 2020, M1

#1. はじめに

先日、Android Studio 4.2 Stable版がリリースされ、同時にKotlinのアップデートも促されたので何も考えずに更新してみたら、新規プロジェクトに限ってこんなエラーが出てビルドがうまくいきません。


```txt:エラーメッセージ
A problem occurred configuring root project 'My Application'.
> Could not resolve all artifacts for configuration ':classpath'.
   > Could not find org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.0-release-764.
     Searched in the following locations:
       - https://dl.google.com/dl/android/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.5.0-release-764/kotlin-gradle-plugin-1.5.0-release-764.pom
       - https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/kotlin-gradle-plugin/1.5.0-release-764/kotlin-gradle-plugin-1.5.0-release-764.pom
     Required by:
         project :

Possible solution:
 - Declare repository providing the artifact, see the documentation at https://docs.gradle.org/current/userguide/declaring_repositories.html
```

#2. とりあえずの解決策

projectの方のbuild.gradleファイルで、下のようになっているものを

```build.gradle
buildscript {
    ext.kotlin_version = "1.5.0-release-764"
    repositories {
        google()
        mavenCentral()
    }
```
このkotlin_versionから尻尾の部分を取り除きます。

```build.gradle
buildscript {
    ext.kotlin_version = "1.5.0"
    repositories {
        google()
        mavenCentral()
    }
```

私の環境では無事にビルドが成功しました。

#3. 成功はしたけれど..
致命的なエラーでは無いですが、ビルド後にこんなメッセージが

```
Please remove usages of `jcenter()` Maven repository from your build scripts and migrate your build to other Maven repositories.
This repository is deprecated and it will be shut down in the future.
See http://developer.android.com/r/tools/jcenter-end-of-service for more information.
Currently detected usages in: root project 'My Application', project ':app'
Affected Modules: app
```

自分で追加したわけじゃないのにdeprecatedで怒られる理不尽を体験しています。悲しい、取っておきましょう。

#4. 最後に
GradleのKotlin Version書き換えでとりあえずビルドは成功しましたが、また新規プロジェクトを作成するたびに、その場凌ぎの策を講じなくてはいけないのは何か釈然としません。他に良い解決策をご存知の方はぜひ教えてください。

##参考
[Can't create new Kotlin project after updating to Android Studio 4.2 - stack overflow](https://stackoverflow.com/questions/67400029/cant-create-new-kotlin-project-after-updating-to-android-studio-4-2)
新人エンジニアが入社後感じたことをまとめてみた

入社から一か月がたったため、これまでに感じていたIT業界に対する過去のイメージと今をまとめてみました。

#はじめに
ご覧いただきありがとうございます。
初めまして！にゃんどろいど([@nyandroid_it](https://qiita.com/nyandroid_it))と申します。
この春から2021年新卒でエンジニアとして働き始め、学びの多い日を過ごしてます。

以前よりこちらのサイトを利用させていただいてましたが、この度投稿にもチャレンジしました。
拙い文章ですが、ご覧いただけたら幸いです。

#これまでやってきたこと
大学ではC、Java、PHP、HTML/CSSなどに触れてきました。
また、こちらで学習しながらAndroidアプリの開発にも触れてきました。
（まだまだ公開できるレベルではありませんが……:sweat:）

アプリ開発はかなり苦労しながら学習していったので、
そうした失敗談などもふまえながら、今後記事として発信していければなと思います！

#なぜこの記事を書こうと思ったか
研修中の身でありますが、自分がそれまでに持っていた業界に対するイメージや、
実際に__「えっ！こんなこと学ぶの？」__と感じた気持ちを忘れないためにも残しておきたいと感じました。

かなり偏見を含んでしまうかとは思いますが、少しでも「そうだよなあ」と共感したり、
「新人ってそういう風に感じるんだな」などと受け取っていただけたらと良いなと思います。



#入社前／後のビフォーアフター
<details><summary>わからないことはすぐに人に聞いて解決</summary>ケースバイケースだが少し自分で立ち止まって考えてみることも必要。
自分で書いたコードの中に答えが隠れていることもある。聞くにしても、自分なりの答えを用意することを意識する。</details>

<details><summary>抽象的な理解が大事</summary>具体的な理解が__メチャメチャ__大事！
感覚的に理解していると壁にブチあたる可能性も……。そして、人に説明する場面で自ら首を絞めることになります</details>

<details><summary>未経験スタートの人もいるから大丈夫～</summary>あまあまでしたね。彼らの必死の追いつきをなめてはいけません。。。
むしろ、「えっ！？」っと思うような質問のがくるなど、未経験の方から気づかされること／学ぶことが多いです。</details>

<details><summary>よっしゃ、ためになりそうな本を読みまくる！！</summary>
読書などのインプットも大事だが、
読んだ内容を誰かと共有するなど__アウトプットすることの方が100倍__大切。
またテキストを読みことも大事だが、それよりも手を動かしてコードを書くことが大切！</details>

<details><summary>Javaだって大学で使ってたんだから、プログラミングもできるはず</summary>ツールを使って開発するなら、ツールの使い方を学ばなければならない。私自身、学生時代はエディタで書き書きしていたのですが、Eclipseを使った開発に入った瞬間撃沈しました。ツールを使いこなせることもエンジニアには必要なことなのかなあと。</details>



#おわりに
改めて見返すとと、私自身かなりなめくさっていたなあと感じました。
タイムマシンが使えるのであれば、一か月前の自分を殴り倒しに行きたいですね。:fist:
また業界のことというよりも、社会全般に対する内容になってしまったと少し反省しました。
今後もまた今の自分の気づきを書き留めたり、違った内容を発信していければと思いますので、、
お付き合いいただけたら幸いです。

:arrow_forward:Twitter：([にゃんどろいど@nyandroid_it](https://twitter.com/nyandroid_it))
spacy_streamlitで爆速で「日本のオリンピックはどうなる？」の係り受けを確認する

## spacy_streamlitとは
[**streamlit**](https://streamlit.io/)はpythonの数行のコードでウェブアプリが作れる最近注目度急上昇中のライブラリである。
そして[**spacy**](https://spacy.io/)はpython上で色々試せる自然言語処理ライブラリである。

**そのstreamlitとspacyが手を組んだだと・・**
それが、[**spacy_streamlit**](https://github.com/explosion/spacy-streamlit)である（あまり説明になっていない）。
![first_page.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/d134984c-2949-56d6-4028-6523ff9334f2.png)

基本的には[**spacy_streamlitのgithub**](https://github.com/explosion/spacy-streamlit)上のQuickstartに書かれていることと変わりないが、Quickstartは英語版のモデルを利用しているので、日本語版のモデルでも動作するか確認した。

※なお、以下の試したソースコードは[**ここ**](https://github.com/Haneko-git/japanese_spacy_streamlit_sample/blob/main/japanese_spacy_streamlit_sample.py)に置いた。

## インストールなど
※今回の実行環境は、windows10(64bit)で実施。

### spacy-streamlitのインストール

```
pip install spacy-streamlit --pre
```

### spacyの日本語モデルのダウンロード
spacyには3種類の日本語モデル(ja_core_news_sm, ja_core_news_md, ja_core_news_lg)が
公開されているが、軽く試したかったので今回はja_core_news_sm,ja_core_news_mdの二つをダウンロードした。

```
python -m spacy download ja_core_news_sm
python -m spacy download ja_core_news_md
```
### コード（実質4行）
以下コードを*japanese_spacy_streamlit_sample.py*として保存。

```
import spacy_streamlit

models = ["ja_core_news_md", "ja_core_news_sm"]
default_text = "もうすぐ梅雨。日本のオリンピックはどうなる？"
spacy_streamlit.visualize(models, default_text)
```

**・・以上。**

早速以下コマンドを実行してみる。

```
streamlit run japanese_spacy_streamlit_sample.py
```
以下のようなウェブ画面が表示されれば成功である。

![page1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/6e205aaa-17f3-0389-d968-00eeb4d79fcc.png)


上記画面のように、**Split sentences**にデフォルトでチェックが入っているので、文章毎に係り受け解析を可視化してくれる。

* ちなみに「日本のオリンピックはどうなる？」の係り受け結果はこちら。

![page2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/68572277-9601-78fa-703a-aaf93b238b55.png)

もちろん、ウェブアプリなので、**Text to analyze**上に別の文章を入力すると、入力した文章の係り受け結果を表示してくれる。

### 他にも
幾つかの結果を可視化してくれる。
※Text Classificationはspacyを理解しないと表示できなそうなので今後確認したい。

#### Named Entities（固有表現）
**オリンピック**がちゃんと**イベント**として認識されている。
※**GPE**: Geopolitical entity, i.e. countries, cities, states.
![page3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/f1d4c5ce-4270-c7cb-a55a-b38d8febd436.png)

#### Vectors & Similarity(単語類似度)
※1 デフォルトは"apple"と"orange"が入力されている。
※2 以下はspacyのja_core_news_mdモデルの結果を表示。

「日本」と「オリンピック」の単語の類似度の方が、「ギリシャ」と「オリンピック」の類似度より高い、ということを発見したりする。

![page4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/eead45e7-7715-af81-da9a-7fb3c9567258.png)

![page6.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/677576a7-94c6-7fec-c6f1-107cbbacaaa1.png)



#### Token attributes(トークンの属性)
![page5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/684711/47c2c232-2462-e393-762a-1694d5f3d025.png)

## 所感
どんな結果が出るのか試しに見たい時に、ウェブ画面上で文章や単語を入れると可視化してくれるので非常に便利で楽しい。

## 参考
* spacy_streamlit:https://github.com/explosion/spacy-streamlit
* spacy:https://spacy.io/models/ja
* streamlit:https://streamlit.io/
yukicoder contest 294 参戦記

# yukicoder contest 294 参戦記

B問題、初めての1桁番目の順位で解けて驚いた,

## [A 1499 羊が、何匹だっけ](https://yukicoder.me/problems/no/1499)

バラして、1足して、戻すだけですよね.

```python
T = int(input())
for _ in range(T):
    s = input()
    t = s.split()
    t[2] = str(int(t[2]) + 1)
    print(' '.joint(t))
```

## [B 1500 Super Knight](https://yukicoder.me/problems/no/1500)

先週のA問題と同じやんけってなった. なのでまずナイーブなコードを書く.

```python
def f(n):
    q = set([(0, 0)])
    for _ in range(n):
        nq = set()
        for x, y in q:
            for dx, dy in [(3, 0), (3, 2), (2, 3), (0, 3), (-2, 3), (-3, 2), (-3, 0), (-3, -2), (-2, -3), (0, -3), (2, -3), (3, -2)]:
                nq.add((x + dx, y + dy))
        q = nq
    return len(q)
```

実際に実行してみる.

```
>>> f(0)
1
>>> f(1)
12
>>> f(2)
65
>>> f(3)
172
>>> f(4)
297
>>> f(5)
456
>>> f(6)
649
```

どうやら *f*(*n*) = *f*(*n*-1) + (*f*(*n*-1) - *f*(*n*-2) + 34) になっているように見える. [Wolfram|Alpha](https://ja.wolframalpha.com/) に `f(n)=f(n-1)+(f(n-1)-f(n-2)+34), f(3)=172, f(4)=297` を入力したら *f*(*n*) = *n*(17<i>n</i>+6)+1 と教えてくれたので解けた.

```python
m = 1000000007

N = int(input())

if N == 0:
    print(1)
elif N == 1:
    print(12)
elif N == 2:
    print(65)
else:
    print((N * (17 * N + 6) + 1) % m)
```
Raspberrypi PicoでMicroPythonを使ってみたのでメモ

#はじめに
今話題のRaspberryPi PICOを入手したので、ネット上の先人の情報に従ってまずはMicroPythonでLチカしようと試みたが、手元のWindowsマシンのpython環境整備が面倒(Thonny導入とか。個人的にPythonはLinuxでいじることにしているが、今は手元にWSL2環境しかないためLinuxがこの手の開発には使えない)なので、これをなんとか回避してLチカできないかと模索

##とりあえずMicroPythonのFWを導入
* 先人の情報に従い、rp2-pico-20210418-v1.15.uf2をPICOにインストールする
* PICOをBOOTSELボタンを押した状態でPCに接続し、当該ファイルを外部メディアに見えているPICOのフォルダにコピーすると自動的にPICOは再起動

#次にTeraTermを起動
TeraTermに関する詳細説明は省きます。すみません

* PICOのシリアルポートが選択可能になっている
* シリアルポート設定は115200bps/8N1で(これも先人の情報)
* Enterキーを押すと、見慣れたPythonのインタプリタコンソール(>>> )が表示される

>TeraTermでなくてもシリアルコンソールソフトならどれでも使えると思います(未確認)

#TeraTermにてPythonコンソールにコードを入力
今回は以下のようなコードをサンプルに使いました

```led_blink.py
from machine import Pin
import time
led = Pin(25, Pin.OUT)
n=0
while True:
  wait = n * 0.1
  led.value(1)
  time.sleep(wait)
  led.value(0)
  time.sleep(wait)
  n+=1
  if (n%10 == 0):
    n = 0
```

これをPythonコンソールに1行1行地道に入力してもよいのですが、適当なテキストエディタ(個人的にはvim一択ですが)で上記コードを記述して全文を選択・コピーし、TeraTerm上でペーストすると簡単に入力できます。ペースト後、TeraTerm上で数回Enterを押すとプログラムが走り出します(Lチカ成功)。Ctrl-Cでプログラムは停止し、Pythonコンソールに戻ります。
>たまにCtrl-Cに反応しなくなる場合があります。この場合はPicoのUSBケーブルを抜き差ししてTeratermを再接続すると復帰します

#最後に
Teratermを使うのはアリなのか？というツッコミはあるかもしれませんが、Windows環境にほぼ手を入れずにPICOでLチカができました。

最近本当に忘れっぽいので自分用のメモとして記します。
Solr ver.8.7.0 の kuromoji の辞書を NEologd にする。

# はじめに
Solr の 2021年に使われているような日本語、特に新語というかカタカナ語などの日本語の形態素解析、ちょっと力不足な感じがする。どうすればよいか調べたら、userdictに自分で単語を登録せよ・・・という記事を発見した。

https://www.pine4.net/Memo/Article/Archives/530

Solrのオリジナルkuromojiの辞書、多分、200x年のものだから、すでに10年以上前のもの・・・。10年間の新語を入力しろ、ファイルを手作業で編集しろだと？

そんなのやってられません。

辞書情報をどこかから取ってきてコピーするとか方法があるだろう、と素人ながらに思いつき、誰か先達がやっていないかと調査 on Google。ありがたいことに先輩がいらした。

https://pandanote.info/?p=2441

https://pandanote.info/?p=4637

私が使いたいとは、clusteringアプレットが使えるラストバージョンの Solr ver.8.7.0.

先達のgithub を参照させていただいて、ほぼ調整なしに使えました。この場を借りて pandanote.info 様に感謝を述べさせていただきます。

# 方法

環境：　Ubuntu 20.04
ビルドとインストールは、先達のおっしゃるとおり。以下、要約。

mecab と apache ant がインストールされていることを確認する。

```
  $ sudo apt install mecab libmecab-dev mecab-ipadic-utf8
  $ sudo apt install ant
```

patchのgitリポジトリをクローンし、Apache solr のソースをダウンロードする。　e.g. solr-8.7.0-src.tgz
ダウンロードしたソースを展開し、展開したディレクトリでパッチを適用する。

```
  $ git clone https://github.com/tomofu74/solr-8-kuromoji-neologd.git
  $ wget https://archive.apache.org/dist/lucene/solr/8.7.0/solr-8.7.0-src.tgz
  $ tar zxf solr-8.7.0-src.tgz
  $ cd solr-8.7.0
  $ patch -p1 < ../solr-8-kuromoji-neologd/solr-8.7.0-kuromoji-neologd.patch 
```

Neologd の github でNEologdの最新の更新日を確認。 20200910 が、このgit でのデフォルトバージョン。
確認した更新日を、 lucene/build.propertiesのneologd.properties に指定。
原作者のpandanote-infoさんに感謝の念を送りつつ、ソースのトップディレクトリで以下のコマンドを実行。 clone-neologd は、githubからのファイルダウンロードの時間も含めて20分弱かかる。コンソールの表示が更新されないが、不安に負けずにガチホ。 筆者の環境では、build-dict が　java error 137 で build failed。　PCを再起動して再度実行すると成功。　PCの熱暴走か？

```
  $ ant ivy-bootstrap
  $ ant compile
  $ cd lucene/analysis/kuromoji
  $ ant clone-neologd
  $ ant build-dict
  $ ant jar-core
```

lucene/build/analysis/kuromojiの下に以下のような名前のjarファイルが作成されます。

```
lucene-analyzers-kuromoji-ipadic-neologd-<lucene version>-SNAPSHOT-<creation date of neologd>.jar
```

インストールは、solr を一旦停止して、 7. の jarファイル を solr webアプリのディレクトリの　WEB-INF/lib の下にコピーして、original の kuromoji を排除。
solr をリスタートして、何事もなく起動すればオッケー。

# 考察
具体的にデータを示すことはできないが、個人の感触としては、だいぶ固有名詞の抽出の具合がよくなった。驚いたのは、the startup でサーチするとカタカナ語の スタートアップもピックアップしてくれること。ちょっと残念だったのは、ドローンが ド　ローン　と２語になっていたこと。これはユーザー辞書に登録する手作業で対応した。

# まとめ
Solr 8.7.0　で　kuromoji NEologd での日本語形態素解析ができるように
した。一部の新語は、ユーザー辞書に追加してフォローすることで、運用に耐えるだろう。
集中力向上テクニック集

作業効率化のために調査しました。

## 作業中
* 無音または、森や海の自然音を流す。　※拡散思考(クリエイティブ)作業中は反対に雑音として音楽を流す。
* 視界に物を置かない。※特にスマホ
* 集中力が切れてきたと感じたら別のタスクに切り替える。（集中力限界の目安：１タスクMAX60min）
* マルチタスクはオーバーヘッドが発生するため、シングルタスクで作業する。
* あらゆる通知を遮断する。
* スタンディングデスクを使用
* 上記スタンディングデスクに加えてフットステッパー等作業と並行して運動を行う。

## 作業時間外
* 勉強前に好きな音楽を聴く。
* 睡眠時間を十分にとる。
* マインドフルネス瞑想を隔日程度で実施する。

## 【番外編】記憶力を向上させたいとき
* 一定量記憶作業する毎に一定時間何も考えない様にすることで、記憶が定着しやすくなる。
* 記憶から取り出した時に記憶力が向上するため、アウトプットを怠らない。
* 人に教えることを想定して学ぶ。
* 身振り手振りをつけ、記憶との関連付け・五感に刺激を与えることで記憶力を上げる。
* 全てにおいて重要となる睡眠時間を十分にとる。
【Nuxt.js】assets/ と static/ はどう使い分ける？


# 結論 → 基本はassetsで！

基本的には assets を用い、 webpack を通さなくていいファイルや webpack で不都合が生じるファイルがあった場合に static の使用を検討すればいいようです。

また、データ容量の小さな画像は「assets」、データ容量の大きな画像は「static」に格納する。

# assetsディレクトリ

assets の場合は下記２点が static と違う点であるとわかりました。

### 特徴1

assets/ に配置した画像は、 `npm run generate` コマンド実行時に、 ハッシュ付きの特別なファイル名に変更された上で、 dist/ に展開されます。この処理がコマンド実行の度に行われることで、同じファイル名で画像が変更された際のキャッシュ管理に役立ちます。

```.html
<!-- このように変換（base64の部分が実際のデータ） -->
<img src="data:image/png;base64,iVBORw0KGgoAAAANSU ... 5ErkJggg==">
```

これにより、**過去のキャッシュの影響を受けない**ことがメリットです。
つまり、ブラウザが過去に訪問したWebサイトに変更があり画像が変わっていたとしても、ファイル名が自動で変えられているため、過去のキャッシュではなく変更後の画像を表示してくれます。


### 特徴２

自動エンコードされ配信されるので高速化。

>
assetsから読み込んだ画像フォルダが1KB未満の小さな容量であったとき、 自動的にbase64エンコードされたデータがhtml上で展開されるため、 小さなアイコンファイル等を複数利用しているケースの画像配信が高速化されます。

[Nuxt.js で始める Webサイト制作](https://lessons.lec.cafe/nuxtjs_website/07.assets.html)


### HTML側からassets配下のファイルを参照

assers/ 配下にファイルがある場合、assets/img/my.jpg のパスで作成した画像ファイルの参照は、 以下のように記述します。

```.html
<template>
  <div>
    ...
    <img src="~assets/img/my.jpg" alt=""> 
    ...
  </div>
</template>
```

### CSS側からassets配下のファイルを参照
css ファイルの中で assets ディレクトリにリンクする必要がある場合は、下記のように記述します。

```.js
background: url('~assets/img/my.jpg');
```



# staticディレクトリ

static/ 配下のファイルは、 webpack でコンパイルされません。
これらの画像は `npm run generate` コマンドで、そのまま dist/ に展開されます。

つまり、**直接サーバーのルート配下に配置**されます。


### static配下のファイルを参照

static 直下に favicon を配置した場合、 static/favicon.ico のパスで作成した画像ファイルの参照は、 以下のように `static/` を省いて記述します。

```.html
<template>
  <div>
    ...
    <img src="/favicon.ico" alt=""> 
    ...
  </div>
</template>
```

### static配下に置くべきなのは？

- 加工した画像などの容量の大きいもの
- favicon.icoなど外部から読み込む可能性のあるもの
- 名前を変更されたくないファイル （assetsの場合、webpackにより名前を変えられてしまうので）
- 基本は CSS/JS は assets だけど、外部ライブラリの CSS/JS など、自分で編集しないファイルは static が良い？


# 参考文献
- [Nuxt.js公式 - ディレクトリ構成](https://ja.nuxtjs.org/docs/2.x/get-started/directory-structure/#assets-%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA)
- [Nuxt.js で始める Webサイト制作](https://lessons.lec.cafe/nuxtjs_website/07.assets.html)
Pythonでscrapingをしている時に同じタグの間の文字を取ってきたい時


# やりたいこと

スクレイピングをしている時に、 `<div>` で中身がまとめられておらず、次のような構造になっていることが多い。


```html
<h2>title01</h2>
<span>content11</span>
<span>content12</span>
<h2>title02</h2>
<span>content21</span>
<span>content22</span>
```

このような場合に下記のように、指定したタグごとに分割した配列でデータを順番に見たいケースではどうするかをまとめる

```python
[
  # title
  '<h2>title01</h2>',
  # 中身
  '<span>content11</span>
  <span>content12</span>',
  # title
  '<h2>title02</h2>',
  # 中身
  '<span>content21</span>
  <span>content22</span>'
]
```

# コード
```python
from bs4 import BeautifulSoup
import requests
import re

def scrape():
    # scraypingのお作法
    url = 'https://qiita.com/Azunyan1111/items/9b3d16428d2bcc7c9406'
    target_url = url.format(1)
    r = requests.get(target_url)
    soup = BeautifulSoup(r.text, 'html.parser')

    # 区切りたい文字で分割する
    # 一番最初と一番最後の要素は間に含まないので、対象から外す
    # ※ 一番最後のh2タグより下要素を残したい場合は [1:] にする
    elements = re.split(r'<h2>|</h2>', str(soup))[1:-1]

    # h2タグを元々の要素に戻したい場合はfor文で戻す
    # タグがいらない場合は、forを消しても大丈夫
    for index, element in enumerate(elements):
        if index % 2 == 1:
            continue
        elements[index] = '<h2>' + element + '</h2>'

    # 次のような要素が返ってくる
    # ['<h2>title1</h2>', 'content01', '<h2>title2</h2>, 'content02', '<h2>title3</h2>']
    return elements

print(scrape())
```

## note
`scrape()` では String が返ってくる。Stringの中身を再度スクレイピングしたい場合は、 `BeautifulSoup` のインスタンスを再度定義する

```python
BeautifulSoup(scrape()[0], 'html.parser')
```
reacthooksを利用して楽天APIで簡単ホテル検索

**apiを利用したことがなかったので、勉強のために実装したので備忘録**
--
開発環境
macOSX Catalina　バージョン10.15.7
VisualStudioCode

**ディズニーという文字が入ったホテルのリストを取得して、ホテル名とアクセスを表示する**
Promiseオブジェクトが
- awaitとすることでjsonとしてデータ取得ができる。
- asyncにしている関数内ではないとawaitは使用できない
- asyncは関数の定義に含めなければならない（非同期処理の指定だから）
- useEffect内で関数を実行することにより、任意の関数内でasyncを実行できる

上記の情報を考慮して記述

```
import './App.css';
import axios from 'axios';
import { useEffect, useState } from 'react';

const getHotels = () => {
  const q = "ディズニー"
  return axios.get(`https://app.rakuten.co.jp/services/api/Travel/KeywordHotelSearch/XXXXXXXXXXXXXX?&applicationId=XXXXXXXXXXXXXXXXX&format=json&keyword=${q}`)
}

const App =()=> {
  const [hotels, setHotels] = useState([])


  useEffect(async () => {
    const res = await getHotels()
    const hotels = res.data
    setHotels(hotels.hotels)
  }, [])

  // mapがうまくいかない仮説：配列になってない
  console.log(hotels)
  // console.log(hotels[0].hotel[0])
  return (
    <div className="App">
      {hotels.length > 1 && 
      // mapする時にkeyidを入れることで、後から指定してスタイル変更などができなくなる
        hotels.map((hotel, id)=>{
          console.log(hotel.hotel[0])
          return(
            <div className="hotel" key={id}>
              <div className="hotel-name">{hotel.hotel[0].hotelBasicInfo["hotelName"]}</div>
              <div className="hotel-access">{hotel.hotel[0].hotelBasicInfo["access"]}</div>
            </div>
          )
        })
      }
    </div>
  );
}

export default App;
```
Vue.js CDNとCLIで勝手が違う

4月から仕事でVue.js + Vuetify環境でシステム開発をやってます。
業務に入る前からVue.jsを使うよ、と言うことで、当初はCDNによる開発を勉強していました。
しかし実際には、サーバ側にVue CLIを導入して開発するということで、環境構築から改めて始めてみたところ、開発方法自体もCDNとは別物だと気付きました。

たぶん、突っ込みどころ満載だと思いますが、突っ込んだら大人として負けだと、生暖かい目で見て下さい。
# Vue.jsの開発環境
Vue.jsを使って開発する場合、2つの方法があります。
一つはCDN（Contents Delivery Network）を使う方法。これはHTML上で`<script>`要素を使って、CDN上にあるVue.jsのパッケージをインポートして利用する方法です。

```html:入力例
<script src="https://cdn.jsdelivr.net/npm/vue@(任意のバージョン)"></script>
```

もう一つは、CLI(Command Line Interface)を利用する方法。これはコマンドを入力することでVue.jsの開発に必要な機能を取得して利用する方法です。
例えば、仮想のWebサーバを起動した上で、Vue.jsで記述された複数のソースコードを1つのHTML+JavaScriptで記述されたページとしてビルドを行い、ブラウザにそのページを送る、あるいはビルドを行ったページソースをデプロイ（出力）することが出来ます。
## それぞれの特徴
### CDN
* クライアントだけで開発環境を完結できる(インターネット接続は必須)
* 開発環境の構築が不要
* コンポーネント化する際に、HTML、CSS、Vue.js部分を別ファイルで記述する必要がある
  * `http-vue-loader.js`を読み込むことでvueファイルの利用が可能
* SPA(Single Page Application)としての構築が難しく、ページごとの内容、ソースコードがブラウザから閲覧できてしまう。
  * 上記方法でSPAも一応は可能。 

### CLI
* VueファイルにすることでHTML、CSS、Vue.jsをまとめることが出来る。
* SPAとしての構築が比較的容易（ページ内容、ソースコードがバレにくい）
* デプロイすることで、静的サーバ上でも運用可能
* サーバ側にNode.js、npmをあらかじめインストールする必要がある
* `@vue/cli`などのパッケージをインストールする必要がある（追加パッケージがなければ随時インストール)
* リロード設定を入れることで、ソースの更新に併せて表示ページの更新が可能
  * `vue.config.js` 等の設定ファイルを更新した場合は、サーバの再起動が必要

気軽に始めるならCDNでもかまいませんが、セキュリティ面を考えて、厳密なSPAとして構築するなら、サーバを用意してCLIで構築する方がいいと思います。

職場に於いては、WindowsマシンにWSL2(Ubuntu)とDocker + Docker Composeを導入し、Vue CLI用のコンテナを構築して開発しています。
こうすることで、`vue.config.js`を更新して再起動する必要があっても、コンテナ自体を再起動させるだけで、システムやサービスに影響を与えることなく開発を進められます。

# 参考


https://macoblog.com/vuejs-cdn-spa/
[.NET5 / Windows Forms] System.Windows.Forms.ListView の強化された機能を確認

# 前置き

.NET 5 で「System.Windows.Forms.ListView」に新機能が追加されたようです。

https://docs.microsoft.com/ja-jp/dotnet/desktop/winforms/whats-new/net50?view=netdesktop-6.0

> - System.Windows.Forms.ListView
>  - 折りたたみ可能なグループをサポート
>  - フッター
>  - 字幕、タスク、およびタイトルの画像をグループ化

とのことで日本語訳だとなんのこっちゃだったのですが、これらすべて ListView の Group 機能に関するものでした。


## 折りたたみ可能なグループをサポート

[ListViewGroup.CollapsedState](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.listviewgroup.collapsedstate?view=net-5.0) を指定することで、グループを折りたたみ可能にすることができます。
`Collapsed`を指定すると折りたたまれた状態で表示。
`Expanded`を指定すると展開された状態で表示されます。


## フッター

[ListViewGroup.Footer](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.listviewgroup.footer?view=net-5.0) に、文字列でグループごとのフッターを指定できます。
[FooterAlignment](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.listviewgroup.footeralignment?view=net-5.0)で水平方向の位置も指定できます。

## 字幕、タスク、およびタイトルの画像をグループ化
### 字幕 = サブタイトル

[Listviewgroup.Subtitle](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.listviewgroup.subtitle?view=net-5.0)に、文字列でグループごとのサブタイトルを指定できます。

### タスク = タスクリンク

[Listviewgroup.Tasklink](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.listviewgroup.tasklink?view=net-5.0)を設定することで、グループごとにリンクを表示させることができます。
このリンクをクリックすると[Listview.GroupTasklinkClick](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.listview.grouptasklinkclick?view=net-5.0)がトリガーされます。

### 画像 = グループのタイトルに画像を表示できる

[ListViewGroup.TitleImageKey](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.listviewgroup.titleimagekey?view=net-5.0) または [ListViewGroup.TitleImageIndex](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.listviewgroup.titleimageindex?view=net-5.0) を指定することで、グループのタイトルに画像を表示させることができます。
ここで指定するキーとインデックスですが、これは [ListView.GroupImageList](https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.listview.groupimagelist?view=net-5.0) に対してのものになります。

# 例

ざっくりですが、以下使用例になります。

![listview.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/23789/67693269-77e1-887f-03de-818b5efac4d3.png)

```cs
            ImageList imageList = new ImageList();
            imageList.Images.Add("sample", Bitmap.FromFile(@"D:/\Users/\shima/\Pictures/\qiita-brand-color.PNG"));
            myListView.GroupImageList = imageList;

            myListView.GroupTaskLinkClick += MyListView_GroupTaskLinkClick;
```

```cs
            foreach (ListViewGroup listViewGroup in myListView.Groups)
            {
                listViewGroup.CollapsedState = ListViewGroupCollapsedState.Expanded;
                listViewGroup.Footer = $"「{listViewGroup.Header}」のフッター";
                listViewGroup.FooterAlignment = HorizontalAlignment.Center;
                listViewGroup.Subtitle = $"「{listViewGroup.Header}」のサブタイトル";
                listViewGroup.TitleImageKey = @"sample";
                listViewGroup.TaskLink = $"「{listViewGroup.Header}」のタスクリンク";
            }
```
Teachable Machine の機械学習モデル出力に関するメモ 〜音声・ポーズプロジェクト編〜【2021年5月版】

Teachable Machine について以下の記事を書いたのですが、それと似た話の記事です。
以下の記事では画像プロジェクトの話を扱いましたが、この記事では音声プロジェクトとポーズプロジェクトの話を扱います。

●Teachable Machine の機械学習モデル出力に関するメモ 〜画像プロジェクト編〜【2021年5月版】 - Qiita
　https://qiita.com/youtoy/items/685d97e0acc5cfc184c0

## 音声プロジェクトの機械学習モデル
### 出力の方法と形式
音声プロジェクトの機械学習モデルを出力する際の画面は以下のとおりです。
![音声プロジェクトでの出力（TensorFlow.js）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/85680fd3-6877-51b8-fb6c-ed286b5a079e.jpeg)

![音声プロジェクトでの出力（TensorFlow Lite）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/aeef7c8b-1795-ef11-0bc9-18ebab1f4b5b.jpeg)

画像プロジェクトの場合の出力形式は「TensorFlow.js、TensorFlow、TensorFlow Lite」の 3つがありますが、音声プロジェクトの場合は 2つになります。

上記の画像では、出力形式で TensorFlow.js が選択されていますが、その横に TensorFlow・TensorFlow Lite の 2つもあります。
ここで、出力できる方法・形式をまとめてみます。

- クラウドにアップロード
   - TensorFlow.js
- ダウンロード
   - TensorFlow.js
   - TensorFlow Lite

### ダウンロードして得られるデータについて
以下の内容は、バックグラウンドノイズと 1つのクラスを学習させて、機械学習モデルを各種形式でダウンロードした場合に得られたファイルの情報です。

- TensorFlow.js
   - metadata.json
   - model.json
   - weights.bin
- TensorFlow Lite
   - soundclassifier.tflite
   - labels.txt

### 出力したモデルの利用について
機械学習モデルの出力形式は複数の形式を選択できますが、それぞれをプログラムで利用する際に用いる言語等は、提示されるコードスニペットをベースにすると以下のようになります。

- TensorFlow.js
   - JavaScript（TensorFlow.js か ml5.js と組み合わせ）
- TensorFlow Lite
   - Kotlin

機械学習モデルを用いる場合のコードスニペット・手順について、上記の中の一部はコード・手順だけでなく GitHub へのリンクも表示されます。

- [TensorFlow.js を利用する場合のコードの関連リンク](https://github.com/googlecreativelab/teachablemachine-community/tree/master/libraries/audio)
- [ml5.js を利用する場合のコードの関連リンク](https://editor.p5js.org/ml5/sketches/SoundModel_TM)
- [TensorFlow Lite の機械学習モデルを Android で利用する場合](https://github.com/googlecreativelab/teachablemachine-community/blob/master/snippets/markdown/audio/tflite/Android.md)

## ポーズプロジェクトの機械学習モデル
### 出力の方法と形式
上記の音声プロジェクトを見ていった時と同様に、出力方法・形式を確認します。

- クラウドにアップロード
   - TensorFlow.js
- ダウンロード
   - TensorFlow.js

なお、出力を行う際の画面は以下のようになっています。
![ポーズプロジェクトの場合.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/1a2e5e2b-7ba7-3e34-8d29-76895de1523a.jpeg)

### ダウンロードして得られるデータについて
以下の内容は、2つのクラスを学習させて、機械学習モデルをダウンロードした場合に得られたファイルの情報です。

- TensorFlow.js
   - metadata.json
   - model.json
   - weights.bin

### 出力したモデルの利用について
機械学習モデルをプログラムで利用する際に用いる言語等は、提示されるコードスニペットをベースにすると以下のようになります。

- TensorFlow.js
   - JavaScript

機械学習モデルを用いる場合のコードスニペット・手順について、上記はコード・手順だけでなく GitHub へのリンクも表示されます。

- [TensorFlow.js を利用する場合のコードの関連リンク](https://github.com/googlecreativelab/teachablemachine-community/tree/master/libraries/pose)
PythonでSlackにbotを作成する

#完成例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659469/10748d8b-5e9d-4653-ce77-caf1979a9dbf.png)




#slack apiにアクセスしてアプリを作成
https://api.slack.com/




「Create a custom app」よりアプリを作成します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659469/fc68b844-8ad6-1d42-fa48-dd9f86eef358.png)


インストールするSlackのワークスペースを選択します。


#Bot用のスコープを追加してアプリをワークスペースにインストール

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659469/9fd46a1d-eea3-eb60-86d3-d6be66871985.png)

OAuth & Permissionsを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659469/2d56fc20-d2b2-8d48-82a7-8ad6e2c3102b.png)

Add an OAuth を選択してchat:writeをスコープに追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659469/76fa2189-5958-30bc-aa09-5ed07422a5cc.png)

ワークスペースにインストールします。











#bot（アプリ）をメッセージを投稿したいチャンネルに追加


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659469/8ea9e8ce-dc8f-3e52-e8b6-5ae122a18ed4.png)

botを追加したいチャンネルの詳細メニューのその他からインストールしたアプリを追加します。(ワークスペースにアプリをインストールすると表示されます。）


#Pythonのコード



```python:example.py
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError


def SendToSlackMessage(message):
    client = WebClient(token='your_token') 
    response=client.chat_postMessage(channel='your_channel', text=message)

SendToSlackMessage("Helloworld!")
```
slack_sdkをpipでインストールする必要があります。
your_tokenにはslack apiに記載されているOAuth Tokenを入力します。
your_channelにはブラウザからslackを立ち上げると識別できるチャンネルIDを入力します。


#OAuth Token
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659469/6b5109ca-563f-92de-b6b3-43f0157e80cd.png)

#チャンネルID
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659469/1f0a5974-d4d2-1a9b-187c-76569552f4de.png)





#実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659469/e2867dc3-482a-b252-64d3-a65ff07ac800.png)
Rails 7つのアクション

# Rails 7つアクション

・index　→　一覧表示

・show　→　詳細表示

・new　→　生成

・create　→　保存

・edit　→　編集

・update　→　更新

・destroy　→　削除

# resourcesメソッド
7つのアクションへのルーティングを自動生成するメソッドです。
resourcesの引数に、:tweetsとシンボルで指定すると、/tweetsのパスに対応するルーティングが生成されます。
例　resources :tweets

# onlyオプション
resourcesにオプションとしてonlyを加えると指定したアクションのみのルーティングを自動生成します。
例　resources :tweets, only: :index
render関数


Hello World
-

テキストHello Worldをrender関数を利用してブラウザ上に表示させるためには以下のような記述が必要となります。createElementでは第一引数に要素名、第二引数に要素の中に入る文字列を記述しています。

render関数の基本
-

render関数は<template>と同じく、Vueコンポーネントにテンプレートを定義するためのものです。
render関数は引数にcreateElementを受けるので、それをhという名前にして使うのが一般的な作法です。

```vuejs
render: function (createElement) {
  return createElement(App)
}
```

こうなって...

```vuejs
render (createElement) {
  return createElement(App)
}
```

短くなって...

```vuejs
render (h) {
  return h(App)
}
```

こうなる。

```vuejs
render: h => h(App)
```


```vuejs
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
  <div id="app">
  </div>
<script>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World'
  },
  render: function(createElement){
    return createElement('h1',this.message)
  }
})
</script>
</body>
</html>
```

![20210508-000110.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960935/0058c096-9459-e108-bbcc-0cf6d22a59bf.png)


styleの設定もする
-

```vuejs
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.js"></script>
  <div id="app">
  </div>
<script>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello World'
  },
  render: function(createElement){
  return createElement('h1',{ style: 'background-color:red;color:white;' },this.message)
}
})
</script>
</body>
</html>
```

第１引数はタグ・第２引数はタグの開始タグ終了タグに挟まれた「テキスト情報」もしくは「値」が入ります。

![20210508-000333.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/960935/0d08ec47-268d-6dfd-345b-ff0a6c51241b.png)



>これでわかるvue.jsのrender関数
>https://reffect.co.jp/vue/vue-js-render