OmniAICreator/Qiita-1.07M huggingface dataset:
[Laravel6] ざっくりSeederを使う

### ターミナル

```php
php artisan make:seeder 〇〇
```

- database\seeds\〇〇が作成される

### Seeds

```php
public function run()
    {
        DB::table('users')->insert([
            'name' => Str::random(10),
            'email' => Str::random(10) . '@gmail.com',
            'password' => Hash::make('password'),
        ]);
    }
```

### DatebaseSeeder

```php
public function run()
    {
        $this->call(UsersTableSeeder::class);
    }
```

- DBSeederは必ず存在している＋作成したSeederが最初はコメントアウトされている
    - ここから個々のSeederを呼び出して実行する仕組みになっているので、コメントアウトを外しておく

### ターミナル

```php
composer dump-autoload

php artisan db:seed
```

- Seederクラスを書き上げたら、Composerのオートローダを再生成する必要がある
    - composer.jsonのautoloadにdatabase/seeds,factoryの記述があるため、書き換えの際に再生成する必要が出てくる
- もし2つ目のコマンドが効かなかったら、生成したデータと既に存在しているデータでコンフリクトが起こっている可能性がある
    - `php artisan migrate:refresh —seed` でマイグレートのリフレッシュとシーディングを同時に行うことができ、問題が解決する
EC-CUBEのハンバーガーメニューアイコンが真っ白

# 困ったこと
EC-CUBEをスマホで表示したら、ハンバーガーメニューアイコンが真っ白で見えない

# 原因
h1フォント太さ設定が適切でなかった

# 対処
CSS追記

```:vim&nbsp;root/html/user_data/assets/css/customize.css
h1:before {
font-weight: 900;
}
```

# 結果
無事ハンバーガーメニューアイコンが表示された！

***
# 参考記事
https://office-obata.com/report/memorandum/post-5293/
なぜ人はAWSを使うのか？

# はじめに
なぜ人はAWSを使うのか。。。

AWSには使われる理由がたくさんあります！

「会社でAWSを使っているけど、なぜ使っているのかわからない」
「AWSってそもそも何？」
という方のためにAWSがなぜ選ばれるのかを自分なりにまとめてみました。

<!--
なぜ人はAWSを使うのか。
使われるのには理由があります。
-->

# AWSとは
AWS(Amazon Web Servises)とは、Amazonが提供するクラウドサービスのことです。
簡単にいうと、サーバーやネットワークなどをインターネット経由で貸してくれるサービスで、いつでもどこでも利用できます！


# メリット
## 手軽に利用を始められる
AWSでは、サーバーやネットワークを管理するための専門的な技術者は要りません。

**マネジメントコンソール**というものがあり、GUIでAWSを操作することができるため、面倒な技術や知識を必要としないのです。

**マネジメントコンソール**では以下のことをGUIで操作して行うことができます。
* ユーザーやグループの作成
* 権限やセキュリティの設定
* 各種サービスの構成を変更
* サーバーやデータベースなどの起動・終了
* バックアップ


## 初期費用が安い
AWSは使った分だけ支払う、**従量課金制**です。
そのため、**将来必要だけど現在必要ない分**を借りる必要がありません。

一方オンプレでは、、、
* サーバーを立てるための多額の初期費用
* サーバーやネットワークを管理するための専門的な技術者が必要→人件費↑
* サーバーを追加するのに追加費用がかかる
余分にコストを割く必要が出てきます。

![aws-ten-reasons-03_202103.a36273a2a8169f2b400e0cf43092f0febc45089f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2668385/cc39352d-659d-50b3-3c5b-f0832b8f9260.png)
引用：https://aws.amazon.com/jp/aws-ten-reasons/



ただ、AWSでは
* 運用コストの方が高くなってしまう
* サーバーを細かくカスタマイズすることができない

などといったデメリットが発生する場合もあります。

## 運用を任せられる
AWSには**マネージドサービス**と呼ばれる、**バックアップやアップデートなどの管理を自動で行なってくれる**サービスがあります。


**マネージドサービス**の特徴
* 自動バックアップ
* 自動アップデート
* モニタリング
* パッチ管理
* セキュリティ
* 故障に備えた冗長化

また、「クラウドはなんとなくセキュリティ的に不安」と思う方もいらっしゃるかもしれません。
しかしセキュリティ面も安心なのがAWSです。
日々の運用を自動で行い管理してくれるので、常にセキュアな状態で運用できます。
（Amazonのセキュリティ管理のエンジニアに勝る人はそうそういないと思うので自社で雇うよりも安心）

ただ、セキュリティ面に関しては利用者による情報漏洩などもあるので責任は50:50となります。


## 豊富な機能
AWSには（2022/5/23現在で)227種類のサービスが存在しています！
これは今でも増え続けており、日々新しいサービスが生まれています。

![aws-ten-reasons-05_202103.c0ddb3f14aacad89131f7e8e3b6c956dcb0b7052.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2668385/ba73a427-6048-90ff-0107-4ae34a950bbe.png)
引用：https://aws.amazon.com/jp/aws-ten-reasons/


これだけの種類のサービスがあるとたいていのものが作れます！
　　　**webサーバー**を作りたい　→　◯
　　　**モバイルシステム**を作りたい　→　◯
　　　**コンテンツを配信**したい　→　◯
　　　**IoT**を作りたい　→　◯
　　　**機械学習**をしたい　→　◯
　　　**ロボット**を作りたい　→　◯

# まとめ
AWSにはたくさんのメリットがあります。
AWSに興味が湧いた方は、一度使ってみてその手軽さを実感してみてください！
date_diffで日付要素を加味して月数の差を求めたいときのメモ

# 概要

date_diff関数は、2つの日付型に対して、指定した粒度での差を返す。
ただし、その際、指定した粒度より細かい要素は切り捨てる形で評価する。

そのため、日付間の月数を取りたいが日数が加味されない、年数を取る際に月数が加味されないといった性質が有り、実用上問題のある場合がある。

# やり方

基準となる日付を切り捨て、その差分をもう一方の日付にも同様に差し引く。

```sql:例
select
    x, y,
    date_diff(y, x, month),
    date_diff(date_sub(y, interval date_diff(x, date_trunc(x, month), day) day), date_trunc(x, month), month)
from
    unnest([
        struct(date('2022-05-01') as x, date('2022-05-29') as y),
        struct(date('2022-05-01') as x, date('2022-06-01') as y),
        struct(date('2022-05-29') as x, date('2022-06-01') as y),
        struct(date('2022-05-29') as x, date('2022-06-01') as y),
        struct(date('2022-05-29') as x, date('2022-06-29') as y),
        struct(date('2022-05-29') as x, date('2022-06-30') as y)
    ])
```

![スクリーンショット 2022-05-25 17.32.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221698/31fbb4b5-81ba-929c-2da8-b8a3486ddd63.png)
WordPressをONLYOFFICEドキュメントに統合する方法

