OmniAICreator/Qiita-1.07M huggingface dataset:
Firebaseにてデプロイエラー



# 問題、原因
FirebaseHostingを使ってWebアプリをデプロイしようとした際に以下のエラーが発生

>Error: Your project ec-app-34154 must be on the Blaze (pay-as-you-go) plan to complete this command. Required API cloudbuild.googleapis.com can't be enabled until the upgrade is complete. To upgrade, visit the following URL:

![スクリーンショット 2022-04-13 19.45.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/987057/7db3dc05-3db2-0b89-2fb5-f7535c94f6e3.png)

原因は`無料プランであるのにもかかわらず、「Cloud Function」をデプロイしようとしたから`です。

# 解決策
FIrebaseのサービス名を指定してすることでCloudFunctionをデプロイ時に使用しないようにすることで解決。

```
$ firebase deploy --only hosting,storage,firestore
```

https://firebase.google.com/docs/cli#partial_deploys
Codewars を Clojure で解くために babashka を使う

spec と test を行うためのテンプレート

```edn:bb.edn
{:paths ["src"]
 :deps {org.babashka/spec.alpha {:git/url "https://github.com/babashka/spec.alpha"
                                 :git/sha "644a7fc216e43d5da87b07471b0f87d874107d1a"}
        org.clojure/core.specs.alpha {:mvn/version "0.2.62"}}}
```

```clojure:example_test.clj
(ns example_test
  (:require
   [clojure.core.specs.alpha :as csa]
   [clojure.spec.alpha :as s]
   [clojure.spec.test.alpha :as stest]
   [clojure.string :as str]
   [clojure.test :as t :refer [deftest is]]))

;; spec
(s/def :ex/vnum3 (s/coll-of number? :kind vector? :count 3 :distinct true :into #{}))
(prn (s/conform :ex/vnum3 [1 2 3]))
(prn (s/explain :ex/vnum3 #{1 2 3}))
(prn (s/explain :ex/vnum3 [1 1 1]))
(prn (s/explain :ex/vnum3 [1 2 :a]))

(s/def :ex/seq-of-keywords (s/* keyword?))
(prn (s/explain :ex/seq-of-keywords [:a :b :c]))
(prn (s/explain :ex/seq-of-keywords [10 20]))

;; define function

;; test
(deftest a-test
  (testing "FIXME, I fail."
    (is (= 0 1))))

(t/run-tests 'example_test)

```

テストをコピーペーストして

```zsh
% bb example_test.clj
```
Vue Routerのrouter-linkを使わずにボタン押下で引数を渡して画面遷移する

Vue Routerによる画面遷移の情報が```router-link```を使うものが多かったので備忘録。

### 下準備
Vue.jsでプロジェクトを作成したらVue Routerをインストールする。
```
npm install vue-router@4
```

Vue Routerによるルーティング先の登録用のJSファイルを作成する。これが画面遷移の案内板となる。


```js:router.js
//Vue Routerを使うためにインポート。
import { createRouter, createWebHistory } from 'vue-router'
import MainPage from 'MainPageの場所'
import OtherPage from 'OtherPageの場所'

export const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'main',
      component: MainPage
      //引数のないコンポーネントは上記3つで十分。
    },
    {
      path: '/other',
      name: 'other',
      component: OtherPage,
      props: route => ({id: route.params.id, text: route.params.text})
      //引数がある場合はpropsを指定しておく。ここでは関数の書き方で引数を指定する。
    }
  ]
})
```

上記で作成した案内板を```main.js```で登録し、アプリケーション全体で利用できるようにする。
（余談だがVue.js 3ではまだbootstrap-vueが使えないので普通のBootstrapを使う。）

```js:main.js
import { createApp } from 'vue'
import { router } from 'router.jsの場所' //上記で作ったrouterをインポート
import App from 'App.vueの場所'
import 'bootstrap/dist/css/bootstrap.css'

const app = createApp(App)
app.use(router) //routerをアプリケーション全体で使えるようにする
app.mount('#app')
```

最後に画面遷移で切り替えたい箇所に```router-view```を書く。

```vue:App.vue
<template>
  <!-- ここでrouterに登録したコンポーネントが切り替わる-->
  <router-view></router-view>
</template>

<script>
export default {
  name: 'App'
}
//その他処理を記載
</script>

<style>
/* CSSを記載 */
</style>
```
これで下準備は完了。

---
### ボタンに画面遷移の処理を追加
routerに登録したコンポーネントにボタンを追加し、ボタン押下時の画面遷移先と渡す引数を設定する。

```vue:MainPage.vue
<template>
  <button 
    type="button" 
    class="btn btn-primary" 
    @click="() => $router.push({ name: 'other', params: { id: userId, text: userText } })" >
    Button Name
  </button>
</template>

<script>
export default {
  name: 'MainPage',
  data() {
    return {
      userId: '12345',
      userText: 'this is other page'
    }
  }
}
</script>

<style>
/*CSSを記載*/
</style>
```
```@click```以下でボタン押下時のイベントを定義する。（```@click```は```v-on:click```でも可）
```name```には```router.js```の```name```に定義した名前を記載し、```params```には```router.js```の```props```で定義した通りに引数を記載する。
```router.push```は```params```がない場合は```path```を記載して遷移することも可能だが、```params```がある場合は```name```でなければ警告が出て動作しないので注意しよう。

```vue:参考：paramsがない場合
<button
  type="button"
  class="btn btn-primary"
  @click="() => $router.push({ path: '/other' })">
```

あとは遷移先の画面で```props```を使って引数を受け取れば遷移は無事完了。

```vue:OtherPage.vue
<template>
  <div>
    <!-- 受け取った引数を表示 -->
    <div>{{ id }}</div>
    <div>{{ text }}</div>
  </div>
</template>

<script>
export default {
  name: 'OtherPage',
  props: {
    //ここで引数を受け取る
    id: String,
    text: String
  }
}
</script>

<style>
/*CSSを記載*/
</style>
```
---

#### 参考
**Vue Router公式**
https://router.vuejs.org/guide/essentials/passing-props.html#passing-props-to-route-components
https://router.vuejs.org/guide/essentials/navigation.html#programmatic-navigation
Athena格闘記録(WAFログのBlock動作・IPアドレス＆ユーザーエージェントで絞り込む)


WAFのログ調査をするべく、Athenaを使用したのですが
中々苦戦したので、備忘録です。

※もっと改善できるとは思うのですが、力及ばず断念してしまったので、
何かあればコメントいただけると嬉しいです※

<br>

**●調査ポイント**
期間範囲指定あり
対象IPアドレス＆ユーザーエージェントで接続元を調査したい
Blockログのみ抽出できれば良い
<br>

**●参考にしたブログ**　\ありがとうございました/
https://qiita.com/hkato/items/610cd54bdea395da7cd1

なんとなくログ抽出はできていたのですが、
ユーザーエージェントで絞り込めていないことに気がついてからというもの、エラーとの長い戦いでした。
<br>
**上記を参考に、最終的に実行したログはこちら。**


~~~
SELECT from_unixtime(timestamp/1000, 'Asia/Tokyo') AS JST,
httpsourcename,
terminatingruleid,
rulegrouplist,
action,
httprequest.clientip,
httprequest.country,
httprequest.httpmethod,
httprequest.headers,
httprequest.uri,
requestheadersinserted,
httprequest.args
FROM "★DB名★"."★テーブル名★",
  UNNEST(httprequest.headers) AS t(f)
WHERE action = 'BLOCK'
AND date(from_unixtime(timestamp/1000, 'Asia/Tokyo')) = date '2022-04-01'
;
~~~
<br>

**●改善したい点**
①dateで期間範囲指定したい(betweenで範囲指定出来るか？！と思ってトライするもエラー続きで断念)
②UNNEST部分(出力したhttprequest.headers部分がとても長くなってしまい、調査時やや煩わしかったため。ここを工夫できれば改善可能？)

<br>
<br>
Athenaとの戦いはまだまだ続く…
Qiita ユーザーページの RSS に件数制限がついたので、API から RSS に変換する Cloudflare Workers を作った

タイトルの通りです。

https://github.com/su8ru/qiita-feed