[ONLYOFFICE Docs](https://www.onlyoffice.com/ja/office-suite.aspx)は、テキスト文書、スプレッドシート、プレゼンテーション、および記入式フォームのためのオンラインビューアとコラボレーションエディタで構成される、オープンソースのオフィススイートです。使用されているコアフォーマットはOOXMLなので、エディタはDOCX、XLSX、PPTXファイルと高い互換性が高いです。Redmine、Jira、Nextcloud、Strapiなど、複数のクラウドサービス内でこのスイートを使用することができます。

[WordPress](https://ja.wordpress.org/)は、ブログやウェブサイトのパブリッシングサービスとして使用されるオープンソースのコンテンツ管理システムです。ONLYOFFICEを含む多くのテーマやプラグインを利用してWordPressインスタンスを拡張することができます。
ONLYOFFICE DocsをWordPressに接続することで、以下のことが可能になります。
　・管理画面からオフィスドキュメントの編集
　・ONLYOFFICEブロックを投稿に追加することで、サイト訪問者がダウンロードせずに挿入されたファイルを閲覧

**前提条件**
・ONLYOFFICE Docs（ドキュメントサーバー）v7.0 または v7.1 
・WordPress インスタンス v5.9.0 以降 
・WordPress用ONLYOFFICEプラグイン

**ステップ1：プラグインのインストール**
プラグインのmasterブランチをクローンします。
```
cd wp-content/plugins
git clone https://github.com/ONLYOFFICE/onlyoffice-wordpress
```
準備ができたら、WordPress管理ダッシュボードのプラグインセクションに移動し、ONLYOFFICEプラグインを有効化してください。

![Рисунок1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2655664/47b7b133-0bca-61e2-de20-0bad84e0da6d.png)

**ステップ2：ONLYOFFICEドキュメントサーバーの設定**
プラグインをインストールしたら、WordPressインターフェース経由でONLYOFFICEを構成してください。管理ダッシュボードに移動して、ONLYOFFICE設定に切り替えて、以下のパラメータを指定しましょう。
- **ドキュメント編集サービスアドレス**：インストールされたONLYOFFICEドキュメントサーバのURL
- **ドキュメントサーバJWTシークレットキー**：不正なアクセスからドキュメントを保護するためのJWTを有効にする。ONLYOFFICE ドキュメントサーバーの設定ファイルにも同じシークレットキーを指定しないと、検証が有効にならない。

![Рисунок2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2655664/af6b218d-cde2-955a-97e7-b7b87538672a.png)

**ステップ3：WordPressでONLYOFFICEを使う**
「Media」セクションからアップロードされたすべてのファイルは、管理ダッシュボード内のONLYOFFICE 「ファイル」ページに表示されます。
![Рисунок3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2655664/03592373-4aea-ad81-e9cc-b32f8fc01f5b.png)

管理者権限を持つユーザーは、2つの共同編集モード、変更履歴、コメント、内蔵チャットを使用して、DOCX、XLSX、PPTXファイルの編集と共同作業を行うことができます。ファイル名をクリックすると、同じタブでエディタが開きます。すべての変更は、同じファイルに保存されます。
投稿を作成する際、ONLYOFFICEブロックを追加してから、新しいファイルをアップロードするか、メディアライブラリからファイルを選択することができます。
![Рисунок4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2655664/5baf443a-5d10-269e-3ad1-a0e52789c90f.png)
投稿が公開された後、WordPressサイトの訪問者はこのファイルにアクセスし、埋め込みモードで閲覧することができます。
以上です。ONLYOFFICE DocsとWordPressが接続され、複数のアプリケーションを切り替えることなく、オフィス文書を扱うことができるようになりました。
Cloud StorageにCSVファイルがアップロードされたらCloud SQLにインポートするCloud Functionsを作成したときに苦戦した箇所のメモ

# 概要
Google Cloud PlatformでCloud StorageにCSVファイルがアップロードされたらCloud SQL上のテーブルにインポートされるようにしたいと思い、Cloud Functionsで関数を作成してみました。
その際に個人的に苦戦した箇所のメモです。

なお、今回 Cloud SQL は MySQL8.0, Cloud Functions は Python3.7を使用しました。

# ソース
~~~python:main.py
def importDs(self, request):
    from pprint import pprint
    import googleapiclient.discovery
    from oauth2client.client import GoogleCredentials

    credentials = GoogleCredentials.get_application_default()
    service = googleapiclient.discovery.build('sqladmin', 'v1beta4', credentials=credentials, cache_discovery=False)

    project = 'プロジェクト名'
    instance = 'Cloud SQLのインスタンス名'
    instances_import_request_body = {
        "importContext": {
            "uri": "gs://バケット名/フォルダパス/CSVファイル名.csv",
            "database": 'データベース名',
            "kind": "sql#importContext",
            "fileType": "CSV",
            "csvImportOptions": {
                "table": "users",
                "columns": [
                    "カラム名1",
                    "カラム名2"
                ]
            }
        }
    }

    request = service.instances().import_(project=project, instance=instance, body=instances_import_request_body)
    response = request.execute()
    pprint(response)
~~~

# 苦戦した箇所
## 引数の設定
修正後
~~~
def importDs(self, request):
~~~
修正前
~~~
def importDs(request):
~~~

修正前のような形で書いていたらTypeErrorが発生したので、下記記事を参考に引数を追加しました。

https://blog.pyq.jp/entry/python_kaiketsu_210831

## buildの引数
ImportErrorが発生したため、下記記事を参考に引数に「cache_discovery=False」を追加しました。
~~~
service = googleapiclient.discovery.build('sqladmin', 'v1beta4', credentials=credentials, cache_discovery=False)
~~~

https://qiita.com/kai_kou/items/4b754c61ac225daa0f7d

## 権限の設定
実行時にパーミッションエラーが出たため、下記記事を参考に権限付与しました。

https://qiita.com/tab02733/items/9558d530d04eb228e41d


# 参考

Cloud Functionsのソース作成及びトラブルシューティング時に、下記のページを参照しました。

https://cloud.google.com/sql/docs/mysql/admin-api/rest/v1beta4/instances/import?hl=ja

https://cloud.google.com/sql/docs/mysql/admin-api/rest/v1beta4/operations

https://qiita.com/ya_ma23/items/378459ced4aba4bfa940

https://qiita.com/tab02733/items/9558d530d04eb228e41d

https://qiita.com/kai_kou/items/4b754c61ac225daa0f7d

https://blog.pyq.jp/entry/python_kaiketsu_210831
今後やること

<div>
<h2>学習を２ヶ月終えて</h2>
<ul>
  <li>学んだこと</li>
　<ul>✔︎HTML/CSS<br>✔︎Ruby<br>✔︎Ruby On Rails
</ul>
</div>

<ul><li>振り返り<br>
参考資料に沿ってアプリを作ってみたが<br>
ルーティングやアソシエーションなど機能が増えていくにつれ<br>
記述する量も増えていった。<br><br>
Railsでのアプリ開発の流れは一通り抑えたが、<br>
一つ一つの変数やモデル、コントローラーの処理が<br>
どのように結びついているのか、まだ理解できていないため、<br>
今後はその観点を意識しつつ復習していく。
<div>
<h4>復習する内容</h4>
<ul>✔︎Ruby<br>→データ型/配列/ハッシュ/繰り返し処理<br>
✔︎Rails<br>→ワイヤーフレーム/MVCモデル/クラス/<br>　変数(ローカル, インスタンス)/DB/アソシエーション
</div>
<div>
<h4>どのようにやる</h4>
<p>参考書を買う<br>開発環境を使って手を動かす<br>ノートにアウトプットする</p>
</div>
<div>
<h4>いつまでにやる</h4>
<p>来月の６月中旬までには理解しておきたい
</div>
PythonでMinecraftサーバーの独自コマンドを作る

## PythonでMinecraftのサーバーを......?
Minecraftのサーバーに自分で独自機能を実装したい！
そんな時は皆さんJavaでプラグインを書いたりするでしょう。
しかしJavaができない人はどうすれば......?
Javaを勉強して書けるようになれ！というのは至極まっとうでぐうの音も出ない正論です。
ですが手段はまだ残されています。Pythonを使うのです！

## MCRconというライブラリ
PythonにはMCRconというライブラリがあります。(https://pypi.org/project/mcrcon/)
その名の通りMinecraftサーバーに実装されているRconという機能を手軽に利用できるようにするライブラリです。

## MCRconを使う
MCRconを使えばサーバーの外からマインクラフトのサーバーにコマンドを簡単に送り込むことができます。
```
with MCRcon('サーバーのアドレス','Rconのパスワード',ポート) as mcr:
    mcr.command('tellraw @a "hello world"')
```
たったこれだけで全員にhello worldと発言することができるのです。
ですがプラグインのように複雑な処理などはできません。

あくまで利用できるのは対象のマインクラフトサーバーに実装されたコマンドのみです。
(プラグインのコマンドには対応)

しかしこれだけでも十分すぎると言えるでしょう。
何故ならマインクラフト内でコマンドブロックやスコアボードを利用してえっせほいせと書いていた計算やターゲットエンティティの指定などの処理をPythonで行うことができるのですから！

コマンドを実行する間隔を空けるにも/scheduleやレッドストーン回路を用いる必要はないのです。
`time.sleep`を一行書いておしまい！

いやあ何でもできそうに思えてきましたね。

## MCRcon + Subprocess = 独自コマンド
MCRconのさわり心地が分かったところで本題に入ります。
Subprocessは分かりますよね？そうですいろんなことができるやつです。
今までサーバーのJavaファイルを動かすために`java` から始まるコマンドで始動させてきたと思います。
今回は独自コマンドを実装するということで、その方法で動かしては少し都合が悪いので先述のsubprocessを代わりに用いることにします。

```
server = subprocess.Popen(['java','-Xms4G','-Xmx4G','-jar','spigot-1.18.2.jar','--nogui'],stdout=subprocess.PIPE,stderr=subprocess.STDOUT,shell=True,text=True,encoding='shift-jis')

for i in server.stdout:
    print(i,end="")
```
たったこれだけのコードでJarファイルからの標準出力をもらい、映し出すことができました。
(上記のコードは Windows で動かす場合のものです。Linuxでは `shell=True` など書かなくてもよい要素がちらほら含まれています。)

この i にはサーバーコンソールの一行一行が文字列として詰め込まれています。
さて、ここまで来て勘のいい方は察しがついたでしょう。
そうです、ここで得た標準出力を正規表現などを用いてごにょごにょ処理して、プレイヤーが独自コマンドを実行したかどうかを判定するのです！
そして条件に当てはまったらMCRconでコマンドを送る......

Javaに触れたことがない方ならこちらの方が楽なのではないでしょうか。
夢は広がりますね。

## さいごに
便利だぞ！というような口調で書いてきましたが実際のところ

・コマンドのサジェストに出ない
・連続して大量のコマンドを流すとエラーが起きる(詳細不明)

など短所も少なからずあります。
これらを理解したうえで独自機能を実装してみましょう！


それでは！
Ruby逆引きメモ

Rubyにおける逆引きメモです。「やりたいこと→メソッド」という形にしています。最後に公式ドキュメントへのリンクを付けてますので合わせて参考にしてください。

## オブジェクトを調べる

サンプル用コード
```ruby
class User
  def initialize()
    @name = "bob"
  end
end

user = User.new
```

<br>

##### オブジェクトが持つインスタンス変数名を調べる

```ruby
p user.instance_variables
#=> [:@name]
```
- 変数名をシンボルの配列として返します。
- 初期化していない変数は返されません。

<br>

##### オブジェクトのインスタンス変数の値を調べる
```ruby
p user.instance_variable_get("@name")
#=> "bob"
```
- 引数にはシンボルも使えます。`(:@name)`

<br>

##### オブジェクトのクラスを調べる
```ruby
"Hello".class
#=> String
```
<br>

## 文字列を操作する

##### 文字列を文字ごとに切り離して配列にする
```ruby
"Hello".chars
#=> ["h", "e", "l", "l", "o"]
```

<br>

## 配列

##### 配列の要素数を取得する
```ruby
array = [1, 2, 3]
array.size
#=> 3
```

<br>

### 参考
https://docs.ruby-lang.org/ja/3.1/doc/index.html
Pythonで歌詞を集めて軽く分析してみた話#4

前回
https://qiita.com/kuroge/items/2b805426a93b8a99737c

今回は…
---
全曲集めるのもいいけど…アーティストごとに曲を収集したいなぁ
わざわざ配列で指定するのもめんどくさいし…

…そや！アーティストごと集められるように改造しよう！

コード(コードが汚いのは実験中だからです。すみません…)
```python:改造コード
import requests
from bs4 import BeautifulSoup
import os

dic = "other"
url = ["134"]
for i in url:
    uta_url = ""
    memory = ""
    load_url = f"https://www.uta-net.com/artist/{i}/"
    html = requests.get(load_url)
    soup = BeautifulSoup(html.content ,"html.parser")
    uta_count = int(str(soup.select("span.f_style3:nth-child(2)")).replace('<span class="f_style3">',"").replace("</span>","").replace("[","").replace("]",""))
    count = 1
    for a in range(uta_count):
        for element in soup.select(f".result_table > table:nth-child(1) > tbody:nth-child(2) > tr:nth-child({count}) > td:nth-child(1) > a:nth-child(1)"):
            memory = element.get("href")
        uta_url += memory + "\n"
        count = count + 1
    
    uta_url = uta_url.replace("/","").replace("song","")
    uta_url = uta_url.split("\n")[:-1]
    net = uta_url

    for i in net:
        kasi = ""
        memory = ""
        load_url = f"https://www.uta-net.com/song/{i}"
        html = requests.get(load_url)
        soup = BeautifulSoup(html.content ,"html.parser")
        for element in soup.find_all(id="kashi_area"):
            memory = element
            memory = str(memory).replace('<div id="kashi_area" itemprop="text">', "").replace("</div>", "").replace("<br/>","\n")
        print(memory)
        kasi = memory
        path = f"{dic}/歌詞{str(i)}.txt"
        if not(os.path.isfile(f"{dic}/{path}")):
            with open(path,"w",encoding="utf-8")as file:
                file.write(str(kasi))
        print(i)

print(uta_url)
```
やっていること（大雑把）
1 アーティストのURLを取得（今回は`B'z`様の歌の歌詞）
2 歌詞が書いてあるURLを取得
3 そこから歌詞を取得
4 otherフォルダーに入れる

一見前やっていたこととほぼ同じに見えますが、ちょっと変えました。

変更点
---
1 歌詞が書いてあるhtmlコードに改行の``<br />``を発見したので``replace``でそれを``\n``に置き換えるコードを設置

さらに便利に使えるようにしました。

今の問題点
---
・歌詞生成を同じアーティストの曲だけで学習しようとすると同じような文章になる

また、修正したらそれも上げていこうかなと思います。

このコードについて
---
調べたところ、このような歌詞解析・生成のPythonコードが調べてもあまり良いのが出てきませんでした。
なので、このコードでよければ、自由に使っていただいてokです！どんどん使ってください

※ただし僕のコードのリミックスを配布・配信するときは、
私のユーザー名（「kuroge」）と、右記のURL→https://qiita.com/kuroge/items/420e5c91606b6b31664c
をリミックスしたコードのコメントに書いてください。


ご視聴ありがとうございました！
UiPath Orchestratorの権限（ロール）を考えてみる

# 前提
この記事は、バージョン 2020.10 を前提としています。
記事執筆時点(2022/4/25)の最新バージョンは 2021.10 ですが、大きな差はないと思います。
また、本記事の内容はあくまでも一例であり、実際の運用に合わせた設計やテストを行ってください。

# はじめに
複数のUiPathロボット専用端末（Unattended）を用意し、モダンフォルダー構成のOrchestratorで管理をしています。
RPAが本番稼働するまでには、とてもざっくりですが、以下のフェーズがあると思います。
+ 環境構築（Orchestratorとロボット）
+ シナリオの開発、テスト
+ 稼働監視などの本番運用

RPA化の規模にもよりますが、開発チームは開発に関することだけ、運用チームは運用に関することだけなど、それぞれのユーザーにロールを割り当てるにはどうしたら良いかを考えてみようと思います。

# 既定のロール（ビルトインロール）

モダンフォルダーには、以下のロールが既定で存在します。
（以下のマニュアルも参照してください）

https://docs.uipath.com/orchestrator/lang-ja/v2020.10/docs/default-roles

+ `Tenant Administrator`
+ `Allow to be Folder Administrator`
+ `Folder Administrator`
+ `Allow to be Automation User`
+ `Automation User`

Allow to be XXX は その下にある XXX とセットのロールになっていて、Allow to be XXX はユーザーに直接設定し、XXX はフォルダー毎に設定するものです。（このあたりはマニュアルを参照してください）

ロール名の通り、デフォルトでは「テナントの管理者」「フォルダーの管理者」「ロボット」という権限しかありません。

# Orchestratorの運用例を考えてみる

それでは、Orchestratorをどのように使うのか、少し具体的に考えてみます。
基本的には、「管理者」と「開発者」を明確に分けるようなことをしたいと思います。

## 想定するロール

例として、以下のような運用（ロールの割り当て）を考えてみます。

### テナントの管理者
+ テナントのことは何でもできる人。
+ ロボットを作ったり、ライセンスの管理をする人。

### フォルダーの管理者
+ フォルダーのことは何でもできる人。
+ プロセス定義や、ジョブの定義・スケジューリングなどを行う人。

### 開発者
+ 開発だけする人。
+ プロセスやジョブの定義はフォルダーの管理者が行うので、開発者はしない（させない）。
+ 当然、ロボットを作ったりライセンス管理なんてする（させる）わけがない。
+ アセットは自由に定義したい。

## さて困った

テナントの管理者は、`Tenant Administrator`。
フォルダーの管理者は、`Allow to be Folder Administrator` と `Folder Administrator` を付けてあげればいいですね。

さて、開発者はどうしましょうか。
`Tenant/Folder Administrator` を付けるのは論外として、`Automation User` にはアセットを編集する権限が付いていませんし、ジョブの作成権限が付いているのでOrchestratorからジョブ実行できてしまいます。

# ロールを作ってみよう

ロールは、「テナントに対するロール」と「フォルダーに対するロール」を分けて設定する必要があります。
そして、同じロール名に対して「テナントに対するロール」と「フォルダーに対するロール」を一緒に設定することは推奨されていません。
ビルトインロールでも、`Allow to be Automation User`はフォルダーに対するロール、`Automation User`はフォルダーに対するロールが設定されています。

## 開発者の前提

開発者は、以下のことを行える前提とします。

:::note info
Studioで個別のプロセスの開発／テストを行う。
:::

:::note info
開発したプロセスをOrchestratorにパブリッシュする。
:::

:::note info
権限を持っているフォルダーに対しては、自由にアセットの設定を行える。
:::

以下のことはできないようにします。

:::note alert
Orchestratorからジョブの実行指示はしない。
:::

:::note alert
実行環境に係るような設定（ロボットやマシン、ユーザーやフォルダーの設定など）はしない。
:::

これを満たせるようなロールを作ってみます。

## 開発者のフォルダーに対するロール

`Allow to be Developer` というロールを作成します。

| ロール    | 権限     | 備考                         |
|:----------|:-------:|:-----------------------------|
| アラート   | `V---` | アラートを参照するために必要です。   |
| ライブラリ | `V---` | 実行に必要な各種ライブラリを参照するために必要です。  |
| パッケージ | `V-C-` | プロセスをOrchestratorにパブリッシュするために必要です。  |

※ `V`=表示、`E`=編集、`C`=作成、`D`=削除 です。

## 開発者のテナントに対するロール

`Developer` というロールを作成します。

| ロール | 権限 | 備考 |
|:-:|:-:|:--|
| アセット  | `VECD`  | アセットの定義を行うために必要です。  |

※ `V`=表示、`E`=編集、`C`=作成、`D`=削除 です。

# おわりに

ロールはもっとたくさんあるのですが、全部書くと長くなってしまうため最小限にとどめました。
今回、例題であげた「開発者」というロールは、このような設定で実現できると思います。
「開発者」でもプロセスの定義をしたい！などもあると思いますので、必要に応じて権限を追加しましょう。
cscの作法 その172

# 概要
cscの作法、調べてみた。
mikovm2、やってみた。

# 参考にしたページ

https://qiita.com/stk2k/items/4722643f4fca55cddb37


# 結果

```
>miko2

トークン：
T_VARIABLE(a)
T_EQUAL(=)
T_EXPRESSION(2 * (5 - 1) + 6 / 3)
T_END_OF_STATEMENT(;)
T_PRINT(print)
T_VARIABLE(a)
T_END_OF_STATEMENT(;)

バイトコード：
opcPUSH 2
opcPUSH 5
opcPUSH 1
opcSUB 0
opcMUL 0
opcPUSH 6
opcPUSH 3
opcDIV 0
opcADD 0
opcPOPV 0
opcPRINT 0

VMの実行結果：
push 2
push 5
push 1
sub 5 1
mul 2 4
push 6
push 3
div 6 3
add 8 2
popv 10
print 10
10

>






```



以上。
plot3D動かし方[備忘録]

3次元プロットをRで作成する際に、scatterplot3dやlatticeで静的なplotを作成できるが、見づらいことがある。
そこで、動的な3次元プロットを作成することとし、rglパッケージのplot3dを用いたので、そのスクリプトを備忘録としてまとめる。
なお、palmerpenguinsのデータセットをplotの作成に用いた(以下サイト参照)。

https://allisonhorst.github.io/palmerpenguins/

また、plot3dの動かし方は、以下のサイトを参照した。

https://kohske.github.io/ESTRELA/201501/index.html

http://rmecab.jp/ranko/chap5.html

以下が実際に動かしたスクリプトである。(Rのversionは4.0.5)

```
#データセット
> library("palmerpenguins")
> head(penguins)

#ライブラリの読み込み
> library(rgl)
> library("magick")

#3次元プロットの作成
> plot3d(penguins$bill_length_mm, penguins$body_mass_g, penguins$flipper_length_mm, xlab="bill length (mm)", ylab="body mass (g)", zlab="flipper length (mm)", col=rainbow(3)[factor(penguins$species)], expand=1.2)

#図の書き出し
> rgl.snapshot("test.png")

#図の書き出し(gif形式)
> movie3d(spin3d(c(0, 0, 1), 15), duration=5 , movie =  "~/Desktop/douga")
```

以下に作成した図を示す。
![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1060100/7b1b75fa-2edd-cbac-6b56-b46912f6e3d4.png)
png形式

![anime.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1060100/0c799a97-3af8-0f49-e6f0-3894163563a5.gif)
gif形式(クリックしないと動いていない...?)
SqlServerにmdf,ldfファイルをリストアする（on Docker）

# はじめに

- Docker 上で SqlServer を立ててそこにリストアする手順

# 環境
- macOS BigSur

# 前提
- Docker for Mac インストール済み
- ldf ファイルを持っている(sample_log.ldf)
- mdf ファイルを持っている(sample.mdf)

# 手順
## SqlServerを立てる
1. コンテナー イメージをプルして実行する

   ```bash
     sudo docker pull mcr.microsoft.com/mssql/server:2019-latest
   ```

1. コンテナー イメージの実行

   ```bash
      sudo docker run -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=<YourStrong!Passw0rd>' \
      --name 'sql1' -p 1401:1433 \
      -v sql1data:/var/opt/mssql \
      -d mcr.microsoft.com/mssql/server:2019-latest
   ```

   - `docker ps -a`で起動が確認できれば OK


## データベースを復元する

1. バックアップ ファイルをコンテナーにコピーする

   - バックアップファイルが置いてあるディレクトリに移動

   ```bash
     docker exec -it sql1 mkdir /var/opt/mssql/backup
     docker cp RH25_JP.mdf sql1:/var/opt/mssql/backup
     docker cp RH25_JP_log.ldf sql1:/var/opt/mssql/backup
   ```

1. SQL を発行する

   - DBに入る

   ```bash
     docker exec -it sql1 bash
     /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "<YourStrong!Passw0rd>"
   ```

   - SQL発行
   ```bash
        1> CREATE DATABASE MyDatabase3 ON (FILENAME='/var/opt/mssql/backup/RH25_JP.mdf'),(FILENAME='/var/opt/mssql/backup/RH25_JP_log.ldf') FOR ATTACH
        2> go
        1>
   ```

## 復元した DB を確認
   - DBeaver などの DB ツールで接続する
   - ユーザ名:`SA`
   - パスワード:`<YourStrong!Passw0rd>`
   - address:`localhost`
   - port: `1401`
MonacaをGASで作ったAPIと連携させる

# 背景
- 迷子猫捜索アプリを作りたい
- 入力フォームとデータ取得APIを用意したい
   → [GoogleFormとGoogleSpreadSheetとGoogleAppsScriptで、フォームとDBとデータ取得APIを作成する](https://qiita.com/itortie/items/a4712fcf330a486a7092)
- 今ある知識（Web系）でアプリを作りたい

ハイブリッドアプリを作れるMonacaを試してみる

# 実装

Monaca https://ja.monaca.io/

アカウントを作成し、サンプルプロジェクトを立ち上げる
一部のソースを改修する

## 入力フォーム
```html:index.html
<body>

    <h1>猫の手</h1>
    <a class="button--large" href="https://docs.google.com/forms/d/e/your form id/viewform"">迷子猫を登録する</a>
    <p></p>
    <a class="button--large" href="demo.html">見つける</a>
</body>
```
クラウドIDEでのエミュレータ画面
<img width="300" alt="form-ide" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/95cd1c22-eb55-5088-35f5-1e9ce743dfd8.png">

フォームに飛ぶことが出来ました。
<img width="300" alt="form" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/6b2fe221-ebc4-37f5-2888-9f5aa167235c.png">

ただし、この方法では画面が遷移してしまうため、元のアプリに戻る必要があります。
スマホのブラウザバックで戻ることはできますが、回答完了後に自動遷移でアプリに戻るなどは難しいかもしれません。

## データ取得API
```html:demo.html
  <body onload="init();" id="stage" class="theme">

    <h1>猫の手</h1>
    <a href="#" class="btn large" onclick="getLocation();">現在地付近で迷子の猫を探す</a>
    <a href="https://script.googleusercontent.com/macros/echo?user_content_key=your gas id" class="btn large">迷子の猫を探す</a>
    <a href="#" class="btn large" onclick="get_cats();">猫データを取得する</a>
    <a href="#" class="btn large" onclick="show_pic();">見つけた猫の写真を撮る</a>
    <div id="viewport" class="viewport" style="display: none;">
      <img style="width:60px;height:60px" id="test_img" src="" />
    </div>
    <div id="table">default
    </div>
  </body>
```
クラウドIDEでのエミュレータ画面
<img width="300" alt="api-ide" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/7e5962f7-b991-d5b6-b00e-c7e4b0685325.png">

403エラーになりました
<img width="300" alt="api-ide-403" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/03cdf1b0-66dd-5dce-9ef3-227ecec15574.png">

ビルドして実機に入れると動作しました
<img width="300" alt="api" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/c04c150d-9ae7-760e-3f69-8dc97f8ff9b4.jpeg">

JSONデータとしてデータだけ受け取る場合は、クロスオリジン制約に引っかかります。
これを越えるためにはcordova-plugin-advanced-httpが必要そう。

https://press.monaca.io/atsushi/5046

カスタムプラグイン機能はProプラン以上でないと使えないので、別の方法を考える必要があります。

# まとめ
MonacaでGAS APIと連携はできたものの、実際に扱うには有償プランが必要
無料でアプリを作るために、別の手段を検討中（Flutterなど）
GASで気象庁のAPIから3日間のデータを取得する

# 気象庁のデータ閲覧
・下記のURLにてブラウザ上で天気を確認することができます。
https://www.jma.go.jp/bosai/forecast/#area_type=class20s&area_code=1320400

# json形式での取得
・今回はGoogleAppScript上で見たいため、json形式を指定すると下記のURLになります。
https://www.jma.go.jp/bosai/forecast/data/overview_forecast/[エリアコード].json
・エリア一覧は下記から参照できます。
https://www.jma.go.jp/bosai/common/const/area.json

# エリア一覧をGoogleAppScript上で見る場合
```areaCode.gs
function areaCode() {
  // 地方一覧を知りたい場合
  var url = "https://www.jma.go.jp/bosai/common/const/area.json";
  var json = UrlFetchApp.fetch(url).getContentText();
  var jsonObject = JSON.parse(json);
  Logger.log(jsonObject);
}
```

# 3日分の天気予報を取得する
今回は東京地方のみを取得してみます。
そのため、エリアは130000を指定しています。
```weatherForecast.gs
function weatherForecast() {
  var baseUrl = "https://www.jma.go.jp/bosai/forecast/data/forecast/";
  var areaCode = "130000.json";
  var json = UrlFetchApp.fetch(baseUrl + areaCode).getContentText();
  var jsonObject = JSON.parse(json);
  for(var i=0; i<jsonObject[0].timeSeries.length; i++){
    const date = new Date(jsonObject[0].timeSeries[0].timeDefines[i]);
    var dayStr = Utilities.formatDate(date, 'JST', 'yyyy-MM-dd');
    Logger.log(dayStr);
    Logger.log(jsonObject[0].timeSeries[0].areas[i].weathers);
  }
}
```
実行をすると下記が出力されます
```出力結果
17:58:27	お知らせ	実行開始
17:58:28	情報	2022-05-25
17:58:28	情報	[晴れ　夜　くもり　所により　雨　で　雷を伴う, くもり　昼前　まで　時々　晴れ, くもり　時々　雨]
17:58:28	情報	2022-05-26
17:58:28	情報	[晴れ　夜　くもり, くもり　昼過ぎ　晴れ, くもり　時々　雨]
17:58:28	情報	2022-05-27
17:58:28	情報	[くもり　所により　夕方　雨, くもり, くもり　時々　雨]
17:58:28	お知らせ	実行完了
```
また当日の詳しい天気予報をテキストで取得したい場合は、baseUrlを下記に変更してみてください。
https://www.jma.go.jp/bosai/forecast/data/overview_forecast/
スプレッドシートでカレンダーを作る①

# やりたいこと

I2に指定した月をいれると、自動的にカレンダーの日にちが変わるものを作りたい
今回はGASを使わずに、関数と条件付き書式だけで実装

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663663/1ff4a663-fc80-6f3f-f14f-95dab7181356.png)

↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663663/b520fda6-1b75-67b7-0122-6ae0add25f89.png)

# 参考

https://office-hack.com/spreadsheet/spreadsheet-calendar/
こちらを参考にさせていただきました。
というよりこの回はほとんどこちらの通りに作成していますので、
より詳しくは上記URLを参考にしてください。

次の回で、GASを使ってカレンダーに記載する予定の数に応じて
行を増やしながらカレンダーを作成します。

# 実装

## 日付の自動入力
C6セル(1週目の日曜日の日付)に以下計算式を入力

> `=DATE(J2,I2,1)-(WEEKDAY(DATE(J2,I2,1),1) - 1 )`


Date関数は`DATE(年, 月, 日)`で日付に変換する関数です。

Weekday関数は`WEEKDAY(日付, [種類])`で指定した日付に対応する曜日を数値で返します。
今回は種類に｢1｣を指定することで、日曜日=1、土曜日=7としています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663663/733ab84a-d99e-b231-9d8e-748b9f2e92f3.png)

あとは、`=前日セル+1`ってやっていけばOKです


写真では既に日にちの表示になっていますが、
実際は`yyyy/mm/dd`の形で表示されてるかと思います。

表示形式>>数字>>カスタム日時
から以下のように設定してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663663/f95c14b5-e094-f73c-4bec-8ce50166129e.png)


## 日付の色

スプレッドシートの[条件付き書式設定]で、土日と祝日に自動的に色をつけます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663663/f135539b-44b8-6486-188f-96bf43a47ebc.png)

条件付き書式のデータ範囲は、少しめんどくさいのですが、
日付が入っているセルを選択します。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663663/3650902f-9b99-8cfd-3a13-f9aad84d5801.png)


### 土日

条件で`カスタム数式`を選択し、
土曜日の場合は、`=WEEKDAY(C6)=7`
日曜日の場合は、`=WEEKDAY(C6)=1`
で設定します。
`(C6)`と入れているところは、範囲開始の先頭セルを選んでいます。

### 祝日

祝日の場合は、まず｢祝日｣シートを作って
以下のように入力します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663663/2a6fe201-9ef7-3568-4429-72ebeb81fa21.png)

そして、名前付き範囲を作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663663/dbe080bd-a872-eb8f-af8a-ffaa89884660.png)

カレンダーのシートに戻り、
条件付き書式に土日と同様に範囲を設定し、
`カスタム数式`で以下を入力
`=COUNTIF(INDIRECT("祝日"), C6)=1`

### 今月以外をグレーアウト

条件付き書式に土日と同様に範囲を設定し、
`カスタム数式`で以下を入力
`=MONTH(C6)<>VALUE(SUBSTITUTE($I$2,"月",""))`
`$I$2`と入力しているところは、実際に月を入力するセルを絶対参照で入れます。