私のポートフォリオでは、RSS を用いて Qiita や Zenn の記事を取得して一覧表示しているのですが、先日、古い記事が表示されていないことに気づきました。原因を探していくうちに、Qiita の Feed から直近 4 件しか取得できていないことに気づき、[GitHub Discussions にて質問](https://github.com/increments/qiita-discussions/discussions/204)したところ、「RSSで取得できる件数は、4/6に4件にする仕様変更を行いました」とのお返事をいただきました。

> RSSの機能はRSSリーダーなどで投稿された記事をリアルタイムで確認するための用途として公開しております。
一方、QiitaはAPIも公開しており、こちらはQiitaにあるデータを取得するための用途となっております。

この説明には納得できますし、「パフォーマンス改善の目的」らしいので、仕方ないのかなとも思います。

ただ、RSS は複数のサイトにまたがる投稿をまとめて取得・管理する際に非常に便利であり、また RSS 方式から API 方式への移行も ~~面倒~~ すぐにはできないため、Qiita API から全記事取得して RSS 形式で返してくれる Cloudflare Workers を作りました。

# 技術スタック

## Hono

Cloudflare Workers に特化した、めちゃくちゃ速いルーティングライブラリです。

今回はあまりルーティング関係無いのですが、[私が「Cloudflare Workers おもしろそう！」って思ったきっかけ](https://zenn.dev/yusukebe/articles/0c7fed0949e6f7)のライブラリでもあるのでヘビーユースしています。

https://github.com/yusukebe/hono

## aspida

API 通信に型を付けるラッパーライブラリです。

今回は `/v2/items` しか叩いていないのであまり目立った活躍はしていませんが、`@aspida/fetch` と組み合わせることで fetch の結果に一瞬で型が付きます。

https://github.com/aspida/aspida

## Obj to XML

Cloudflare Workers は Node.js ではないので、Node.js に依存しないライブラリを使用する必要があるのですが、なかなかいいライブラリが見つからず、結局[手書き](https://github.com/su8ru/qiita-feed/blob/3841b84f4e6615bdc51cabf61f528ebe568f2ad8/src/index.ts#L18-L48)してしましました。

ただ、Cloudflare Workers で Node.js を～ みたいな記事はちょくちょく見かけるので、もしかしたら動かす方法があるのかもしれません。勉強不足を痛感します。

# 使ってね！

https://zenn.dev/catnose99/articles/cb72a73368a547756862

↑の記事のように、RSS を使って記事を集約するというのは思ったより多くの需要があるのかもしれません。
かくいう私も、この記事に影響を受けてポートフォリオに Qiita, Zenn への投稿記事を一覧表示していますし。

Cloudflare Workers 無料枠は 24 時間あたりのリクエスト数に上限があるため URL を貼ることはしませんが、MIT License で公開しているので、Fork などしてご自由にお使いください。
特に設定するようなこともなく簡単なので、RSS の件数制限で困っている人はぜひデプロイしてみてください。

また、Issue や Pull Request についても受け付けていますので、なにかありましたらどうぞ！

https://github.com/su8ru/qiita-feed
Windows PC へのPython3 導入手順


# 1.はじめに

Windows10 の環境に Python3 を導入します。

※ Mac PCへの Python3 導入手順は、以下リンク先をご参照ください。

・Qiita：Mac PC へのPython3 導入手順
https://qiita.com/Haruka-Ogawa/items/b1bf5ad9b28d44702a64


# 2. Python3 導入

### ① ダウンロード

まず、公式サイトからPythonを ダウンロードします。
最新版は以下リンク先を開くと取得することができます。

・Python：Download the latest version for Windows
https://www.python.org/downloads/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174250/0a081064-85f4-1ebe-cfdb-7374cd5f5d93.png)


今回は Python **3.10.4** をダウンロードします。
[Download Python 3.10.4]ボタンをクリックすると、ダウンロードが開始されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174250/9d327b43-89e4-9c90-0ba0-96c96c8f9850.png)



### ② インストール

Python のインストールを行います。

ダウンロードされたexeファイルを実行すると、以下のような画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174250/bcf24ac0-0013-18f2-30a4-e92e02868e86.png)


導入後 Pythonを使用するには PATHを設定する必要があります。
手動でPATHを設定しても良いですが、
ここではあらかじめ [Add Python 3.10 to PATH]にチェックを入れて、インストール時にPATHを設定するようにしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174250/32b340ab-ee0d-35f2-a587-4deae0b1c5b3.png)


[Install Now]をクリックすると、インストールが開始されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174250/b946f6aa-2404-62f3-e08e-46ef876d70bb.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174250/441d1c12-3a43-a359-93dc-17c996cd209a.png)


[Setup was successfull]　画面が表示されたら、[Close]ボタンをクリックして 画面を閉じます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174250/204a5d0d-a516-e9f2-458c-58d59f86ad22.png)


### ③ 確認

Python **3.10.4** の導入が完了したら、
コマンドプロンプトを起動して Pythonのバージョンを確認し、無事導入されていることを確認します。

コマンドは以下の通りです。

```
python -V
```

実行例は以下の通りです。

```
C:\>python -V
Python 3.10.4
```

Python **3.10.4** が導入されたことが確認できました。

さらに、コマンドプロンプトでpythonコマンドを実行すると、
対話モードが開始されて、様々な処理を実行することができます。

```
C:\>python
Python 3.10.4 (tags/v3.10.4:9d38120, Mar 23 2022, 23:13:41) [MSC v.1929 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> print("hello world")
hello world
```

対話モードを終了する場合は、quit() を入力します。

```
>>> quit()

C:\>
```


# 3. おわりに

今回は、Windows PC への Python 導入を行いました。

Python導入には 他にも方法がありますが、exeファイルを実行する方法が 最も簡単で手っ取り早いです。