:::note warn
条件付き書式は上から順番に適用されるので注意が必要です。
* 今月以外をグレーアウトを **一番上** にもっていった場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663663/8bb54ec6-8551-c997-a063-5749e28ca74a.png)
* 今月以外をグレーアウトを **一番下** にもっていった場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2663663/ac672680-917a-2b1e-7775-713937308111.png)
:::


### ②はこちら
https://qiita.com/sorapi1245/items/8a5db192375c9b22f48b
GoogleFormとGoogleSpreadSheetとGoogleAppsScriptで、フォームとDBとデータ取得APIを作成する

# 背景
- 迷子猫捜索アプリを作りたい
- (その機能として)入力フォームとデータ取得APIを用意したい
- 無料で手間をかけずに作りたい

GoogleFormを使い、SpreadSheetをDBに見立てて、GASでAPIを準備すれば楽なのでは？
→ 試してみた

# 実装

## フォーム
GoogleFormを使って項目を作る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/f2f3e0d9-7413-0cbc-8d15-f7889e2f91d4.png)

## DB
Formの回答をSpreadSheetに書き出す
関連付けされたSheetは名前を変えてもOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/597ce21a-d18e-0a84-92bd-3d726a9d37bb.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/0f3b43c9-ddf4-2f19-67cf-be6abb5a8bc5.png)

## API
GoogleAppsScriptsを使ってSpreadSheetの情報を返すAPIを作る
```js:gas.js
function doGet(e) {
  var table = getTableFromSheet()
  var payload = JSON.stringify(table)

  ContentService.createTextOutput()
  var output = ContentService.createTextOutput();
  output.setMimeType(ContentService.MimeType.JSON);
  output.setContent(payload);

  return output;
}

function getTableFromSheet() {
  const SHEET_URL = "https://docs.google.com/spreadsheets/d/your sheet id/";
  const SHEET_NAME = "your sheet name";

  var spreadSheet = SpreadsheetApp.openByUrl(SHEET_URL);
  var sheet = spreadSheet.getSheetByName(SHEET_NAME);
  var table = sheet.getDataRange().getValues();

  return table;
}
```
# 確認
デプロイ後にURLにGETアクセスすると、SpreadSheetの情報をJSONで返しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628523/5881d52e-b7dc-f22f-c8c9-fc45d88cec9c.png)

# まとめ
実際にアプリに組み込む際にはアクセス権の調整などは必要そうですが、フォーム・DB・APIを無料でサッと作るには良いかと思います。
Ubuntu 22.04のKubernetes上にMetrics Serverをインストールする

# 目的
- Kubernetes環境上にmetrics servcerをインストールする
- 自分用のメモ

# 前提
- Ubuntu 22.04にKubernetesをインストール済み
- ノード名は`ubuntu-k8s-master`

# 手順
## マスターノードの設定変更
### マスターノードでPodがScheduleされるようにする
マスターノード以下を実行し、PodがScheduleされないように設定されていることを確認（デフォルト設定）
```
$ kubectl describe nodes ubuntu-k8s-master | grep -i taint
Taints:             node-role.kubernetes.io/control-plane:NoSchedule
```
確認できたら、続けて以下を実行し、PodがScheduleされるようにする
```
$ kubectl taint nodes ubuntu-k8s-master node-role.kubernetes.io/control-plane:NoSchedule-
node/ubuntu-k8s-master untainted
```

## metrics serverのインストール
### metrics serverのYAMLマニフェストファイルのダウンロード
証明書関連の問題でそのままでは完全には起動しないので若干修正
```
$ wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.6.1/components.yaml
```

### metrics serverのYAMLマニフェストファイルの修正
```
$ cp -p components.yaml components.yaml_`date +%Y%m%d`
$ vi components.yaml
```
`args:`の末尾に`- --kubelet-insecure-tls`を追記
```
$ diff components.yaml components.yaml_`date +%Y%m%d`
140d139
<         - --kubelet-insecure-tls
```

### metrics serverのデプロイ
```
$ kubectl apply -f components.yaml
```

### 起動確認
```
$ kubectl get pods -A | grep metrics
kube-system   metrics-server-658867cdb7-r74zg             1/1     Running   0             11m
```

### kubectl topの確認
```
$ kubectl top node
NAME                CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
ubuntu-k8s-master   97m          2%     902Mi           11%
```
```
$ kubectl top pods -A
NAMESPACE     NAME                                        CPU(cores)   MEMORY(bytes)
kube-system   coredns-6d4b75cb6d-b945m                    2m           13Mi
kube-system   coredns-6d4b75cb6d-tx77z                    1m           13Mi
kube-system   etcd-ubuntu-k8s-master                      12m          59Mi
kube-system   kube-apiserver-ubuntu-k8s-master            40m          285Mi
kube-system   kube-controller-manager-ubuntu-k8s-master   12m          43Mi
kube-system   kube-flannel-ds-rjtfp                       2m           13Mi
kube-system   kube-proxy-t78jq                            1m           17Mi
kube-system   kube-scheduler-ubuntu-k8s-master            3m           18Mi
kube-system   metrics-server-658867cdb7-r74zg             4m           17Mi
```

# 今後の予定
- ~~metrics serverを追加する~~
- prometheusを追加する
- クラスターにノードを追加する
- zabbixを動作させる
web.xml의 DispacherServlet

０．web.xmlの内容

- ServeltContextの初期パラメータ

- セッションの有効時間設定

- Servlet/JSPに対する定義

- Servlet/JSPマッピング

- MimieTypeマッピング

- Welcome File list

- ErrorPages処理

- リスナー/フィルター設定

- 保安


web.xmlで全部抜かずに一部分はcontrollerに行くようマッピングしておいてFrontControllerから受け取って処理する。 
ところでwebではrequest、responseごとに新しいrequestオブジェクトとresponseが生成され上書きされデータが消えるが、
これを防ぐのがRequestDispatcherである。

スプリングでは、FrontControllerとRequestDispatcherの仕事を集めてDispatchServeltで処理する。


スタティック以外のジャバソースはすべてnewを使って使用しなければならないがスプリングではnewする必要がない

DispatchServletがコンポーネントスキャン(ComponentScan)をして必要なオブジェクトをすべて自動的にnewしてメモリに表示するため、






１．スプリングは誰が今newになるべきか分かるだろうか

​それをするのがアノテーション（annotation）だ。