以前の記事 [Mac PC へのPython3 導入手順](https://qiita.com/Haruka-Ogawa/items/b1bf5ad9b28d44702a64)では、Python3の導入にpyenvを使用しましたが、
Windows PCでも pyenv（pyenv-win）を使用することが可能です。

pyenvでは、Pythonのバージョンを管理することができ、
複数バージョンを切り替えて使用したい場合は、pyenvの使用を検討するとよいと思われます。

# 参考情報

・Python.jp プログラミング言語 Python情報サイト：Windows版Pythonのインストール
https://www.python.jp/install/windows/install.html
[firebase] Local Emulator Suiteをdocker化してみたが、データの永続化ができない場合

# 問題

とりあえずローカルマシンに環境依存をなくしたいので、あらゆるものをdocker化したい。[Local Emulator Suite][1]を今回やってみたが、公式のイメージはない模様。

自作してみて動くのは動くが、

```shell
$ docker-compose up
...
...
```

のように動かしておいて、`ctrl+c`をして止めると再起動時にデータが保持されない。

これは`firebase emulators:start --import data --export-on-exit`として起動しても変わらず...

# 原因

どうもdocker-compose側で`ctrl+c`をサービス内まで届けてない？様子で色々検索して試したがうまいことINTシグナルを渡すことができなかった。

```shell
$ docker-compose -v
docker-compose version 1.29.1, build c34c88b2
```


# 試したこと

https://stackoverflow.com/a/65507904/1227960

これは一見動きそうだが、ctrl+c時にtrapされている様子はなかった。

https://qiita.com/furu8ma/items/d92bdb6323a776c856b8

これを使って`docker-compose down`の代わりに`docker exec container_name firebase emulators:export -f export/data`とかしてみてもtrap事前にサービスが止められてる感じだった。

# 解決方法

https://tech.anti-pattern.co.jp/firestore-local-emulator/

この人のパターンのように定期的にデータをexportするようにした。
shellはよく分からないのでrubyで...

```rb:export.rb
loop do
  sleep 10
  puts "###############"
  puts "exporting ..."
  puts "###############"
  system("firebase emulators:export -f export/data")
end
```

```sh:startup.sh
ruby ./export.rb &
firebase emulators:start --import export/data
```

こうすることで10秒置きにとりあえずデータは保持されるので、なんとかなる。

> `export`はdocker-composeで指定しているvolume

```yml

  firebase:
    ...
    ...
    volumes:
      - export:/firebase/export
    ...
    ...

volumes:
  export: {}
```



[1]: https://firebase.google.com/docs/emulator-suite/install_and_configure?hl=ja
SQL オンライン学習サイトのまとめ


# オンラインでSQLを学べるサイト
オンラインでSQLを実行して学ぶことができるサイトのまとめです。
日本語のサイトは少ないですが、英語が問題ない人は結構な量のSQLを学ぶことができます。


# Beginner SQL Learning
[リンク](https://learnsql.nocde.net/)
日本語対応
![1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/55b0a626-1f1e-7a99-e9cc-015be44b89d6.jpeg)

# ＳＱＬ攻略
[リンク](http://sql.main.jp/)
日本語対応
![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/c143b5bb-3d08-40fb-62f6-860fff5bbc78.jpeg)

# SQLZoo
[リンク](https://sqlzoo.net/wiki/SQL_Tutorial)
英語のみ
![3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/a399112f-aea8-1803-e098-3f71401b0555.jpeg)

# SQLBolt
[リンク](https://sqlbolt.com/)
英語のみ
![4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/e9e5f16b-47ad-b961-1a7b-5273aeb13963.jpeg)

# Select Star SQL
[リンク](https://selectstarsql.com/)
英語のみ
![5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/9e9054c7-cafd-6ab1-ac69-4dd4c461ae74.jpeg)

# ByteScout SQL Trainer
[リンク](https://app.bytescout.com/sql-trainer/)
英語のみ
![6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/903c9d81-d840-39e8-e128-52df62bb23a7.jpeg)

# SQL Teaching
[リンク](https://www.sqlteaching.com/)
英語のみ
![7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/6eb69d86-6611-1635-c6e2-af8bec06de34.jpeg)

# PostgreSQL Exercises
[リンク](https://pgexercises.com/questions/basic/selectall.html)
英語のみ
![8.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/0b56cd24-a7dc-1c07-533b-8997579462e5.jpeg)

# GalaXQL
[リンク](http://sol.gfxile.net/g3/)
英語のみ
![9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/184aaa20-aa52-a982-545e-d2add0eb6b9b.jpeg)

# SQL Online IDE
[リンク](https://sqliteonline.com/)
英語のみ
![11.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/e5a66cb1-0aff-5d43-8369-deb415c2ce68.jpeg)

# Online SQL IDE
[リンク](https://www.jdoodle.com/execute-sql-online/)
英語のみ
![12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561267/ea336dad-f308-1438-2052-0980a1bf1851.jpeg)
Rで量質混在データの処理tips

##### この記事では、備忘録としてメモを書いていきます。

データの構造が数字と文字が混在している場合がデータ全体を一気に処理したい。

サンプルデータはirisデータセットを用いてる。
![iris.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2298986/a938cdfa-f045-256f-13c6-050c3fb1ebe9.png)

## 例１.行ごとに合計値を計算したい。
```R:
t(apply(mat, 1, function(row) sum(row)))
```
「Species」列は文字列タイプなので、エラーになった。
```：
Error in sum(row) : invalid 'type' (character) of argument
```
もっと効率ありの書き方をやってみる。
```R:
rowSums(iris)
```
前と同じ理由で「Species」列は文字列タイプなので、エラーになった。
```：
Error in rowSums(iris) : 'x' must be numeric
```
rowSumsは数値タイプの列のみ応用する
```R:
iris %>% mutate("合計値"=rowSums(iris[sapply(iris, is.numeric)], na.rm = TRUE))
```
成功した。イメージは↓
![iris2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2298986/552fbd6a-d4bb-d57d-1fef-bea344a58996.png)

## 例2.混在データの小数点の切り捨て問題
普通のroundを利用する
```R:
round(iris, digits = 0)
```
まだ「Species」列は文字列タイプなので、エラーになった。
```：
Error in Math.data.frame(list(Sepal.Length = c(5.1, 4.9, 4.7, 4.6, 5,  : 
  non-numeric variable(s) in data frame: Species
```
modify_ifを利用してみる。
```R:
iris %>% modify_if(~is.numeric(.), ~round(., 0))
```
今度は成功した。イメージは↓
![iris3.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2298986/76ba1eb4-3694-a2fd-1ca4-317f17390f5e.png)

合計値計算の部分一部コードを↓のページから：
https://qiita.com/hoxo_m/items/d19d92130c4f6bbafcda
***
## 免責
この記事は個人のメモであり、所属する企業や団体とは関係ございません。
Salesforceの画面フローでレコードとそのレコードの添付ファイルを一画面で作成する

Salesforceの便利機能「画面フロー」

レコード作成を関連オブジェクトのレコードや活動まで一気に一画面で行えるように出来るなど、とても便利です。

で、レコードに写真や報告書などの添付ファイルを付けたい時があると思うんですが、
通常はレコードを一旦作成して、その後添付ファイルをアップします。

でも添付ファイルのアップがほぼ必須のような状況では、これもメンドクサイ！

画面フローで一気にできればラク！！
・・・なんですが、調べると意外とやり方が出て来なくてかなり苦労しました。

と言う事で、自分の備忘録としてQiita初投稿でこのやり方をアップしたいと思います。

全体像はこんな感じです。
![スクリーンショット 2022-04-13 182021.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/96228788-3371-5cfd-ed36-7f9da39e71f8.png)
![スクリーンショット 2022-04-13 182038.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/e617240b-5a5a-83da-184c-82ce8df0b2cd.png)
添付ファイルは複数アップロードを許可するようにしているのでループ処理が入っています。
単一のみであればループ処理は必要ありません。
でも、このアップロードって一気にファイルをドラッグ＆ドロップできるので、結構便利なんですよね。


で、画面要素にこのように「ファイルのアップロード」コンポーネントを設置します。
![スクリーンショット 2022-04-13 182145.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/9ede6b55-084e-2be9-2194-6f5f3ae4f309.png)

この次がポイントで、このように「ファイルのアップロード」→「詳細」→「手動で変数を割り当て」をチェック→「コンテンツドキュメントID」にテキストコレクション変数（今回は"TextCollectionContentsDocumentId"）を割り当てます。
![スクリーンショット 2022-04-13 182256.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/c770f8ad-ff11-2112-65c4-ed7f00029729.png)

添付ファイルの処理の前に、先に添付される側のレコードを作成しておきます。
![スクリーンショット 2022-04-13 182547.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/bfa7aab2-97fa-90c9-1232-8fe384ff6fe1.png)

先ほどコンテンツドキュメントIDを割り当てたテキストコレクション変数でループ処理を行います。
![スクリーンショット 2022-04-13 182706.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/3a604554-e652-621e-bca6-e1ca57cf7b17.png)

ループの中で割り当て処理に使うレコード変数を作成しておきます。
レコード変数は「コンテンツドキュメントリンク」というオブジェクトで作成します。
単一変数とコレクション変数を作っておきます。
![スクリーンショット 2022-04-13 183240.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/1943e2d5-ced5-c3d4-9bed-d6353d7f2701.png)
![スクリーンショット 2022-04-13 183517.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/dd1cc406-7e54-84f5-7f09-fde052bf4059.png)

ループ内の割り当てで、
コンテンツドキュメントリンクの単一レコード変数の「コンテンツドキュメントID」にループの現在の値、「リンク済みエンティティID」に先ほど作成した添付される側のオブジェクトIDを割り当てます。
![スクリーンショット 2022-04-13 183742.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/77edba65-ff90-8f88-8622-5c70f6f8717d.png)

ここまでくれば後は簡単ですね。

今色んなIDの値を割り当てられた単一レコード変数をコレクション変数に追加します。
![スクリーンショット 2022-04-13 184039.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/ea0b8530-dd2e-215d-7dac-69cc56d0baa3.png)

最後はループの外でレコードコレクション変数からレコードを作成すれば完成です。
![スクリーンショット 2022-04-13 184204.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629350/afee5089-8e0c-d58b-5bdf-4f0e4d55092e.png)


出来てみるとなんてことはないのですが、
画面要素内のファイルのアップロードコンポーネントで、手動で変数を割り当てないといけないのにドはまりしました。
ていうか、なんで上にその項目あるねん！！
そこで変数割り当てたらイケると思うやんッ！！

あと、コンテンツドキュメントリンクっていうオブジェクトがある事にも中々気づかなくて困りました。

誰かのお役に立てば幸いです。
【init】Google ColabでLINEおうむ返しBOTを爆速で動かす

# 下準備
- Google Colabを開く(https://colab.research.google.com)
- LINE Channel SecretとChannel Access Tokenの取得(https://developers.line.biz/console)
※参考: [Messaging APIを始めよう](https://developers.line.biz/ja/docs/messaging-api/getting-started)
- ngrokのYour Authtokenの取得(https://ngrok.com/)

# 定数の定義
```python
# LINEのチャネルシークレット
LINE_CHANNEL_SECRET = '下準備の値'
# LINEのチャネルアクセストークン
LINE_ACCESS_TOKEN = '下準備の値'
# ngrokのAuthtoken
NGROK_AUTHTOKEN = '下準備の値'
```

# 必要なライブラリのインストール&ngrokの初期化
```
!pip install flask
!pip install line-bot-sdk
!pip install pyngrok

!ngrok authtoken $NGROK_AUTHTOKEN
```

# ngrokによるWebhook URL作成・設定
```python
import os

from pyngrok import ngrok
from pyngrok.conf import PyngrokConfig

os.system('kill -9 $(pgrep ngrok)')
webhook_url = ngrok.connect(addr='127.0.0.1:5000', pyngrok_config=PyngrokConfig(start_new_session=True))
print (webhook_url)
```
出力された公開urlのhttpをhttpsに変更し、
https://developers.line.biz/console
[チャネル設定画面]>[Messaging API設定]内のWebhook URLに設定。

[Messaging API設定]>[LINE公式アカウント機能]>[あいさつメッセージ]の右側[編集]をクリック。
[応答メッセージ]をオフにし、[Webhook]をオンに変更。

# おうむ返しBOTのプログラム入力
```python
from flask import Flask, request, abort

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage, ImageSendMessage,
)

app = Flask(__name__)

line_bot_api = LineBotApi(LINE_ACCESS_TOKEN)
handler = WebhookHandler(LINE_CHANNEL_SECRET)

@app.route("/test")
def test():
    return "TEST OK"

@app.route("/", methods=['POST'])
def callback():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        print("Invalid signature. Please check your channel access token/channel secret.")
        abort(400)

    return 'OK'


@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text),
    )

if __name__ == '__main__':
    app.run()
```
ただコピペ。

# LINE友達登録
友達追加をして話しかけましょう。
AWS EC2 AmazonLinux2にMySQL 5.7系をインストールする

当記事では、MySQL 5.7.36をインストールします。

各バージョンを選択したい場合は、こちらからrpmファイルのバージョン及びURLを確認して下さい。

　:paperclip: MySQL Product Archives
　https://downloads.mysql.com/archives/community/

以下の手順は、こちらのサイトを参考にしました。

　:paperclip: Amazon Linux 2 で MySQL を使用する
　https://www.acrovision.jp/service/aws/?p=736


以下、実行したコマンドを羅列します。

#### 1. デフォルトでインストールされている MariaDB をアンインストール

```ruby:command
sudo yum list installed | grep mariadb
sudo yum remove mariadb-libs
```

#### 2. MySQL公式のyumリポジトリを追加

```ruby:command
sudo yum localinstall https://dev.mysql.com/get/mysql-community-server-5.7.36-1.el7.x86_64.rpm
```

#### 3. インストールするバージョンを有効化

```ruby:command
sudo yum-config-manager --disable mysql80-community
sudo yum-config-manager --enable mysql57-community
```
ここで、以下のようにGPGキーが無効となっている場合があります。

The GPG keys listed for the "MySQL 5.7 Community Server" repository are already installed but they are not correct for this package.
Check that the correct key URLs are configured for this repository.

この場合は、こちらの参考サイトのように、以下のコマンドを実行します。

　:paperclip: [MySQL] アップデート時にGPGキーのエラーで停止してしまう場合
　https://blog.katsubemakito.net/mysql/mysql-update-error-gpg

```ruby:command
sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022
```

#### 4. MySQLのインストールを実行

```ruby:command
sudo yum install mysql-community-server
```

#### 5. インストール後、バージョンを確認する

```ruby:command
mysqld --version
```

再起動後、サービスとして稼働していることを確認できます。

※ROOTユーザのパスワードは、MySQLのログに出力されています。
　/var/log/mysqld.log
Node.js×TwitterAPIでツイートを連続取得

# search_metadataによる連続取得
TwitterAPIは一度のリクエストで最大100件までしかツイートを取得できません。
例えば`#ゲーム実況`というタグが付いたツイートを収集しようと思っても最新の100件を取得したらそこで次の取得を打ち切ってしまいます。
どうにかならないかということで調べてみたところ以下の記事がヒット。
[Node.jsでTwitter検索から100件を超えるツイートを取得したい](https://qiita.com/ryo-a/items/53fe9eadcf719b817c9a)

詳細はリンクを参照してほしいのですが、以下のようなコードで連続取得が可能になります。
```
const Twitter = require('twitter');
const client = new Twitter({
    consumer_key: "---",
    consumer_secret: "---",
    access_token_key: "---",
    access_token_secret: "---"
});

const searchTweet = async　function (maxId = null) {
    let result = [];
    const searchParam = {
         q: "#ゲーム実況"
    }
    if(maxId){
        searchParam['maxId'] = maxId;
    }
    const { statuses, search_metadata } = await client.get("search/tweets", searchParam );
    result = result.concat(statuses);
    
    // 次の取得対象ツイート存在チェック
    if(search_metadata != undefined && search_metadata['next_results']){
        maxId = search_metadata['next_results'].match(/\?max_id=(\d*)/)[1];
        result = result.concat(await searchTweet(maxId));
    }
    return result;
}
```
以下の`search_metadata['next_results']`に次のツイート情報が格納されており、それをパラメータで指定して再帰呼び出しを行うことで次の100件分ツイートを取得しています。

```
const { statuses, search_metadata } = await client.get("search/tweets", serachParam);
```
# search_metadataがない場合の連続取得
この`search_metadata`というものは全部のエンドポイントの戻り値にあるものではないようで、例えば特定のユーザの投稿内容を取得する場合はsearch_metadataを参照できません。
そんな場合はどうしたらいいんだ！と色々模索してごちゃごちゃした結果以下のように落ち着きました。
```
const getUserTimeline = async function(maxId = null) {
    const searchParam = {
         screen_name: "baito_san"
    }
    if(maxId){
        searchParam['maxId'] = maxId;
    }
    let data = await client.get("statuses/user_timeline", searchParam);
    if(data.length > 1){
        // 1件目の取得結果を除外
        data = searchParam ['max_id'] ? data.slice(1) : data;
        const maxId = data.slice(-1)[0]['id_str'];
        data = data.concat(await getUserTimeLine(maxId);
    }
    return data;
}
```
n - 1回目の取得結果の最後のツイート情報をn回目の検索パラメータのmaxIdに指定して連続取得しています。問題としてはn - 1回目の最後のツイートがn回目の取得結果の1件目に来てしまい、無駄に1件多く取得してしまっていることです。
他にうまいこと取得できる方法があればぜひコメントで教えてください。

# あとがき
今回のコードを使って自分の過去ツイートを全件取得して出力してみたのですがなかなかに痛々しくて羞恥心を覚えました。皆さんも是非自分の過去ツイートを出力してみてください。

# 参考記事・ページ
[Node.jsでTwitter検索から100件を超えるツイートを取得したい](https://qiita.com/ryo-a/items/53fe9eadcf719b817c9a)
[Twitter 開発者 ドキュメント日本語訳](http://westplain.sakuraweb.com/translate/twitter/Documentation/REST-APIs/Public-API/GET-search-tweets.cgi)
C# で２次元リストを扱う

## はじめに
先日業務にて、表題の件について調査したので共有します。


#### 配列とリストの違い
+ **配列**
宣言時に静的なメモリ確保を行うため、要素数の変更は行うことができない
※１次元配列であれば Array.Resize メソッドにより要素数の変更はできる

+ **リスト**
動的なメモリ確保を行うため、要素の追加や削除を柔軟に行うことができる

#### 配列とリストの使い分け
配列とリストの違いからもわかるように、要素数が決まっている場合は**配列**を使用し、要素数が動的に変化する場合は**リスト**を使用すると良いかと思います。また、より汎用的なプログラムを作りたいのであれば、リストを使用すると良いのではないでしょうか。

が、、

どうやら、

> C# で多次元リストを宣言するための組み込みメソッドはありません。

だそうです。。

そこで、本投稿にて要素数が動的に変化するデータを２次元で扱う方法について解説します。


## 本題

C# で２次元リストを扱いたい場合、
リストを入れ子にして多重に定義することで実現できます。

下記は二次元リストの例（**T** には任意のデータ型を指定）
***List\<List\<T>>***


#### 使用例

```C#:qiita.cs

List<List<int>> Data = new List<List<int>>();
for (int i=0; i<3; i++)
{
    List<int> Items = new List<int>();
    Items.Add(i);
    Items.Add(i+3);
    Items.Add(i*i);
    Data.Add(Items);
}

```

#### Dataリストの中身
|              |Items[0]|Itams[1]|Items[2]|
|:------------:|-------:|-------:|-------:|
|**Data[0][n]**|       0|       3|       0|
|**Data[1][n]**|       1|       4|       1|
|**Data[2][n]**|       2|       5|       4|


### ちなみに
上記の方法では１種類のデータ型のみ（例では int 型）指定できますが、下記の方法では様々なデータ型を指定できます。

#### クラスのリストとして２次元を表現する

```C#:qiita.cs

public class Member
{
    public long Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}
List<Member> Group = new List<Member>();
for (int i=0; i<3; i++)
{
    Member m = new Member();
    m.Id = i;
    m.Name = "anyName";
    m.Age = i + 20;
    Group.Add(m);
}

```


#### Groupリストの中身
|            |Id      |    Name|     Age|
|:----------:|-------:|-------:|-------:|
|**Group[0]**|       0| anyName|      20|
|**Group[1]**|       1| anyName|      21|
|**Group[2]**|       2| anyName|      22|


#### タプルのリストとして２次元を表現する

```C#:qiita.cs

var Group = new List<(long Id, string Name, int Age)>();
for (int i=0; i<3; i++)
{
    Group.Add((i, "anyName", i + 20));
}

```

#### Groupリストの中身
|            |      Id|    Name|     Age|
|:----------:|-------:|-------:|-------:|
|**Group[0]**|       0| anyName|      20|
|**Group[1]**|       1| anyName|      21|
|**Group[2]**|       2| anyName|      22|


クラスを定義しない分コード量は少なくなりますね。


## 結論
本投稿では表題の件に対して３種類の実現方法を提示しましたが、特別な条件がない場合は可読性や保守性の観点から、２種類目の**クラスのリスト**を使用するのが最適かと思います。


## おわりに
今回は私自身が実務で躓いたことについて共有させていただきました。
同じように躓いている方のお力になれれば幸いです。

最後までお読みいただきありがとうございました。
また逢う日まで。

### 参考文献
https://www.delftstack.com/ja/howto/csharp/declare-a-multidimensional-list-in-csharp/
Android6.0 ApacheHTTPClientからURLConnectionへの置き換え

*自分が失敗したならあと10人は失敗しているだろう(ゴキブリ理論)*
タイトル対応中にmultipartで詰まった、ということで失敗備忘録です。
# multipartでテキストと画像データをPost
### 以下を参考に実装
https://qiita.com/informationsea/items/778d9525c3aaded73577
### 問題発生
サーバーへテキストと画像を送信できた。がしかし、テキスト内の日本語が文字化けしている。
1.日本語以外（英字/数字）は文字化けなし。
2.画像は壊れることなく送信できている。
### 原因
`DataOutputStream`への書き込みを直接`writeBytes`で実装しているため、
`AndroidOS`標準の文字コード(UTF-8以外のなにか(未調査))でエンコードされていた。
### 対応
`writeBytes`は使用せず、一度StringBuilderであつめてからByte変換するようにした。
``` java:RestUtility.java
// 以下内容は参考元より多少の改変をしています
final String BOUNDARY=  "**BOUNDARY**";
final String lineEnd = "\r\n";

DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
StringBuilder builder = new StringBuilder();

// 一度StringBuilderに格納し
builder.append("--").append(BOUNDARY).append(lineEnd);
builder.append("Content-Disposition: form-data;name=\"").append(entry.getKey()).append("\"").append(lineEnd);
builder.append("Content-Type: text/plain; charset=UTF-8").append(lineEnd);
builder.append("Content-Transfer-Encoding: 8bit").append(lineEnd);
builder.append(lineEnd);
builder.append(entry.getValue());
builder.append(lineEnd);

// UTF-8を指定してByte変換(DataOutputStreamの関数を使用しない)
outputStream.write(builder.toString().getBytes(StandardCharsets.UTF_8));

```
### あとがき
気づいてみればごくごく普通のことだった。
**文字コードが絡んだならば、厳戒態勢を敷こう！！！！！**

**オイオイオイオイ！！
もっといい書き方があるぜぇ！！！！！**
というとき、すみませんがコメントにてご指摘ください。

### 参考
#### Apache HTTP Client の削除
https://developer.android.com/about/versions/marshmallow/android-6.0-changes.html?hl=ja#behavior-apache-http-client
VirtualBoxでCentOSを開くと画面が小さくなる場合の対処

VirtualBoxでCentOSを開くと画面が小さすぎて見えない問題のメモです。
※当方使用PC：MAC

◆現象
　・VirtualBoxでCentOsを開くと、ウィンドウが非常に小さく表示され見にくい。
　・VirtualBoxのバージョンが上がっており、当現象を検索した時に出てきた　　　
　　VirtualBoxメニュー上のDeviceの選択肢がなかった。

◆解決法
　①VirtualBoxを起動
　②画面左枠の「ツール」を選択
　③画面右枠の「環境設定（P）」を選択
　④新しく出てきたウィンドウの上部「ディスプレイ」を選択
　⑤いくつか出てくる項目のうち「表示倍率」の%を100から200~300へ変更
　⑥表示画面が大きくなると思います

◆その他
　・当方使用PCがMACのためWindowsの動作は不明
AKAZEで特徴量抽出

# 最終的にやりたいこと

背景の同じでない2つの画像の差分を取る

## 今回やったこと

[参考](https://miyashinblog.com/opencvakaze-knn/)をもとに２枚の画像から特徴量を抽出してマッチングを行った

### 使ったもの
元々はopencvのチュートリアルのにあるコードを動かそうと思っっていたが著作権の関係でSHIFTを使うことができなかったので同様の処理を行えるかつより強力なAKAZEを用いた.

今回使用した２枚の写真は以下
![image_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675330/5308da29-de4f-0bcb-62f4-3ca91300c636.png)
![image_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675330/64667562-5bed-1faa-2092-da1e7385efb9.png)

## 結果
![img_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675330/8b06d7cd-bcb2-4cb2-1d1b-cac8b6fe62e1.jpeg)

### 感想と今後の予定
特徴量の抽出からマッチングまできちんとできたがやはり植物といった自然のものよりも人工物に特徴があるんだなと思った
次は特徴をもとに位置合わせを行って２枚の差分をとりたい

## コード
参考のものを使わせてもらいました. ありがとうございます.

```python
import cv2

#画像を読み込む
#画像１, 2
img1 = cv2.imread('image_1.png')
img2 = cv2.imread('image_2.png')

#AKAZE検出器の生成
akaze = cv2.AKAZE_create()
#画像を読み込んで特徴量を計算する
#kp = keypoints(特徴点), des = descriptors(特徴点描画)
#detectAndCompute() => (kp: 特徴点の一覧, des:獲得頂点の特徴量記述子)のタプル

kp1, des1 = akaze.detectAndCompute(img1, None)
kp2, des2 = akaze.detectAndCompute(img2, None)

#特徴量のマッチングを行う
#総当たりマッチング(Brute-Force Matcher)を生成
bf = cv2.BFMatcher()
#特徴量ベクトル同士をBrute-ForceとKNNでマッチング
matches = bf.knnMatch(des1, des2, k=2)

#データをマッチングの精度が高いもののみ抽出する
ratio = 0.6
good = []
for m, n in matches:
    if m.distance < ratio * n.distance:
        good.append([m])

#対応する特徴点同士を描画する
img3 = cv2.drawMatchesKnn(img1, kp1, img2, kp2, good, None, flags = 2)

#画像表示
cv2.imshow('img',img3)
#画像保存
cv2.imwrite('img_3.jpg',img3)
#キーを押したら終了する
cv2.waitKey(0)
cv2.destroyAllWindows()
```
　
## 参考
https://miyashinblog.com/opencvakaze-knn/
【Next.js】vercelでブランチ変更してデプロイする手順



手順
-

### STEP1
トップから`View Domains`をクリック
![スクリーンショット 2022-04-13 16.57.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425910/5ae3f0ce-3767-68d2-c8f9-c29bc09827cc.png)

### STEP2

ブランチを変えたいAppの右にある`Edit`をクリック
![スクリーンショット 2022-04-13 16.58.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425910/53157d13-e178-9107-bb0d-3ec1efae9278.png)


### STEP3

`develop`ブランチを任意のブランチに変更します。今回は`feature/sw-641`
![スクリーンショット 2022-04-13 16.59.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425910/4ee156e2-6cf3-553e-4eb9-f958ade71ab7.png)

### STEP4
トップに戻って、`feature/sw-641`の`Preview`のデプロイ履歴の所で`Redeploy`を押します
![スクリーンショット 2022-04-13 17.00.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425910/3b37d161-21a0-44d9-39ea-89388cc47d60.png)
Katacoda 環境に SSH ログインする方法 (ngrokの代替手段)

# はじめに

悲報！以前は ngrok 経由で Katacoda 環境に SSH アクセスできていましたが、ngrok を起動すると Katacoda 環境がリセット (Expired) されるようになりました。

![スクリーンショット 2022-04-13 16.39.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/f57b36a9-2f5b-de6e-60f6-2857b911e54d.png)

https://qiita.com/kitazaki/items/b90fb9e85221033e9a9a

# 代替手段の模索

Software Design 2022年4月号の「【最終回】はじめよう，おうちクラウド【6】おうちクラウドをインターネットに公開しよう」の記事に代替手段に関する参考情報が掲載されていました。

https://gihyo.jp/magazine/SD/archive/2022/202204

https://github.com/anderspitman/awesome-tunneling

# frp (fast reverse proxy)

awesome-tunnelingのリストの中で、frp (fast reverse proxy) を使用することで Katacoda 環境に SSH アクセスできることを確認しました。

https://github.com/fatedier/frp

# 構成

インターネット上で ngrok.com がパブリックIPアドレスを提供してくれました。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f37323437392f38393365316438322d643339322d373761312d633835322d3132313138626631333231372e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/abc6b930-b285-066e-380c-0eecf8ba0f63.png)

ngrok.com の代替として、パブリッククラウドのホスティング環境などで frps (サーバ機能) を動作させる必要があります。

![スクリーンショット 2022-04-13 16.46.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/dc5872b5-ba4e-7691-da01-ad8cc67fb057.png)

## Katacoda で frp を使う

図は <a href="https://github.com/fatedier/frp">frp</a> のサイトから引用しています。
まず、最初にパブリッククラウドのホスティング環境などで frps コマンドを実行します。

![スクリーンショット 2022-04-13 16.48.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/9209cb8d-8d40-0c07-8619-371b3b07c1a1.png)

次に Katacoda 環境で frpc コマンドを実行する必要がありますが、Katacoda 環境から外向きの通信で開いているポートは 80 (HTTP) と 443 (HTTPS) のみです。
パブリッククラウドのホスティング環境では、frps コマンドは 80 (HTTP) または 443 (HTTPS) ポートで待ち受ける必要があります。

![スクリーンショット 2022-04-13 16.53.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/b2039d13-96d1-1c40-75b5-647ab8d161e5.png)

Katacoda 環境で frpc コマンドを実行して frps と正常に接続されると、frpc.ini で指定したポート番号でトンネルが作成されます。

![スクリーンショット 2022-04-13 16.53.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/e869df43-3839-49b0-48be-19cdf7646388.png)

## 設定例

### パブリッククラウドのホスティング環境 (frps.ini)

パブリッククラウドのホスティング環境は ポート: 443 で待ち受ける例です。
tokenの値(12345678)は容易に推察されない値に置き換えてください。

```txt:frps.ini
[common]
bind_port = 443
token = 12345678
```

### Katacoda 環境 (frpc.ini)

SSH アクセス (パブリックIP (ポート: 10022) → Katacoda 環境 (ポート: 22)) と VNC アクセス (パブリックIP (ポート: 5901) → Katacoda 環境 (ポート: 5901)) を作成する例です。
[public IP]の部分はパブリッククラウドのホスティング環境のIPアドレスに置き換えてください。
tokenの値(12345678)は容易に推察されない値に置き換えてください。

```txt:frpc.ini
[common]
server_addr = [public IP]
server_port = 443
token = 12345678

[vnc]
type = tcp
local_ip = 127.0.0.1
local_port = 5901
remote_port = 5901

[ssh]
type = tcp
local_ip = 127.0.0.1
local_port = 22
remote_port = 10022
```

# 結果

ターミナルから SSH 接続とVNC Clientで X Windowsを表示させることができました。

![スクリーンショット 2022-04-13 17.16.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/b174f7a1-bfb9-e6a1-9c7b-5e07b17e0e52.png)
【完全未経験】3４歳の素人が異業種からITエンジニアに挑戦するって話

## 概要
- ゼロからプログラミングを勉強するときに触れた内容の備忘録
- 現役のエンジニアの友人に言われたアドバイスも合わせてメモ

## プログラミングとは
コンピュータに命令を与えて、目的の処理を実現すること

## 勉強中の言語
Java
基礎を固めるべく日々奮闘中

## 使用パソコン
- MacBook
CUDA10.1, Driver Version 418.39でPyTorchのGPUが使えないとき

## 解決策
```
$ pip install torch==1.5.0+cu101 torchvision==0.6.0+cu101 -f https://download.pytorch.org/whl/torch_stable.html
```
で解決

## 環境
- Ubuntu 
- CUDA 10.1
- NVIDIA-SMI 418.39
- Python 3.7.12

## 起きたこと
Deep Learning用PCでPyTorchのセットアップをしている時に、PyTorchがGPUを認識してくれなかった。

Anacondaで仮想環境を構築後、`conda`でPyTorchをインストールした。
```
$ conda create -n test_torch python=3.7
```

`conda activate`で作った環境に移動した後、[こちら](https://varhowto.com/install-pytorch-cuda-10-1/)を参考にしてPyTorchをインストールした。

```
$ conda install pytorch torchvision cudatoolkit=10.1 -c pytorch
```

これで無事PyTorchがインストールされ、使えるようになったと思った。
``` test.py
import torch

x = torch.rand(5, 3)
print(x)
```

しかし、これではCUDAが使えなかった。
``` cuda.py
import torch

torch.cuda.is_available()  # False
```


## 試したことと解決策
GPUを認識しているか試すために、以下を実行した

```gpu.py
import torch

torch.cuda.current_device()
```

すると、`Torch not compiled with CUDA enabled`というエラーが出た。
このエラーで検索すると、[こちら](https://yayuyota.jp/assertionerror-torch-not-compiled-with-cuda-enabled/)が見つかった。どうやら、`conda`でインストールしたことが悪さしているようだった。改めて`pip`でPyTorchをインストールし直した。

```
$ pip install torch===1.5.0 torchvision===0.6.0 -f https://download.pytorch.org/whl/torch_stable.html
```

インストール後、もう一度`torch.cuda.current_device()`とすると、次は以下のようなエラーが出た。

```
AssertionError: 
The NVIDIA driver on your system is too old (found version 9010).
Please update your GPU driver by downloading and installing a new
version from the URL: http://www.nvidia.com/Download/index.aspx
Alternatively, go to: https://pytorch.org to install
a PyTorch version that has been compiled with your version
of the CUDA driver.
```

どうやらNVIDIAのドライバが古いらしい。
ドライバのアップデートをすればいいのだろうが、このPCではこの環境に依存した他の実行環境も動いているため、できるだけアップデートは行いたくない。

そこで、このドライバのバージョンに合ったPyTorchをインストールすることにした。

[公式](https://pytorch.org/get-started/previous-versions/)から過去のバージョンのインストールコマンドを探す。今回はCUDA10.1のため、v1.5.0を使用した。

```
# CUDA 10.1
$ pip install torch==1.5.0+cu101 torchvision==0.6.0+cu101 -f https://download.pytorch.org/whl/torch_stable.html
```

インストール後、もう一度以下を実行すると、きちんとGPUが読み込まれた。
``` cuda.py
import torch

print(torch.__version__)  # 1.5.0+cu101
torch.cuda.is_available()  # True
torch.cuda.current_device()  # 0
```

## まとめ
CUDA10.1でNVIDIAドライバをアップデートせずにPyTorchのインストールを行うには、以下のコマンドが有効である。

```
# CUDA 10.1
$ pip install torch==1.5.0+cu101 torchvision==0.6.0+cu101 -f https://download.pytorch.org/whl/torch_stable.html
```
どうやら肝は`+cu101`の部分らしい。CUDAの10.2以降であれば`+cu102`のようにCUDAのバージョン指定をしなくても良いが、CUDA10.1やCUDA9.2の場合は、指定が必要なようである。
WordPressが返すURLを指定する方法

# はじめに
リバースプロキシ環境下などの場合に，
WordPressがレスポンスするURLを変更したいことがあります．
備忘録として設定方法を残します．
※WordPressのデータベースはMySQLを使用することを前提としています．

# 方法
MySQLに入ります．
```
$ mysql -u root -p
mysql>
```

WordPressのデータベースを選択します．
```
mysql> use wordpress;
```

レスポンスしたいURLを2箇所に設定します．
```
mysql> update wp_options set option_value = "http://example.com/" where option_name = 'siteurl';

mysql> update wp_options set option_value = "http://example.com/" where option_name = 'home';
```

設定が反映されていることを確認します．
```
mysql> select * from wp_options where option_name = 'home';
+-----------+-------------+---------------------------------+----------+
| option_id | option_name | option_value                    | autoload |
+-----------+-------------+---------------------------------+----------+
|         2 | home        | http://example.com/             | yes      |
+-----------+-------------+---------------------------------+----------+
1 row in set (0.00 sec)

sql> select * from wp_options where option_name = 'siteurl';
+-----------+-------------+---------------------------------+----------+
| option_id | option_name | option_value                    | autoload |
+-----------+-------------+---------------------------------+----------+
|         2 | home        | http://example.com/             | yes      |
+-----------+-------------+---------------------------------+----------+
1 row in set (0.00 sec)
```

反映されているのを確認したら，一応phpなどのWordPress関連のミドルウェアを再起動しておくことをお勧めします．
【コマンド】システム情報を表示するコマンド neofetch


:::note info

> neofetchのリポジトリの最終更新は2021年11月であり、リポジトリは2024年4月にアーカイブされています。後継としてfastfetchの利用が可能です。

というコメントをいただきましたので、追記します。
https://github.com/fastfetch-cli/fastfetch

また、WSL2(Linux-22.04)にて、fastfetchを試してみましたので最後に追記しています。
:::


# はじめに
システム情報を取得するために使用される非常に便利なコマンド「neofetch」をインストール、実行した際の備忘録です。MAC、WSL2(Linux:Ubuntu)、Windowsそれぞれの環境にて実行。

# インストールとコマンドの実行

##### MACの場合
```bash:ターミナル
# インストール
$ brew install neofetch
# 実行
$ neofetch
```
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/f09d90f3-1953-926a-27a1-5509bd7aba47.png)

###### 設定ファイルの場所
```bash:ターミナル
$ cat ~/.config/neofetch/config.conf
```
コメントアウトされているものがいくつかあり、好みで設定を変更することが可能(例:Disk,Local IP等)

![Screenshot_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/cad352ec-3263-1cd9-d502-55123b9b0bff.png)

##### WSL2(Linux-Ubuntu)の場合
```bash:WSL2:Ubuntu
# バージョンを確認
$ lsb_release -a
# アップデート
$ apt-get update
# インストール
$ sudo apt install neofetch
# 実行
$ neofetch
```
![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/5248e6ed-beec-77d8-29df-01720151befb.png)

##### Windowsの場合

```:PowerShell
C:\Users> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser

C:\Users> iwr -useb 'https://raw.githubusercontent.com/scoopinstaller/install/master/install.ps1' | iex

C:\Users> scoop install neofetch

# gitのpathが見つからないというエラーが出たため、gitのインストール
C:\Users> scoop install git

C:\Users> neofetch
```
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/56940ecf-2cfa-5a27-6e51-03d73500547c.png)

# さらに：システム情報を表示するコマンド

###### MACの場合
```bash:ターミナル
$ system_profiler
```

##### WSL2:Ubuntuの場合
```bash:WSL2:Ubuntu
$ sudo lshw
```

##### Windowsの場合
```:PowerShell
C:\Users> systeminfo
```

# 後継：fastfetchをやってみる

#### WSL2(Linux-Ubuntu)の場合

https://github.com/fastfetch-cli/fastfetch

のInstallation→Linux→Ubuntuのところを参照しながらやってみます。

```bash:WSL2:Ubuntu
$ sudo add-apt-repository ppa:zhangsongcui3371/fastfetch

$ sudo apt update

$ sudo apt install fastfetch

$ fastfetch
```
以下のようになりました。

![100.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/167772/697214bb-993e-94b3-588c-087e6801e876.png)


# 参考にさせていただいたサイト

https://kazumaxneo.hatenablog.com/entry/2018/10/07/130840

https://blog.fascode.net/2020/04/18/how-to-run-neofetch-in-windows/
WSL2のインストール方法


実務でWindowsを使用しているので、WSL2を導入した際の一連の流れを自分用にメモしました。
基本的には最下部のURLを参照して環境構築しました。（感謝。）

## WSLのインストールとUbuntuの設定

- windowsの機能である「Windowsの機能の有効化または無効化」を確認。

- 「Linux用Windowsサブシステム」にチェックが入っていないことを確認。

- PowerShellを使用して、管理者としてインストールを実行。

```markdown
wsl --install
```

- インストールが完了したらPCを再起動して、「Linux用Windowsサブシステム」「仮想マシンプラットフォーム」にチェックが入っていることを確認。

- 下記よりWSL2用Linuxカーネルのインストールを行う。

https://docs.microsoft.com/ja-jp/windows/wsl/install-manual#step-4---download-the-linux-kernel-update-package

- WSL2をデフォルトに設定。


```markdown
wsl --set-default-version 2
```

- Microsoft StoreからUbuntu20.4 LTSをインストールし、初回起動して「ユーザー名」「パスワード」を設定。

- PowerShellから以下のコマンドで現在の設定を確認。

```markdown
PS C:\> wsl --list --verbose
  NAME                   STATE           VERSION
* Ubuntu20.04LTS         Running         2
  docker-desktop         Running         2
  docker-desktop-data    Running         2
```

※ Ubuntuに＊がついていない場合は、下記コマンドを実行してUbuntuをデフォルトに設定。

```markdown
PS C:\> wsl --set-default Ubuntu-20.04
```

### ファイルアクセスについて

- `~$ cd /mnt/c/`でUbuntuからWindowsファイルにアクセスできる。
- エクスプローラーから「¥¥wsl$」でWindowsからLinux上のファイルにアクセスできる。

### Ubuntuのアップデート

※Ubuntuのアップデートは自動的に行われないため、定期的にアップデートする。（下記参照）

```markdown
sudo apt update && sudo apt upgrade
```

## Windows Terminalのインストール

- 下記よりインストールする。

https://www.microsoft.com/ja-jp/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab(https://www.microsoft.com/ja-jp/p/windows-terminal/9n0dx20hk701?rtc=1&activetab=pivot:overviewtab)

インストール後、PowerShell、Git Bash等が一元的に使用できる。


## 参照
https://chigusa-web.com/blog/wsl2-win11/
AWS OpenSearch Serviceの429 Too Many Requestsについて

初めまして、catooです。

この度会社のチームメンバーとモチベを上げるためにという議論の中でQiitaの投稿が上がったのでやってみようと思いまして記事を書こうと思い立ちました。
会社内での気づきを備忘録としてDevOps観点で書いていこうと思っています。

DevOpsしていくのにめっちゃめんどいなぁと思っている内容を記載します。

## OpenSearch 環境
バージョン：Elasticsearch 6.4
サービスソフトウェア：R20220323-P1


# OpenSearch における429 Too Many Requestsエラー
AWS Opensearch Serviceの「429 Too Many Requestsエラー」に対する考え方は以下のトラブルシュートにまとめられています。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/opensearch-resolve-429-error/

>HTTP 429 エラーまたは es_rejected_execution_exception には、次に示す変数が起因していることが考えられます。
>>データノードのインスタンスタイプと検索もしくは書き込みの制限
インスタンスメトリクスの高い値
アクティブスレッドとキュースレッド
HTTP 429 エラーは、クラスターへの検索および書き込みリクエストが原因で発生する場合があります。

書き込みや検索処理それぞれで429エラーの発生可能性があり、障害時には各変数をチェックして何が原因か調べていく必要があります。
ですが、実はこのOpensearchそこそこの定常トラフィックであってもこの４２９エラー発生します。
そのため、常に読み込みおよび書き込みに対しての拒否に対する対策を実施していく必要が有りました。

## 検索処理の429エラーのチェック
発生頻度が、書き込み拒否に比べて少ないのであまり対策をねっていないです、すみません。。。


## 書き込み拒否の429エラーのチェック
AWSのページを確認すると以下の通り
>書き込み拒否としての 429 エラーメッセージは、一括キューエラーを示します。es_rejected_execution_exception[bulk] は、キューがいっぱいであること、および新しいリクエストが拒否されたことを示します。この一括キューのエラーが発生するのは、クラスターに対するリクエスト数が一括キューのサイズ (threadpool.bulk.queue_size) を超えた場合です。各ノードの一括キューは、使用している Elasticsearch のバージョンに応じて、50～200 のリクエストを保持できます。

実際に出ているエラーログを見てみると、queue capacity = 200なので最大値になっています。

```
{
  "error": {
    "root_cause": [
      {
        "type": "remote_transport_exception",
        "reason": "[CHHsNVd][x.x.x.x:9300][indices:data/write/update[s]]"
      }
    ],
    "type": "es_rejected_execution_exception",
    "reason": "rejected execution of org.elasticsearch.transport.TransportService$7@7d3ada18 on EsThreadPoolExecutor[name = CHHsNVd/write, queue capacity = 200, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@34296b04[Running, pool size = 4, active threads = 4, queued tasks = 206, completed tasks = 2247916]]"
  },
  "status": 429
}
```

## 実際にやっている対策
運用しているシステムでは、その中でも下記ポイントついて一応対策しています。
>・アプリケーションロジックに指数関数的な再試行ロジックを追加します。指数関数的な再試行ロジックにより、失敗したリクエストが自動的に再試行されます。
注: クラスターで継続的にリクエストの同時実行性が高い場合には、指数関数的な再試行ロジックは 429 エラーの解決には役立ちません。トラフィックが突然または時折急増する場合は、このベストプラクティスを組み込みます。

指数関数的な再試行ロジックではなく、単純な再試行ロジックを用いて対処しています。
現状では５回のリトライまでにほぼ成功応答が得られる状況を確認しました。
DynamoDBのAWSSDKには「指数関数的な再試行ロジック」が組み込まれていますが、
Opensearchについては、アプリケーション側に自身で組み込む必要がある点が工数増加の大きな点だと思いました。

## 後から対策しようとしたけど簡単にできなかったやつ
>・ドキュメントのインデックス作成を高速化すると、書き込みキューがキャパシティー上限に達する可能性が低くなります。

この項目については、高速化する手段が限られているように思います。
調べても出てこないのでES関係のエンジニアと喋りましたが、解決方法は単発リクエスト/bulkリクエストのサイズを減らす方法かReplicaシャードを減らす方法の２種類しか有りませんでした。
システムでは単発リクエストで大した大きさのデータを送っていなかったので、Replicaシャードを削る方法しか取り得なかったのですが、Elasic社推奨の「保持期間ごとのインデックス」っていなかったので、インデックスの再作成が必要になったため対応不可と判断しました。

https://www.elastic.co/jp/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster


# まとめ
AWS OpenSearch における429 Too Many Requestsエラーが頻発していたので色々検討したり調べたりした内容をまとめました。
基本的には再試行ロジックを組み込むことで対処可能なのですが、運用していくにつれてシャードの偏り等でも発生の可能性があるらしいのでそのあたりも様子見しながらシャードの管理を進める必要も出てきそうです。
要件変更等に対応しながら、アプリケーションからのリクエスト総数を見積もって調整していく運用の複雑さがやばすぎると思った。
このコンポーネントもうやめたい。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/opensearch-rebalance-uneven-shards/
Docker, Jupyter-notebookでQiskitを使った量子計算の学習

Macで確認しましたが、WindowsでもLinuxでもDockerが使える環境なら使えると思います。

適当なディレクトリを作って、
```shell
mkdir qiskit && cd qiskit
```

適当なテキストエディタでDockerfileとdocker-compose.ymlを作る。

```Dockerfile:Dockerfile
FROM qiskit/jupyter:0.1

RUN pip install git+https://github.com/qiskit-community/qiskit-textbook.git#subdirectory=qiskit-textbook-src
```

```yaml:docker-compose.yml
version: "3.8"
services:
  qiskit-jupyter:
    build: .
    image: qiskit-jupyter
    volumes:
      - ${PWD}/:/home/jovyan/work
    ports:
      - 8888:8888
    container_name: qiskit-jupyter
    environment:
      - JUPYTER_ENABLE_LAB=yes
```

下の様にコンテナを立ち上げると、

```shell
docker-compose up
```

こんなのが出てくるので、

```shell_session
qiskit-jupyter  |
qiskit-jupyter  |     To access the notebook, open this file in a browser:
qiskit-jupyter  |         file:///home/jovyan/.local/share/jupyter/runtime/nbserver-10-open.html
qiskit-jupyter  |     Or copy and paste one of these URLs:
qiskit-jupyter  |         http://[何か英数字列]:8888/?token=[何か英数字列]
qiskit-jupyter  |      or http://127.0.0.1:8888/?token=[何か英数字列]
```

`http://127.0.0.1:8888/?token=[何か英数字列]`の箇所を選択し、URLとしてブラウザで開きます。
Jupyter Notebookが立ち上がるので、後はこのサイトを見て学習

https://qiskit.org/textbook/ja/preface.html
Unity 2021 で Assets/Plugins/Android/res フォルダが使えなくなった際の対応

# 環境
- macOS(Apple Silicon)
- Unity 2021.3.0f1

# 事象
- 通知用アイコンやアプリ名のローカライズファイルを `Assets/Plugins/Android/res` に配置していたら Unity 2021で Android ビルドできなくなった

# 対応方法
- AARファイルではなく、「Android ライブラリプロジェクト」 なるものを作成して対応しました
    - https://docs.unity3d.com/ja/2022.1/Manual/AndroidAARPlugins.html の「AAR プラグイン」じゃなくて下部の方
- AARファイルを zip コマンドや Android Studioから生成するという方法を複数の先人の方々が書いてくださっていたのですが、何かが至らなかったのか自分の環境だと上手く動きませんでした😣

# 手順
- `Assets/Plugins/Android` 直下に `CustomAndroidResource.androidlib` (フォルダ名は何でも大丈夫ですが、 `.androidlib` で終わらせる)フォルダを作成して下記の内容物を入れていきます

# フォルダの内容物
- AndroidManifest.xml
- project.properties
- res

# 各内容物の中身
```AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="custom.android.res"
          android:versionCode="1"
          android:versionName="1.0">
</manifest>
```
- `package="custom.android.res"` の部分は resフォルダしか使用しないので被らなければ何でも大丈夫だと思います。詳しい方がいたら教えてください

```project.properties
target=android-9
android.library=true
```

- Firebase (FirebaseApp.androidlib/project.properties) を参考にしていますが、 `target=android-9` の部分は必要に応じて変えてください


あとは既存のresフォルダを本フォルダにまるごとぶちこんでください

自分の環境では以上で動きましたが、動作保証などはもちろん出来ないので何かあれば教えて頂ければと思います