@Controller@RestController@Configuration@Repository@Service@Componetなどが代表的な自動オブジェクト生成アノテーション
それではこうしてnewをつけておくとクライアントの要請を見てDispatchServletがそのClassに接続してくれる
つまり、DispatchServletがすることはコンポーネントスキャンをしてどこに行くかアドレスを分配すること。

​
２．スプリングサーバの動作方式

２－１．​スプリングサーバ（トムキャット）を実行させるとweb.xmlを読み込んで設定。

それでContextLoaderListenerがロードされ、root-context.xml(ApplicationContext.xml)を読みながら、

DB関連(いずれも共通して使用するため、スレッドを生成して管理する必要がない)オブジェクト(Dao,voetc)をコンポーネントスキャン(componentscan)してメモリに表示

​

２－２．サーバが実行されてからユーザからrequest要請があれば、

servelt-context.xmlを読んでDispatcherServletをメモリにロード。ロードしながら関連するすべてのコンポーネントをスキャンしてIoCコンテナに積載

住所に合ったコントローラを探してほしいと！！

ハンドラーマッピングに要請する(設定してノンスレッドの数だけ生成、リサイクルする)、該当class(Iocに積載された)所に行けるようにする。

つまり、後ろのメモリにアップロードされたclassからDBへのアクセスが可能(逆順不可)
Github Actions RSpec, Jestの設定メモ

初学者がGithub ActionsでRSpec, Jestを設定した時の備忘録です。

## 環境
macOS Monterey 12.3.1
Rails 6.1.6
Ruby 3.1.2
React 18.1.0
Node 16.15

## 0. ディレクトリ構成

現在のディレクトリ構成は以下の通り。

myapp/
┣ backend/ # Railsアプリ
┗ frontend/
  ┗ app/ # Reactアプリ

## 1. ymlファイルの作成

以下の構成でディレクトリ、ファイルを作成する。

myapp/
┣ .github/ # 作成
┃ ┗ workflow/　# 作成
┃   ┗ test.yml　# 作成 (任意のファイル名)
┣ backend/
┗ frontend/
  ┗ app/

## 2. RSpecの設定

こちらを参考にして作成しました。

https://docs.github.com/ja/actions/automating-builds-and-tests/building-and-testing-ruby

https://qiita.com/ham0215/items/41a66a17190dd7c319a7

https://zenn.dev/ryouzi/articles/cd6857c08e60e7

```yml:test.yml
name: Test

on:
  pull_request:
    branches:
      - main # mainブランチに向けたプルリクエスト作成時に実行

jobs:
  rspec:
    name: RSpec
    runs-on: ubuntu-latest
    env:
      RAILS_ENV: test
    defaults:
      run:
        working-directory: backend # ディレクトリを指定 (runに適用される)
    services:
      mysql:　 # テスト用データベースの設定
        image: mysql:8.0.29
        ports:
          - 3306:3306
        env:
          MYSQL_ALLOW_EMPTY_PASSWORD: yes # パスワード空を許可
          BIND-ADDRESS: 0.0.0.0
        options: --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 10 #ヘルスチェック
    steps:
      - uses: actions/checkout@v3
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: "3.1.2"
          bundler-cache: true # キャッシュの設定
          working-directory: backend # ディレクトリを指定 (ここでも指定しないとキャッシュされない)
      - name: Run migration
        run: |
          cp config/database.yml.ci config/database.yml
          bundle exec rails db:create
          bundle exec rails db:migrate
      - name: Run RSpec
        run: bundle exec rspec
```

`steps:`以下は各リポジトリのページを参考にしました。

https://github.com/actions/checkout

https://github.com/ruby/setup-ruby

`bundler-cache: true`を設定すると`bundle install`まで実行してくれます。

合わせてconfig/database.yml.ciを作成しました。

```yml:database.yml.ci
test:
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: # 空
  host: 127.0.0.1
  database: myapp_test
```

## 3. Jestの設定

公式Docsを参考にして作成しました。

https://docs.github.com/ja/actions/automating-builds-and-tests/building-and-testing-nodejs#example-using-yarn

```yml:test.yml
...
# RSpecの下に追加

  jest:
    name: Jest
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: frontend/app # ディレクトリを指定
    steps:
      - uses: actions/checkout@v3
      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: "16.15"
          cache: "yarn"
          cache-dependency-path: "frontend/app/package.json" # ファイルのパスを指定
      - name: Install dependencies
        run: yarn install
      - name: Run Jest
        run: yarn run test
```

設定ファイルをgithubへプッシュし、プルリクエスト作成するとRSpec, Jestが実行されました。
今後はfrontend, backendの片方を編集した時に、対応したテストのみが実行されるように修正しようと思います。
M1MacのGPUで PyTorch動くぞ！

みなさんこんにちは。げそんです。
なにやらPyTorchがM1macのGPUに対応したらしいので使ってみました。

https://pytorch.org/blog/introducing-accelerated-pytorch-training-on-mac/

### 環境
MacBook Pro 16inch M1Max 
macOS 12.3.1 
python==3.9
torch==1.13.0.dev20220524

### インストール方法
pipでpytorch-nightlyを入れれば普通に使えるようです。
```sh
pip3 install --pre torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/nightly/cpu
```


### GPUの使い方
どうやらdevice名は`mps` (Metal Performance Shader)になるようです
```py
import torch
mat = torch.randn(5,5,device="mps")
print(mat)
```
output
```
tensor([[-1.2882, -0.0142, -0.4158, -0.1735, -0.6549],
        [-1.0239,  1.3813, -0.2038, -0.4096,  0.9557],
        [-2.1195,  0.7094,  0.7672,  2.3561,  1.5051],
        [ 1.2114, -0.2336, -1.6929, -0.1132,  1.5441],
        [-0.6241,  0.8835,  1.7996, -0.2908,  1.1778]], device='mps:0')
```


### ベンチマーク
とりあえずでっかい行列計算させてみました。
8192x8192の32bit不動小数点行列を計算させてみました。普通に高速化されました。

```py
import torch
import timeit
gpu_device = "mps"
dtype = torch.float
number = 10
size = 8192
cpumat = torch.randn(size,size,dtype=dtype)
gpumat = cpumat.to(gpu_device)

cpu_time = timeit.timeit("torch.matmul(cpumat,cpumat)",number=number,globals=globals())
print("cpu_time: {:.6f} seconds".format(cpu_time))
gpu_time = timeit.timeit("torch.matmul(gpumat,gpumat)",number=number,globals=globals())
print("gpu_time: {: .6f} seconds".format(gpu_time))
```
output
```
cpu_time: 6.042504 seconds
gpu_time:  0.010529 seconds
```
ちなみに筆者のRTX3090で上記のベンチマークを実行した時は 0.00017秒くらいだったのでボコボコですね

### 問題
- 実装されている演算が少ない
累乗の演算など対応していない演算がとても多いです。
- バグが多い
`torch.arange(10,device="mps")`の返り値が`tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0], device='mps:0')`となるなど根本的に計算結果を狂わすバグが数多く潜んでいます。
- なぜか実行回数を多くすると遅くなる
先ほどのベンチマークのtimeitの試行回数を30にしたあたりからグンと遅くなります。Cache周りかなーと適当に考えていますがそこら辺はよくわかりません。

### 所感
兎にも角にもやっぱりGPUは速いです。そしてMacでもPyTorchが使えるようになったことは非常に嬉しいです。
[Microsoft Teams]入れ子になったセキュリティグループでアプリポリシーを適用する

Microsoft Teams で特定の部門だけにアプリの利用を許可したい、強制ピン止めを変更したいといった場合、アプリの「アクセス許可ポリシー」や「セットアップポリシー」をユーザーに割り当てる必要があります。

課や部単位でセキュリティグループを作成しており、例えば以下のような図でX事業部に共通のポリシーを割り当てたいとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2095776/e241e838-954c-b598-073f-3f3b3ef7c714.png)

この場合、AzureADで「X事業部」のメンバーを取得すると「A課」と「B課」が該当します。さらに「A課」と「B課」のメンバーを取得するとようやく今回ポリシーを取得すべきユーザー（User1さん、User2さん、User3さん、User4さん）が取得できます。

このような入れ子になっているセキュリティグループに対して、メンバー全員にポリシーを割り当てるPowerShellスクリプトを作成したのでご利用いただければと思います。

```PowerShell
#対象のセキュリティグループと適用するポリシー
#適宜変更するか引数指定にしてください。

$secGroup="X事業部"
$permissionPolicyName="Japan permission"
$setupPolicyName="Japan setup"

#
#AzureAD/Teams モジュールのインポートと接続
#

Import-Module azuread
Import-Module MicrosoftTeams
$c = Get-Credential
Connect-AzureAD -credential $c
Connect-MicrosoftTeams -credential $c

#
#前回適用されたポリシーの削除(Globalに戻す)
#

$oldList =(Get-Content ".\current.txt") -as [string[]]
foreach($old in $oldList)
{
 Grant-CsTeamsAppPermissionPolicy -identity $old -PolicyName global
 Grant-CsTeamsAppSetupPolicy -identity $old -PolicyName global
 Write-Host "$old さんのポリシーを削除しました"
}

#
#配列の初期化
#
$global:list=@()

#
#再帰呼び出し関数の定義
#
function getMemberList($mems){

 foreach($member in $mems)
 {
  if($member.SecurityEnabled -eq "true")
  {
    $ms = Get-AzureADGroupMember -All 1 -ObjectId $member.ObjectId
    getMemberList($ms)
  }
  else
  {
    $global:list += $member.UserPrincipalName
  }
 }
}

#
# ここからメイン処理／対象のセキュリティグループを取得し関数を呼び出す
#
$members = Get-AzureADGroup -Filter "DisplayName eq '$secGroup'" | Get-AzureADGroupMember -All 1
getMemberList($members)

#
# 作成されたユーザー一覧に対してそれぞれポリシーの割り当て
#

foreach($upn in $global:list)
{
 Grant-CsTeamsAppPermissionPolicy -identity $upn -PolicyName $permissionPolicyName
 Grant-CsTeamsAppSetupPolicy -identity $upn -PolicyName $setupPolicyName
 Write-Host "$upn さんのポリシーを適用しました"
}

#
# 次回実行時に一度ポリシーを削除するためリストを保存
#

$global:list | Set-Content -Path ".\current.txt"

```

# 解説
このスクリプトは再帰構造になっています。
あるセキュリティグループのメンバーを一つずつ確認し、そのメンバーがユーザーならばリストにそのユーザーのメールアドレスを追加します。
セキュリティグループならば、自分自身を呼び出して、メンバーを一つずつ確認して、そのメンバーがユーザーならばアドレスを追加します。
セキュリティグループならば（セキュリティグループ内のセキュリティーグループなら）さらに自分自身を呼び出して…と繰り返します。

（追記）途中でセキュリティグループから抜けたユーザーのポリシーを戻すため、実行終了後に対象ユーザーの一覧を保存し、次回実行時にいったんポリシーを削除するという処理を追加しました。
（追記）IdentityとしてMailよりUserPrincipalNameのほうが適切と思われるため修正しました。

# 注意事項
- 事前にPowerShellを管理者として実行し、以下を実行しモジュールをインストールしておきます。
Install-Module AzureAD
Install-Module MicrosoftTeams -SkipPublishCheck
- セキュリティグループの中に配布グループが…といったようなややこしい構造になっている場合、if 文を変更してご利用ください。
- ポリシーを変更するということはある程度リスクを伴いますので、実行の際はご注意ください。
【備忘録】Rパッケージ開発のファイルの配置

# ファイルの配置を定型化したい

公開を意図していなくても、ファイルの配置を規則的に行うことにはメリットがある。
CRANの作法に（あるていど）従ったファイル配置に関する備忘録を作成する。
特にデータ関連のファイル配置について。
データには使用目的に応じていくつかの種類に分けられる。

1. 公開するバイナリデータ`.rda`
1. 公開しないバイナリデータ`.rda`
1. 公開するその他形式のデータ`.csv` `.xlsx`など
1. 公開しないその他形式のデータ`.csv` `.xlsx`など

この内、1, 2, 3については配置する場所が決まっている。
4についてはっきり言及している資料は見つからないが、プロジェクトフォルダの外に置いておくのがいいだろうと思っている。

1. 公開するバイナリデータは`data/`
1. 公開しないバイナリデータは`R/sysdata.rda`
1. 公開するその他形式データは`inst/extdata/`

## 手順

1. プロジェクトの作成。RStudioを使って新規パッケージ用のプロジェクトを立ち上げる。
1. `usethis::use_namespace()`でNAMESPACEを作成。`R/`配下にスクリプトを作成しないなら必須でない（？）。個人的なデータ生成がもっぱらの関心なら必要ない場合もあるだろう。
1. `usethis::use_readme_rmd()`でREADMEを作成。同上。ただし、RMarkdownを使いたい場合はこれを利用した方がいいと思う。
1. `usethis::use_testthat()`でテスト用フォルダを作成。これも必須ではないが、便利な機能なので使うに越したことはない。
1. `usethis::use_test("テスト名")`で`tests/testthat/test-テスト名.R`が作成される。同上。
1. データの生成や整理に使うスクリプトは`data-raw/`配下へ配置する。このディレクトリは、`usethis::use_data_raw()`を実行することで自動的に作成される。
1. スクリプト内で`usethis::use_data(オブジェクト名)`とすれば、`data/`配下へバイナリデータ`オブジェクト名.rda`が作成される。データの生成はここで行う。バイナリが不要なら`use_data()`の代わりに`readr::write_csv()`など。
1. その他の形式のデータ（csvなど）をプロジェクト内に保存する場合は、`inst/extdata/`配下へエクスポートする。これは公開した場合、公開される。プロジェクト外なら任意でよいだろう。
1. 公開パッケージで、公開しないデータは`R/sysdata.rda`とする。
1. 通常、パッケージ公開にあたってはチェック`devtools::check()`とテスト`usethis::test("テスト名")`が必要になるが、公開目的でないなら必須ではない。このテストはそもそも`data-raw/`以下のファイルは対象にならない（？）
1. また通常、公開パッケージならば`README.Rmd`で、作成したライブラリを読み込んで実演するといったことがよく行われるが、これはあらかじめリポジトリにアップロードされてあるバイナリ形式のライブラリがインストールされていなければ機能しないため、公開していないデータを読み込んで出力することはできない。`devtools::load_all()`で読み込んだデータは`rmarkdown::render()`からは読み込めないようである（？）。`load_all()`の実体は`source()`なので多分そう。

## 予定

公開する場合は、ライセンスの記載やパッケージの説明、マニュアルの作成等の文書を作成する必要があり、またそれらは単なる文書ではなく、相互にリンクしていて、書き方にもフォーマットがある。またそれらを支援するツールがusethisやdevtools、roxygen2などのパッケージから利用できる。
それらについても今後ここに追記するかもしれない。

# 参考文献

1. [Automate Package and Project Setup • usethis](https://usethis.r-lib.org/index.html)
1. [Chapter 14 External data | R Packages](https://r-pkgs.org/data.html)
1. [第1章 新しくRパッケージを作る | Practical R Package Development (Japanese)](https://bookdown.org/yutannihilation/practical-r-package-development-ja/r.html)
1. [【R】 初めてのパッケージ作成で迷った5つのコト。 - Qiita](https://qiita.com/zakkiiii/items/b5b98ca1096b40739dee)
1. [こわくないRパッケージ開発！2016 - Qiita](https://qiita.com/wakuteka/items/54f81f32baf50e243461)
1. [🔧Rパッケージ開発時に利用するデータの種類とその使い分け - cucumber flesh](https://uribo.hatenablog.com/entry/2016/02/09/053000)
セッションとは

# セッション（一連の関連性のある処理）
WebブラウザとWebサーバーのやり取りにおいて一連の関連性のある処理を```セッション```という。
例
ECサイトで商品を選ぶ、買い物カゴに入れる、カゴの中身を確認する、商品を購入するという流れを一連の関連性のある処理と捉える。

## セッションの管理
Webサーバーへのアクセスは多数のWebブラウザから行われている。
Webブラウザからセッションとして扱いたい場合は```Cookie```を用いてセッションを管理する。

いろいろなセッションを識別するために```セッションID```という情報を呼び出す。

```セッションID```はWebサーバーで生成される。Cookieを含めてWebブラウザに送信される。
それ以降```セッションID```を用いて処理することでセッションを維持することができる。
ECサイトで何を買ったかはセッションIDと紐づいたセッションデータをとしてWebサーバーに保存される。

## 思ったこと
```セッションID```がWebサーバー側から生成されてそのサーバー内でセッションを行うことができる。
```セッションID```に紐づけられている```セッションデータ```にデータが保存されているのか。

## セッションIDのやり取り
一般的にCookieを用いてセッションIDをやり取りするが、
場合によってはCookieの使えないWebブラウザもある。
そのため他の手法がある。
・URLにセッションIDを埋め込む手法
・フォームデータに埋め込む手法 
しかしこれらはあまり利用されていない。
それは
Cookieを用いた手法よりも```情報が漏洩する```可能性が高いためだ。
また
```セッションID```は重要な値のためあまり推測されにくい値である必要がある。
最新のタグを取得するGitコマンド

# 結論
```
git describe --tags --abbrev=0
```
※取得できるのはcheckoutしているブランチに対する最新のタグ情報です。
# 今までの自分
私：よし、リリースも終わったし、gitでタグを打つぞ！！
私：そのためには今のタグを調べないといけないなぁ〜〜。
私：とりあえず、「git tag」コマンドを入力っと。

```
$ git tag
v1.0.0
v1.1.0
：
：
v90.1.5
v90.10.0
v90.2.0
：
：
v90.9.4
```
私：うわ、いっぱいタグが並んでるやん！！！！
私：え〜〜〜〜っと、最新のタグは、命名規則にしたがうと...これだ！
私：だから、今回打つタグ名は「v90.9.5」だ!!!
私：おっと危ない、並びに騙されるところだった。やっぱり「v90.10.1」だ！！！

上記では一見「v90.9.4」が最新のタグに見えますが、「v90.10.0」が命名規則的には最新です。

# これからの自分
私：よし、リリースも終わったし、gitでタグを打つぞ！！
私：そのためには今のタグを調べないといけないなぁ〜〜。
私：とりあえず、「git describe」コマンドを入力っと。
```
$ git describe --tags
v90.10.0-8-gxxxxxxxx
```
私：だから、今回打つタグ名は「v90.10.1」だ！！！

上記では最新のタグが「v90.10.0」ということが一目でわかります。
この「git describe」を使うと自分の目でリストから探す必要がなくなります。

# git describeって？
describeは直訳すると「説明」という意味です。
git describeを実行すると「checkoutしているブランチ」の説明として、ハイフン区切りで「タグ名」と「このタグからのコミット回数」と「コミットに対するハッシュ値」が取得できます。
フォーマットに当てると以下のような感じです。
```
タグ名-コミット回数-gコミットのハッシュ値
```
※コミットのハッシュ値の頭にはgがつきます。このgは「git」って意味らしいです。
## オプション「--tags」
オプションで「--tags」とするとそのブランチ以外で打たれたタグを考慮した結果になります。
## オプション「--abbrev」
オプションで「--abbrev=0」とすると「タグ名」のみが取得できます。
オプションで「--abbrev=1」とすると「タグ名」と「コミット回数」と「コミットに対するハッシュ値」が取得できます。
### この数字って？
0を指定するとタグ名のみです。
1以上を指定すると「タグ名」と「コミット回数」と「コミットに対するハッシュ値」が取得できます。
#### 1以上は同じ？
1以上を指定すると同じなのか?というと、違います。
1~5を指定すると「コミットに対するハッシュ値」が5文字で
6以上で指定すると「コミットに対するハッシュ値」が指定した文字数で取得できます。

# 最後に
私のようにタグのリストに目を酷使している人がこの記事によって救われれば幸いです。

# 参考
https://te2u.hatenablog.jp/entry/2014/11/26/003112
Launch Screenが正しく期待通りに表示されない問題

## 現象
LaunchScreen.storyboardを利用して起動時スプラッシュを表示しようとすると
- ノッチあり端末では、スプラッシュが出るはずの画面が真っ暗になってしまう
- ノッチなし端末では、期待通りに表示される

というややこしい問題が起きていた。

## 原因と解決方法
LaunchScreenにpdfを画像として利用していたことが原因。
これをpng画像に差し替えたところ、どちらでも期待通りに表示されるようになった。
AppleのLaunchScreen Debugガイドによると、LaunchScreenに使う画像はjpg/pngのどちらかでなければいけないそう。

```
The image in your launch screen storyboard did not originate from the target’s asset catalog. If you are using static images in your launch screen, check to make sure the image originates from the product’s asset catalog as a JPG or PNG image.

```

## See Also
https://developer.apple.com/documentation/technotes/tn3118-debugging-your-apps-launch-screen

## 環境
Xcode 13.2.1
iOS 14.x/15.x
シミュレータ/実機どちらでも

### 検索用キーワード Debug Launch Screen with Safe Area(with notch) Not showing
jqを使ってJSONの配列をElasticSearchのBulk API に投入できる形に変換する

例えば以下のようなデータ郡をElasticSearchに投入したいとする

```json:dataset.json
[
  {
    "isbn": "978-4-00-023745-1",
    "title": "それで君の声はどこにあるんだ?",
    "sub_title": "黒人神学から学んだこと",
    "author": "榎本空 著",
    "publisher": "岩波書店",
    "published_at": "2022.5",
    "series": "",
    "price": 2000
  },
  {
    "isbn": "978-4-00-061532-7",
    "title": "気候民主主義",
    "sub_title": "次世代の政治の動かし方",
    "author": "三上直之 著",
    "publisher": "岩波書店",
    "published_at": "2022.5",
    "series": "",
    "price": 2100
  },
  ...
]
```

複数データの投入はElasticSearchの[Bulk API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-bulk.html)を使えば実現できるのだが、これはpostするデータのフォーマットが少々特殊なため加工してあげる必要がある。

具体的には以下のようなフォーマットが必要

```json:bulk_dataset.json
{"index":{"_index":"books","_id":"978-4-00-023745-1"}}
{"isbn":"978-4-00-023745-1","title":"それで君の声はどこにあるんだ?","sub_title":"黒人神学から学んだこと","author":"榎本空 著","publisher":"岩波書店","published_at":"2022.5","series":"","price":2000}
{"index":{"_index":"books","_id":"978-4-00-061532-7"}}
{"isbn":"978-4-00-061532-7","title":"気候民主主義","sub_title":"次世代の政治の動かし方","author":"三上直之 著","publisher":"岩波書店","published_at":"2022.5","series":"","price":2100}
...
```


これはjqを使って以下のように指定してあげれば:ok: 

```terminal
$ cat dataset.json | jq -c '.[] | ({"index":{"_index": "books", "_id": .isbn}}, .)' > bulk_dataset.json

# curlで投入
$ curl -X PUT "http://localhost:9200/books/_bulk?pretty&refresh"  -H "Content-Type: application/json" --data-binary "@bulk_dataset.json"
```
【Django】カラム追加とか色々やったあとにテーブル（モデル）を作り直してマイグレーションファイルをきれいにする手順

初期開発してて、とりあえず思いつきでモデル作成して、その後に「このカラムも必要」、「あれも必要」ってしたとき、マイグレーションファイルが増えちゃってきれいにしたいときの処方箋です。


```
 $ python manage.py showmigrations app_name
app_name
[X] 0001_initial
```

[X] がついているのはマイグレーション実行済みってことですね。

これで現在のマイグレーション状況を確認できます。これは別に叩かなくてもいいです。
app_nameを省略するとすべてのアプリケーションが対象になります。


## 今までのマイグレーションをなかったことにする。
```
python manage.py migrate <app_name> zero
```

zeroにします。

これを叩くと、app_nameに関連するマイグレーションがなかったことになります。
具体何が起こるかというと、
- app_nameに関連するテーブルがデータベースから消えます（DROP）
- django_migrationsっていうマイグレーションを管理しているテーブルからapp_name関連で行ったマイグレーションのレコードが消えます

これで完了かと思うとまだです。
マイグレーションファイルはまだ残っているのでこれを消します。

ちなみにこの状態でshowmigrationsを叩くと、

```
 $ python manage.py showmigrations app_name
app_name
 [ ] 0001_initial
```

を叩くと、こうなります。

このことから、
0001_initialの部分はマイグレーションファイルがあるか否かで表示している。
加えて、django_migrationsのテーブルの中に0001_initialがあったら[X]がつく。
みたいな感じと推測できます。


## マイグレーションファイルの削除

```
find app_name/migrations -type file | grep -v -E '__init__.py' | xargs rm -rf
```


これで完了です。

※ubuntuとかの環境の場合、

```
find app_name/migrations -type f | grep -v -E '__init__.py' | xargs rm -rf
```
じゃないと怒られるかもしれません。


もし、 -type fileをつけ忘れてしまうと、migrationsのディレクトリが消えてしまって、makemigrationsが叩けなくなります。
そうなったら自分でmigrationsディレクトリを作って__init__.pyを作らないといけないです。
なんでこんなことわざわざ言ってるのかというとお察しの通りです。


## 以上です

誰かのお役に立てばさいわいです。

ちなみにこの記事を見てる方の半分くらいはもしかすると、

```
python manage.py squashmigrations
```

これで幸せになれるんじゃないかなと思ってます。
Python RQ を利用している場合のワーカー更新手順

# はじめに

Python でキューを利用したバックグラウンド処理を実現可能なライブラリの１つとして rq がある。

https://python-rq.org/

メッセージキューとして Redis を利用したライブラリであり、任意の数のワーカープロセスを立ち上げることでバックグラウンド処理が実行できるようになる。 Rabbit MQ や Kafka などのメッセージキューイング用の特別なミドルウェアを用いなくても、小規模なバックグラウンド処理用のシステムを構築できる。

これを本番で使ってみて、安全な更新を行うにはどうすればよいのかを調査した結果、一癖あったのでその調査結果と手順を書き記す。

# 問題点

アプリケーション（ワーカー）を更新する場合、全てのワーカーが動作していない状態で更新しないと、ジョブが処理途中のままになってしまう。 そのため、更新時には先だってフラグを立て、各ワーカーがこれ以上新しいジョブを実行しないようにしておき、全てのタスクが完了した状態で更新を行いたい。

この問題を解消するために suspend モードがある。 rq で提供されているcliで、`rq suspend` として実行することで、各ワーカーがこれ以上新しいジョブを一時的に受け取らないようにできる。 もちろん、以下の実行例でも記されているように、現在ワーカーで実行中のジョブは停止しない。 

```bash
$ rq suspend
Suspending workers.  No new jobs will be started.  But current jobs will be completed
```

この挙動から、利用者が考えるのは「suspend 状態にしたから、新しいジョブを受け取らないだろう」ということなのだが、実はこのような直感的な挙動になっていない。
**idle状態で、何のジョブも実行していないワーカーは新しくジョブを受け入れる状態を続けている。** そして、１つでもジョブを完了させると suspend 状態となり、新しくジョブを受け入れない状態となる。
この問題は以下の Issue にまとめられている。

https://github.com/rq/rq/issues/526


# 解決策

上記Issueで対処法がいくつか書いてあるが、更新時に何もしないジョブ（番兵）を適当な個数実行させるのが簡単である。 bash での判断例は以下の通り。

```bash
# idle 状態のワーカーがいるかどうかを確認して、件数分番兵ジョブを登録する
$ rq info | grep idle | wc -l 

# 全てのワーカーが suspend 状態であればワーカーを再起動して更新を反映
# なお suspend 状態で起動したワーカーは最初から状態が suspend になっている
$ rq info | grep suspend | wc -l

# 再起動後は suspend を解除
$ rq resume
```