OmniAICreator/Qiita-1.07M huggingface dataset:
react-routerでnavigateする時、stateはどこに保存されているの？

# 結論

**window.historyのstate**

# 前提
react-routerはなんとなくで使えてしまうので、一応確認したという程度のメモ書きです。
[react-router公式](https://reactrouter.com/en/main/start/overview)のメインコンセプトによると、HistoryはURLの変更をsubscribeできるようにするオブジェクトで、ブラウザのhistoryをプログラム的に操作するAPIも提供していると記載があります。
> History - An object that allows React Router to subscribe to changes in the URL as well as providing APIs to manipulate the browser history stack programmatically.

また、LocationはWEB APIのwindow.locationを基にしているという記載もあります。
> Location - This is a React Router specific object that is based on the built-in browser's window.location object. It represents "where the user is at". It's mostly an object representation of the URL but has a bit more to it than that.

このコンセプトを見る限り、ブラウザ標準APIを利用してURLの変更を検知→描画するDOMを変更するライブラリということが分かります。

https://reactrouter.com/en/main/start/concepts#history-and-locations

# 実際にstateを渡して遷移してみる
メインコンセプトの時点で大方検討はついていますが、実際にconsole.log()で確認してみます。
```tsx:stateを渡す起点となるページ
const MockPage2 = (props: Props) => {
    const navigate = useNavigate()
    const navigateWithState = () => navigate('/mock', {state: {id : 99}})
  return (
    <>
      <Typography>MockPage2</Typography>
      <Button onClick={navigateWithState}>react-routerでstateを渡して遷移</Button>
    </>
  )
}
```

```tsx:stateを貰うページ
const MockPage = (props: Props) => {
    const location = useLocation()
    const state = location.state as {id : number}
    console.log(window.history)
    console.log(window.location)
  return (
    <>
      <Typography>MockPage</Typography>
      <Typography>{state.id}</Typography>
    </>
  )
}
```
遷移する前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647020/2d20e637-ed7b-20fc-7d81-2f34be683260.png)

遷移後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647020/679e3f94-c85a-350c-347e-3b6a7add8ae1.png)

# Window.locationには何も入っていない

window.historyのプロパティstateに保存されており、react-routerのuseLocationはwindow.historyのstateを便利に取得してくれているようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647020/1319def4-0f25-641b-cae3-1c77d667717a.png)

ちなみにsessionStorageやlocalStorageにも保存はされないです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647020/a1933c86-6d03-06b3-52dd-ca46bc358d09.png)


# 参考
https://developer.mozilla.org/ja/docs/Web/API/History

https://developer.mozilla.org/ja/docs/Web/API/Location
【SwiftUI】Labelを簡単に使えるように拡張する

# はじめに
SwiftUIのUIコンポーネントには複数の書き方があります。
例えばButtonだと以下のような書き方があります。

```swift
Button {
    
} label: {

}
```

```swift
Button("") {

}
```

2個目の書き方は`Text`を省略することができます。
Labelでも同じような書き方ができるように拡張します。

# 実装
```swift
extension Label where Title == Text, Icon == Image {
    init<T: StringProtocol>(_ title: T, icon: Image) {
        self.init {
            Text(title)
        } icon: {
            icon
        }
    }
}
```

# 使い方
```swift
Label("このように書くことができます", icon: Image(systemName: "volleyball.fill"))
```

# おわり
1行で書けるようになりました。
[macOS]アンインストールしたSteamゲームのアイコンの削除方法

# 事象
Steamでインストールしたゲームをアンインストールした後、ゲームのアイコンがLaunchpadから消えていませんでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954734/547f5974-7253-a0e5-0c01-5d0a83d04d40.png)



# 試したこと
Finderで「Applications」を開き、そこからアプリを削除しようとしたのですが、該当アプリが存在しませんでした。
![Screenshot 2023-02-14 at 22.21.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954734/4e68a36c-86c1-e7b8-baee-2bdc2155eb3c.png)


# 対処法
ルートディレクトリ配下の「Applications」ディレクトリではなく、ユーザーホームディレクトリ配下の「Applications」ディレクトリに該当アプリが入っていました。
以下のように、ユーザーホームディレクトリ配下の「Applications」ディレクトリ内の該当アプリを削除すればOK。
![Screenshot 2023-02-14 at 22.27.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954734/d6d629c0-e005-7777-783d-7be205c9b8ee.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954734/a377e0f4-769c-da70-4ef7-a17880684a3d.png)
Launchpadからも消えました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2954734/34da6629-1503-6d9a-26d2-853266888e98.png)
10の倍数の差が2以内で条件分岐する方法

復習しアウトプットします。


```ruby:
正の整数を入力し、その整数が、10の倍数（10,20,30...）からの差が2以内であるときはTrueそれ以外はFalseを出力するメソッドを作成
>出力例：
near_ten(12)→True
near_ten(17)→False
near_ten(19)→True
```


```ruby:自身の回答
def near_ten(num)
 if  num_x = num % 10
     num_x <= 2 || num_x >= 8
     puts "True"
 else
    puts "False"
 end
end
```
```ruby:模範解答
def near_ten(num)
  quotient = num % 10
  if quotient <= 2 || quotient >= 8
    puts "True"
  else
    puts "False"
  end
end
```

惜しい。。。。惜しいい！！！！！
悔しい。笑


# アプトプット解説
```diff_ruby:
def near_ten(num)   
+ num_x = num % 10　　  　#入力された数値を１０で割り１の位の余りを算出
- if  num_x = num % 10
-     num_x <= 2 || num_x >= 8
+ if  num_x <= 2 || num_x >= 8  　#一の位が0,1,2,8,9であれば差が2以内となりTrueとなる
     puts "True"
 else
    puts "False"
 end
end
```

:::note info
ポイント
演算子について、違いを把握しておく必要がある。
a / b →除算
a % b →a を b で割った"余り"

:::



まだまだ詰めが甘いな。。。
引き続き復習して行きます。
Data Frameを結合する方法


# 目的
dfを横方向に連結して表示したい。

# 結論
連結なので今回はcontact()を使用する。
横方向に連結したいので、axis=1とする。

https://www.yutaka-note.com/entry/pandas_concat_join_merge

# お試し
```py
import pandas as pd

df1 = pd.DataFrame({"名前": ["田中", "佐藤", "鈴木"], 
                    "出身地": ["北海道", "青森", "秋田"], 
                    "チーム": ["チームA", "チームB", "チームB"]})
df2 = pd.DataFrame({"test1": ["田中", "佐藤", "鈴木"], 
                    "test2": ["北海道", "青森", "秋田"], 
                    "test3": ["チームA", "チームB", "チームB"]})
print(pd.concat({"Data1":df1, "Data2":df2}, axis=1))
```
<実行結果>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/156536/cf866341-bc6b-3035-a7f4-3ba43c7d22ff.png)
docker環境のローカルで、PHPで作成したHTMLファイルをブラウジングする際のURLの意味

# docker環境もXAMP等と同じく、localhostでブラウジングできる

## トップページのURL

```
http://localhost:50080/
```

上記URLのlocalhostは自身のパソコンを指す。
50080の部分はポート番号を指し、何のアプリケーションを指すか？を示している。
この番号はapacheであり、これでdockerのappコンテナ内のapacheにアクセスしていることになる。
その設定はdocker-compose.ymlファイルに記述してある。
以下、その記述箇所

```
ports:
      - "50080:80"
```

上記の :80 の箇所はapacheがデフォルトで決めてある番号で、URLにはここまで記述しなくてもapacheが起動するようになっている。
Django タグ付け機能

Twetterなどで使われているタグ付け機能を作ってみようと思い作成してみました。
とりあえず機能として成り立てばいいかなと思い作成したので、改善点などがあればコメント等で教えていただけたら嬉しいです。

# タグ付け機能をつけるモデル
~~~python:models.py
class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)

class Notes(models.Model):
    title = models.CharField(max_length=255)
    content = models.CharField(max_length=500)
    tags = models.ManyToManyField(Tag)
    favorites = models.ManyToManyField(Users, blank=True, related_name='favorite_notes')
    user = models.ForeignKey('accounts.Users', on_delete=models.CASCADE)
~~~
タグ付け機能を行うモデルとして、「Tag」と「Notes」を定義しました。
「Tag」モデルはタグを保存するためのモデルになっており、「Notes」モデルの`tags`フィールドで多対多で紐付いています。
アプリの機能としてはノートをタグにより分類する感じになります。

# フォームの作成
~~~python:forms.py
class CreateNoteForm(forms.ModelForm):
    title = forms.CharField(label='タイトル')
    tags = forms.CharField(label='タグ') # タグを入力するためのフィールド
    content = forms.CharField(label='あらすじ', widget=forms.Textarea())

    class Meta:
        model = Notes
        fields = ('title', 'content') # Notesモデルにタグ自体を保存しないので除く
~~~
フォームにタグ入力するためのフィールドを作成するために`tags`を定義してますが、`Notes`モデルには保存しないので`fields`で`tag`は指定しません。

# ビューの作成
ビューの関数内でタグの保存の処理などを行います。
~~~python:views.py
def create_note(request):
    create_note_form = forms.CreateNoteForm(request.POST or None)
    if create_note_form.is_valid():
        note = create_note_form.save(commit=False) # save(commit=False)で保存せずにインスタンスを受け取る
        note.user = request.user # Notesはユーザーモデルとも紐付いているのでユーザーを指定
        note.save() # タグ以外のフィールドを保存
        tags = create_note_form.cleaned_data['tags'] # tagsフィールドに入力された値を取得
        tag_list = tags.split() # 入力された値をsplitで半角スペースごとに区切る
        for tag in tag_list:
            tag = tag.lower() # 区切られた値をlowerで小文字にする
            if Tag.objects.filter(name=tag).exists(): # existでタグが既にあるか判定（ある:True,ない:False）
                add_tag = Tag.objects.filter(name=tag).first() # 存在する場合はタグを取得
                note.tags.add(add_tag) # addでタグを追加
            else:
                add_tag = Tag.objects.create(name=tag) # 存在しない場合タグを新しく作成
                note.tags.add(add_tag)
        
    return render(request, 'note/create_note.html', context={
        'create_note_form': create_note_form,
    })
~~~
`note.save()`でタグ以外のフィールドを先に保存し、その後にタグを紐付けていきます。
`cleaned_data['tags']`でtagsフィールドに入力された値を取得し、`tags`に代入しています。
タグは半角スペースで区切って入力するので、`tags`で受け取った値を`split`で半角スペースごとに区切り`tag_list`にリスト型で代入。
`for`文でタグを一つずつ取得し、`lower`で小文字に変換しています。同じ文字列でも別のデータとして扱われてしまうので今回は小文字に統一させます。（Python,python, PYTHONなどが別々に保存され統一性がなくなる）
`if Tag.objects.filter(name=tag).exists():`でタグが既に存在するか判定します。同じ文字列が複数ある場合でも統一性がなくなってしまいます。
タグが存在する場合、`add_tag = Tag.objects.filter(name=tag).first()`でタグを取得。
タグが存在しない場合、`add_tag = Tag.objects.create(name=tag)`でタグを新規作成。
`note.tags.add(add_tag)`により関連付ける事ができる。
Railsのモデルにscopeを定義する場合の注意点


## はじめに
自分用のメモになります。

## 結論
find_byなどのnilを返すメソッドをscopeに設定しない

## なぜなら
モデルのscopeでnilが返される条件が発生すると、該当のscope検索結果を無視した、クエリを発効してしまう。

Humanモデル

``` ruby:human.rb
class Human < ApplicationRecord
  enum :gender, { male:0, female: 1}

  scope :find_gender, -> (gender) { find_by(gender: gender) }
  scope :tall_people, -> {where("height > ? ", 160)}
end

```

DB
``` ruby:
class CreateHumen < ActiveRecord::Migration[7.0]
  def change
    create_table :humen do |t|
      t.string :name
      t.integer :height
      t.integer :gender, default: 0, null: false

      t.timestamps
    end
  end
end
```

## コンソールの結果
``` rb
irb(main):006:0> Human.find_gender("male").count
  Human Load (0.2ms)  SELECT "humen".* FROM "humen" WHERE "humen"."gender" = ? LIMIT ?  [["gender", 0], ["LIMIT", 1]]
  Human Count (0.3ms)  SELECT COUNT(*) FROM "humen"                                 
=> 7                                                                                
irb(main):007:0> Human.all.count
  Human Count (0.2ms)  SELECT COUNT(*) FROM "humen"
=> 7                  
```

"male"は見つからないため、find_byではnilが返される。
scope内でnilが返されたため、scopeの条件は無視されて、実行される。
全てのレコードの数を検索した場合と同じ結果が加勢される










## 参考

https://gihyo.jp/book/2020/978-4-297-11462-6
Web技術の基礎用語（プロトコル、Cookie等）

# はじめに
この記事では以下の項目を説明していきます。
まだあまり理解できていないよ、という方は是非読んでいってください。

- リクエスト・レスポンス
- プロトコル
- ポート番号
- ステートフル・ステートレス
- Cookie

# リクエスト
一言で言うと「要求」です。
どんな要求かと言うと例えば、Yahoo! JAPANのページが見たいとします。
そこで「Yahoo! JAPANのページを見せてください。」とサーバーに要求します。
この要求がリクエストです。

# レスポンス
一言で言うと「応答」です。
先ほどの例を使って、「Yahoo! JAPANのページを見せてください。」と要求したとします。
そこで、サーバー側がこちらに対して、「これがYahoo! JAPANのページです！」といった具合に、要求に対して返事を返してくれます。
このサーバーから返ってくる応答のことをレスポンスと言います。

# プロトコル
コンピュータとコンピュータがお互いに情報をやりとりし合う時にはルールが必要です。
そのルールのことをプロトコルと呼びます。

そもそもルールって必要なの？と思った場合は以下に簡単な例を示しますので見てみてください。
　　例えば、皆さんは日本にいたら、誰かと話す時は日本語で話しますよね。
　　それはお互いが「日本語で会話をする」、「その日本語の意味を知っている」といった暗黙のルールがある上で成り立っています。
　　これが急にフランス語で話しかけられたら会話（コンピュータの場合は通信）出来ないですよね。
　　そういったことからお互いに情報をやりとりするためにはルールを決めてそれを守る必要があります。

いくつかプロトコルを簡単に紹介します。
- HTTP
    - HTMLをやりとりするために定められたプロトコル
- FTP
    - ファイル転送のためのプロトコル
- SMTP
    - メール転送のためのプロトコル

# ポート番号
番号はそのままの意味なので、先にポートについて説明します。
ポートは、コンピュータが情報を受け入れるための窓口のようなものです。
コンピュータは情報を受け取った時に、その情報がどのプロトコルのもので、どのアプリケーションが処理すべきなのか分かりません。
そこでそれを解決するのがポートです。アプリケーションは自分専用のポートを決めて、情報を待ちます。
そのポートに入ってきた情報は自分宛というのが決まっているので、そこで待ち受けているアプリケーションが処理します。

例えば、役所をイメージしていただくと分かりやすいかもしれません。
役所は手続きの種類によって窓口が違っていたりしますよね。
住所変更の手続きは◯番の窓口、パスポートの申請は○番の窓口、といった具合です。

このようにアプリケーションによってポートの番号が違います。
このポートに割り振られている番号のことをポート番号といいます。

# ステートレス
ステートは英語でstateです。stateの意味は「状態」です。
ステートがレスということで、「状態がない」、「状態を保持しない」というイメージです。

プロトコルで紹介したHTTPは、状態を持たないプロトコルでステートレス・プロトコルと呼ばれています。
状態を持たない、というのはどういうことかというと、HTTPでは1回1回のリクエストが独立しており、2回目以降に1回目のの情報が保持されません。
なので2回目のリクエストで、1回目のリクエストをした時に情報を渡したから分かるでしょ？っていうのは出来ないです。

こういった性質のことをステートレスと言います。

# ステートフル
ステートレスとは逆で「状態を保持する」というイメージです。
先ほどのプロトコルで紹介したFTPはステートフル・プロトコルと呼ばれています。
FTPは1回1回のリクエストが完全に独立しているわけではなく、前回のリクエストを覚えていて、その情報を踏まえて次のリクエストを実行します。
このような、状態を持つ性質のことをステートフルと言います。

# Cookie（クッキー）
Cookieの説明をする前にCookieが考案された背景を簡単にお話しします。

先ほど、HTTPはステートレスで状態を持たないと説明しました。
しかしWebアプリケーションにはログインしたユーザーでないと使えない機能とかよくありますよね。
そういった時に「ログインをしている」という状態を表現したいです。でもHTTPは状態を持ちません。。どうしようかと。
そこで考えられたのがCookieです。

話を戻しまして、Cookieの説明です。
Cookieとは、WebサーバーからWebブラウザへ送られる小さな情報です。
この情報は「名前=値」という組み合わせで表現されます。
仕組みとしては、WebサーバーからCookieを受け取ったWebブラウザは、次回同じWebサーバーにアクセスする際、受け取ったCookieをそのままHTTPリクエストに入れてWebサーバーに送ります。
リクエストを受け取った側は、リクエストに入っているCookieを調べることでアクセスしてきた相手が誰なのかを知ることができます。
このような仕組みで成り立っています。

こうしてCookieによって、WebアプリケーションとWebブラウザの間で情報を交換できるようになりました。

# 参考文献
- [プロになるためのWeb技術入門](https://www.amazon.co.jp/%E3%80%8E%E3%83%97%E3%83%AD%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AEWeb%E6%8A%80%E8%A1%93%E5%85%A5%E9%96%80%E3%80%8F%E2%80%95%E2%80%95%E3%81%AA%E3%81%9C%EF%BC%8C%E3%81%82%E3%81%AA%E3%81%9F%E3%81%AFWeb%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E9%96%8B%E7%99%BA%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84%E3%81%AE%E3%81%8B-%E5%B0%8F%E6%A3%AE-%E8%A3%95%E4%BB%8B-ebook/dp/B09MVGM99L/ref=sr_1_1?crid=24JAWMR2VKX7M&keywords=%E3%83%97%E3%83%AD%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AEweb%E6%8A%80%E8%A1%93%E5%85%A5%E9%96%80&qid=1676369109&sprefix=%2Caps%2C189&sr=8-1)

# 終わりに
少しでもイメージが出来るようになっていただけたら嬉しいです。
もし表現が適切でない等ありましたらコメントいただけますと幸いです。
InfluxDBシリーズのカーディナリティは、測定のフィールドキーの数に依存していますか?

短い答えはノーです。つまり、InfluxDB シリーズのカーディナリティは、測定のフィールド キーの数に依存しません。

influx_inspect の reporttsi は、「データベース内の正確な系列カーディナリティの合計」を報告します。データベース内の正確な系列カーディナリティの合計を計算します。

コード層を層ごとにドリルダウンできます。ここの reporttsi コードから、システムがすべてのシャードを反復処理してから、すべての測定値を反復処理して、それらのシリーズ ID を計算していることがわかります。シリーズ ID は、こちらのコードで定義されています。測定とタグのみに関連していることがわかります。

// CreateSeriesListIfNotExists creates a list of series in bulk if they don't exist.
// The returned ids slice returns IDs for every name+tags, creating new 
    series IDs as needed.
func (f *SeriesFile) CreateSeriesListIfNotExists(names [][]byte, tagsSlice 
    []models.Tags) ([]uint64, error) {

系列カーディナリティの計算では、フィールドのキーまたは値は考慮されません。
Flaskのlogに付加情報を出力する

# 訂正（一部ダメでした。。）

```
record.clientip = request.remote_addr
```

上記を設定すると、特定条件（SQLAlchemyでSQLALCHEMY_ECHOを設定した場合など）下で下記エラーが発生してしまいました。

> RuntimeError: Working outside of request context.

appインスタンス生成前にrequestコンテキストにアクセスしてるからと思われます。
とりあえずメモ

## まえがき

趣味の小物アプリでflaskを使った際に調べたこと。
nginxのログと照らし合わせるため、flaskのログにIPアドレスを出したいと思った。
（本当はrequest_id的なもので透過的に見れるといいんですが…）

pythonのloggerではフォーマット指定の際、extraフィールドに任意の識別子を含めることが出来る。
extraパラメータは実際のログ出力時に毎回指定する事も出来るが、filtersを使ってミドルウェア的に挟むことが出来る。

## 環境
> Python 3.10.10
> Flask==2.2.2

## ソース抜粋
```python: common/logger.py
from logging import Filter
from logging.config import dictConfig
from flask import request

class ExtraFilter(Filter):
    def filter(self, record):
        # flaskからremote_addrを取得しclientipとして設定
        record.clientip = request.remote_addr
        return True

def init():
    dictConfig({
        'version': 1,
        'formatters': {
            'default': {
                # ↓↓↓↓↓↓ フォーマットに独自フィールドのclientipを入れた
                'format': '[%(asctime)s][%(levelname)s][%(clientip)s][%(module)s] %(message)s',
            }
        },
        'filters': {
            'extra': {
                # ↓↓↓↓↓↓ 上で定義したフィルタクラスをフルパスで
                '()': 'common.logger.ExtraFilter',
            },
        },
        'handlers': {
            "fileHandler": {
                "class": "logging.FileHandler",
                "formatter": "default",
                # ↓↓↓↓↓↓ ハンドラと紐づけ
                'filters': ['extra'],
                "level": 'DEBUG',
                "filename": f"/logs/flask.log",
                "encoding": "utf-8"
            }
        },
        'root': {
            'level': 'DEBUG',
            'handlers': ['fileHandler']
        }
    })
```

```python: app.py
from flask import Flask
import common.logger

def create_app():
    # ↓↓↓↓↓↓ 初期化
    common.logger.init()
    app = Flask(__name__)
    return app

app = create_app()
```

## 出力結果例
```
[2023-02-14 21:49:52,441][INFO][172.26.0.1][login] test
```

## 参考

https://qiita.com/ping2shi2/items/db8eb4d9158761cb819c

https://docs.python.org/ja/3/howto/logging-cookbook.html#using-filters-to-impart-contextual-information
【UE4】Stringから特定の「文字」や「記号」を削除する

# 0. 最初
こんにちは、皆さんはペペロンチーノ食べていらっしゃいますか？
今回は、BPのStringから指定し特定の「文字」や「記号」を
削除する処理を書いていきます

# 1. 完成
最初に完成形から
「11,4,5,g,h,hd,hr,h,f,jg,r,y,」と入力
![無題の動画-‐-Clipchampで作成-_1_ (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/bb2c8dab-f514-64f0-076b-6dd0873b07d7.gif)

「11/4/5/g/h/hd/hr/h」と入力
![無題の動画-‐-Clipchampで作成-_2_ (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/2246b85e-5781-d9e8-cc68-727462372b6c.gif)

# 2. 解説
処理は以下の通りです
![Sub.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/93e9392b-289d-a9eb-d3ec-bceaefe75c1d.png)

 処理の流れは以下の感じ
1.「FindSubstring」で削除したい文字を数える
2. 削除したい文字の有無を判別し、無い時「-1」が返却される
3. 削除したい文字が有る時「Split」で削除したい文字を中心に分ける
（「111,2」でカンマを削除したいの時「111」「2」に分けられる）
4. 分けられた文字を合わせ再度探索する
5. 削除したい文字が無くなり次第文字列を返す

# 3. 最後
お疲れ様でした
些細な処理ではありますが以外と使う場面が多いです
Intを3桁以上Stringにすると必ずカンマが入るのでその削除などにご活用ください！

UEは日本語の記事が少ないのでこの記事が少しでもお役に立ちますように！
AzureDevOpsのプルリクエストにテンプレートを導入して、レビュアー・レビュイーの負担を軽減したい

# 経緯

Descriptionに何も書かれていないPRのレビューを依頼されると、

- 変更点は何か
- どこを変更したのか
- 何を確認しなきゃいけないか

などを全てレビュアーが判断したり、コミッターに確認せねばなりません。
チケットが紐づいていればまだ糸口を掴めますが、「バグの修正」みたいなアバウトなチケットだと結局何したんや? 状態です。

また、誤字脱字や、`// TODO`コメントの消し忘れが残っていることも結構あり、それに対する指摘数が増えてしまってレビュアー・レビュイー双方にとって負担になる課題もありました。

そこで、PRのテンプレートを使用し、説明の充足・セルフレビューを促すことによるうっかりミスの軽減に努めようと思いました。

# PRテンプレートを用意する

https://qiita.com/07JP27/items/103f8acc9810d917ecbb

https://learn.microsoft.com/ja-jp/azure/devops/repos/git/pull-request-templates?view=azure-devops

上記を参照することで、PRテンプレートを設定する方法を理解できました。

## 実際に作ってみたテンプレート

```pull_request_template.md
# チェックリスト

- [ ] セルフレビューはしましたか?
  - 誤字・脱字などの軽微なミスはここで減らしましょう
  - デバッグログ出力や、不要なコメントは残っていませんか?
  - 逆に不足しているコメントはありませんか?
- [ ] ユニットテストは作成しましたか?
  - 対応しない場合は、タスクチケットを作りましょう
  - 理由があってユニットテストを作成しない場合、PRのDescriptionに理由を記載してください
- [ ] PRのタイトルは変更点を端的に表していますか?
- [ ] 対応チケットはPRに紐づいていますか?
  - トレーサビリティ向上のために、チケット駆動開発を意識しましょう!

# 対応内容の概要

- 変更点やできるようになることを説明してください
- 端的に言うことが難しいなら、1つのPRサイズが大きすぎるかも

# レビュアーに対して伝えたいこと

- 申し送り事項や重点的に確認してほしいところを記載します

# 参考にしたサイトなど

- [サイト名](URL)
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647946/4da84506-1a2d-10dd-8ef9-719fdb5c125f.png" width="800">

実際にしばらく使ってみて、改善点があればまた反映しようと思います。

# 2023/02/27追記

マージする際にコミットメッセージを編集しないと、`git log`した際にとても見辛いですね。
いつも私は「チェックリスト」の部分は削除するようにしてます。
Auto Completeを設定する際に、コミットメッセージも編集するようにチームの意識づけが必要かなと思います。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647946/e5fc4cde-9656-966b-ca0f-8776f955fe96.png" width="700">

# 参照

https://qiita.com/07JP27/items/103f8acc9810d917ecbb

https://learn.microsoft.com/ja-jp/azure/devops/repos/git/pull-request-templates?view=azure-devops

https://dev.classmethod.jp/articles/pull-request-template/

https://techblog.jmdc.co.jp/entry/2022/09/09/114809
ElixirのStreamを使ってみる

[この記事](https://qiita.com/GeekMasahiro/items/260b823e8defa42ccc15)で遅延評価の例を書きましたが、Enum.all/2を使えばStreamを使わなくても書ける内容でした。
もうすこしStreamらしい例を考えてみました。

heavy_processを10回よびだす。
返り値がすべて正の数だった場合、trueを返す関数をStreamで作成してみます。

```Elixir
  def get_result() do
    Stream.repeatedly(fn  -> heavy_process() end)
    |> Stream.take(10)
    |> Stream.map(fn val -> val > 0 end)
    |> Enum.all?()
  end

  def heavy_process() do
    IO.puts("heavy!")
    hd Enum.take_random([-1, 10, 20, 30, 40], 1)
  end
```

```Elixir
iex> Tutorial012.get_result_stream2
heavy!
heavy!
false
```

うまく動きました。
ちなみにパイプの間でどんな値になっているのかinspectでみてみると

```Elixir
  def get_result_stream2() do
    Stream.repeatedly(fn  -> heavy_process2() end)
    |> IO.inspect(label: "a")
    |> Stream.take(10)
    |> IO.inspect(label: "b")
    |> Stream.map(fn val -> val > 0 end)
    |> IO.inspect(label: "c")
    |> Enum.all?()
    |> IO.inspect(label: "d")
  end
```

こんな感じになっています。実際の値が受け渡されているわけではありません。

```Elixir
iex(28)> Tutorial012.get_result_stream2
a: #Function<51.124013645/2 in Stream.repeatedly/1>
b: #Stream<[
  enum: #Function<51.124013645/2 in Stream.repeatedly/1>,
  funs: [#Function<56.124013645/1 in Stream.take_after_guards/2>]
]>
c: #Stream<[
  enum: #Function<51.124013645/2 in Stream.repeatedly/1>,
  funs: [#Function<56.124013645/1 in Stream.take_after_guards/2>,
   #Function<48.124013645/1 in Stream.map/2>]
]>
heavy!
heavy!
heavy!
heavy!
heavy!
heavy!
heavy!
heavy!
heavy!
heavy!
d: true
true
```
博士論文　再現実験　前日徹夜

博士論文に限らず、学術論文は、再現実験は基本的な作業である。　

実施主体（第一者）が行う場合もあれば、第二者（契約相手）が行う場合もあれば、第三者（公的機関）が行う場合もある。　

公的な研究費の場合には、公的機関が行う再現実験は、第二者によるものと分類することができる。　

# 博士論文 端末間経路選択のための片方向遅延差測定方式

端末間とは、End to End。今流行りのE2Eというやつです。　

経路選択とは、複数の経路がある場合のどちらを選ぶかを決めることができる場合を指します。　

測定するためには、通信を行わないと結果がすぐには得られません。能動測定になります。　

片方向遅延差とは、上りと下りの通信路の混雑状況や、帯域幅が違う場合に、それぞれの片方向の遅延に差があるかもしえrないことを前提としています。　

実際には、各端末等は時刻同期していない場合を想定します。　

遅延に差がある場合の時刻同期は、NTP(network time protocol)などの測定方法では必ずしも有効に同定できるとは限らず、かつ、時刻同期に必要な時間を消費する場合には、遅延よりも通信の利益を損なう可能性があります。　

# 品質 
次の指標は、必ずしもそれぞれが独立しているわけではありません。
それぞれを数値化して、横顔(profile)として眺めることにより、最終的な経路を選択しようという考え方です。　

そのため、どこかに、なにか不正なデータがあろうがなかろうがかまわないかもしれません。　
不正なデータ自体が、誤りなのかもしれないし、故障なのかもしれません。　

測定の誤差評価が大切かもしれません。

## Availability（可用性）　
時刻同期に時間がかかるのであれば、可用性を損なうかもしれません。 

## Bandwidth(帯域幅）　
PHS、GPSなどの無線通信では、登りと下りで、帯域幅が違うことがあります。無線通信では放送(broad cast)が機能的で、同時に大量の情報をあちこちにばらまくことが可能かもしれません。 

## Cost(費用)
どんなに便利に大量の情報を転送できたとしても、お金が莫大にかかるのでは実用的には嬉しくありません。　

## Delay(遅延)
なるべくすぐ欲しい。今欲しいというのが伝言です。遅いことは誰でもできるってことになるかもしれません。　

## Error(誤り）　
お知らせに間違いがあるかもしれない場合には、誤り訂正符号をつけて、再計算してから利用するかもしれません。時間（遅延）と電力（費用）がかかります。　

## Fault/Failure(故障)
ハードウェアなどが故障したら、送受信ができないことがあるかもしれません。　

## Geometric Distance（地理的距離）
地理的距離が遠ければ、選択可能な経路数は減るかもしれませんし、物理的な遅延は大きくなるかもしれません。　

## Hop Count（経路数）
たどりつくまでに、いくつの機器を経由するかで、遅延も費用も、誤りも故障も増えるかもしれません。

# 測定方法　
実験ではphsで測定しました。　

博士論文を提出する頃には、phsは低下傾向が見えてきていて、　
それ以外の方法で実験したかを聞かれると思い、　
論文審査会の前日に大学に泊まり込み、gpsで遅延測定をしました。　

gpsの機器は買ったばっかりで、その日初めてプログラムするものでした。　

１時間か、２時間か、経ったころ、測定ができはじめ、　
phsで測定し現象と同じ傾向のデータが取得できました。　

過去の実際の遅延測定では、北アメリカ大陸、南アメリカ大陸、ヨーロッパ、アジア、アフリカ大陸、オーストラリアの合計25カ国くらいと、日本との間の複数の経路での遅延測定をしていました。　

時代時代で、有線の方が速かったり、無線の方が速かったりいろいろあったために論文として整理しようとしてきたものです。　

今回は、第一者による再現実験の概要報告でした。　
35年間、公的試験研究機関での測定としては、ほとんど追試(再現試験のこと）ばかりでした。　
自分の実験の追試（再現試験）も、自分でできたことはよかったと思います。　

実験によっては、高価な機材が必要であったり、長い年月が必要な場合もあります。　
研究の第一歩は、追試(再現試験）からというのをお薦めしてみます。
Inertiaとは

laravelとvue.jsを使用してプロジェクトを作成した際に、使用していて便利だなと感じた
Inertiaについて振り返りも含めたメモとしてInertiaについて記載しています。

# Inertiaとは
Inertiaとは簡単に、サーバーサイド（バックエンド）とクライアントサイド（フロントエンド）をつなぐためのツールです。
今回でいうとクライアントサイド（Vue.js）とサーバーサイド（Laravel）に当たります。
公式でも下記のように記載しているように接着剤みたいなものだと考えるとわかりやすいです。

>Inertiaはフレームワークではありませんし、既存のサーバサイドやクライアントサイドのフレームワークと置き換わるものでもありません。むしろ、それらと一緒に動作するように設計されています。Inertiaは両者をつなぐ接着剤のようなものだと考えてください。

ちなみにInertiaの読み方は「イナーシャ」というみたいです。
思いっきりイナーティアって読んでいました、、

## Inertiaが対応しているもの
:::note info
クライアントサイド
React
Vue
Svelte
:::
:::note info
サーバーサイド
Laravel
Rails
:::

# Inertia使用するメリット
### これまでのLaravelと同じ感覚でVueやReactなどのモダンフロントを使用することができる
Laravelでは、コントローラーで処理を行い、これをビュー側（blade.php)に受け渡してページを表示したりします。
Vue.jsを使う場合にも、基本的には、同じような手順で処理を行えます。ただ、`Webアプリ全体で使える ルート設定が使えなかったり`と色々な不便があります。
Inertiaを使えば、Vue側でも、laravelのルート設定を使ったり、ヘッダを組み込んだり、ページネーションを簡単に搭載したりできます。


# Inertia使用した実装
## ルーティング
```routes/web.php
Route::resource('items', ItemController::class)
    ->middleware(['auth', 'verified']);
```

## コントローラー
```App\Http\Controllers\ItemController.php
<?php
namespace App\Http\Controllers;

use App\Http\Requests\StoreItemRequest;
use App\Http\Requests\UpdateItemRequest;
use App\Models\Item;
use Inertia\Inertia;

class ItemController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $items = Item::select('id', 'name', 'price', 'is_selling')->get();

        //Inertia::renderという関数は第一引数にコンポーネント、第二引数にプロパティ配列を渡します。
        //Inertia::renderを呼び出す事で、どのコンポーネントに対してどの変数をセットするかをここできめ、vueにも反映させることができます。
        return Inertia::render('Items/Index', [
            'items' => $items
        ]);
    }
```

## ビュー側
```resources/js/Pages/Items/index.vue
<script setup>
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout.vue';
import { Head, Link } from '@inertiajs/vue3';

defineProps({
    items: Array
})
</script>

<template>
~~省略〜〜
<tbody>
   <tr v-for="item in items" :key="item.id">
       <td class="px-4 py-3">{{ item.id }}</td>
       <td class="px-4 py-3">{{ item.name }}</td>
       <td class="px-4 py-3">{{ item.price }}</td>
       <td class="px-4 py-3">{{ item.is_selling }}</td>
   </tr>
</tbody>
~~省略〜〜
</template>
```
【Solidity】(tips)externalアクセス制限のついた関数を同contract内から呼び出す方法

出会う機会は多くはないと思いますが、externalアクセス制限のついた関数を同contract内から呼び出す方法を下記します。

RemixIDEを使用して、externalアクセス制限のついた同contract内で呼び出そうとするとエラーが起きます。
[コード]
![スクリーンショット 2023-02-14 20.49.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1621669/8795c955-e470-8d6e-142b-4fc09551a39b.png)
[エラー内容]
![スクリーンショット 2023-02-14 20.50.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1621669/ef79864d-c4c7-64b2-4570-0645fed88a11.png)

ここで、呼び出す関数の前にthisをつけると、エラーがなくなり関数を呼び出すことができます。
![スクリーンショット 2023-02-14 20.52.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1621669/94b71b1c-cb25-ed19-b989-5a1887f73fb2.png)

※参考
externalがコントラクト内から呼び出せてしまうとpublicとの違いがないように思いますが、
下記記事によるとガス代が異なるみたいです。
https://ethereum.stackexchange.com/questions/19380/external-vs-public-best-practices
ShellShoccar-Jpn/Parsrsのparsrj.shは構文解析が雑！(雑記事)

`Version : 2022-02-06 01:34:22 JST`

# 結論
- `{0:A}`とか`{A:B}`とか`[A,B]`とか通った。
  - objectのキーは本来引用符で囲まれたstringでなければいけないのだが囲まなくても通る。
  - valueこそ雑。objectやarrayじゃなければstringかstringじゃないもの(本来はnumberか`true`か`false`か`null`じゃないといけない)、としか考えていない。
  - arrayの要素も同様。
- 開発やメンテが楽というメリットと引き換えにこれじゃあ脆弱。
- 入力されるJSONが正しい構文であるものだと予期されたツール。
- 厳密なことをやるのは我々の宿題なんだろうな。
- ちなみにmakrj.shはtrue、false、nullとかを認識。この他、stringではない組み込み型に変換し得るものは組み込み型に変換。ただし引用符で囲ったstringは少し雑。元々が正しい構文であるという前提で。
- この記事書くの飽きた。
備忘録(eclipseでMySQL連携の方法)

## EclipseでJavaとMySQLの連携にとても苦戦したので、その戦いの記録

初心者なので必要のないデータやチンプンカンプンな表記があるかもですが、ご指摘いただけると嬉しいです。

## バージョン
OS:Windows 10 Home
Eclipse:2021-06 (4.20.0)
MySQL:Ver 8.0.32 for Win64 on x86_64 (MySQL Community Server - GPL)
JDBC:mysql-connector-j-8.0.31.jar
(2023/2/14現在)

## 今回の挑戦
Javaでデータベース連携をしたシステム開発に挑戦をしたい

## どこまでできたか
eclipseでMySQLへの接続まで

#### 参考にしたサイト
・[MySQLへのダウンロード時に参照](https://johobase.com/mysql-download-install-initial-setting/)
・[MySQLの立ち上げ方について参照](https://prog-8.com/docs/mysql-env-win)
・[恐らくバージョンの違いでこの記事だけでは上手く起動できず下記サイトも参照](https://beiyan-tool.info/?p=471)
・[このサイトでJDBCというものが必要だと知った](https://style.potepan.com/articles/26613.html)

##### Mavenプロジェクトというのが良くわからず、途中で断念。
・[DBViewerのインストール方法について参照](https://dotnsf.blog.jp/archives/1010597771.html)
・[JavaでMySQLに接続してデータ操作をする](https://www.task-notes.com/entry/20150414/1428980400)

このページの中で操作が上手くできず
Exception in thread "main" java.lang.Error: Unresolved compilation problem: at a.MySQLConnector.main(MySQLConnector.java:4)
というエラーが出てしまう。

##### 調べていくとJDBCのドライバ指定を変えないといけない事を知る。
・[解決方法](https://qiita.com/SadanoMarkwell/items/02c85224be7d5de5fbf3)

それと同時にアクセスするMySQLのステータスも変更



## 今回わからなかったこと
Mavenプロジェクトという存在
pom.xmlファイルどこにあるの
SQLのdeleteを死ぬほど楽にする関数を作った

## はじめに
省略。


## コード
```php
<?php
function deleteRecord($host, $db, $user, $password, $table, $id) {
    try {
        $dsn = "mysql:host=$host;dbname=$db";
        $pdo = new PDO($dsn, $user, $password);

        $stmt = $pdo->prepare("DELETE FROM $table WHERE id = ?");
        $stmt->execute([$id]);

        if ($stmt->rowCount() == 1) {
            echo "Record deleted successfully";
        } else {
            echo "Record not found";
        }
    } catch (PDOException $e) {
        echo "Error deleting record: " . $e->getMessage();
    }

    $pdo = null;
}
?>

```

## 使い方
```php
deleteRecord($host, $db, $user, $password, $table, $id);
```
NVIDIA Broadcast のカメラが映らない問題について

## はじめに
Discord や Zoom、OBS などのソフトウェアで NVIDIA Broadcast のカメラが映らない問題がありました。ソフトウェア上でカメラとして `Camera (NVIDIA Broadcast)`を選択しても、何も映らなかったり、ソフトウェア自体がフリーズしたり、クラッシュしたりすることがありました。色々探し回った結果、海外の reddit に解決策があったのでまとめておきます。結論としては、AVerMedia 製のキャプチャソフトである RECentral のサービスが悪さをしているみたいです。 RECentral のサービスを停止すると Broadcast のカメラが正しく映りました。原因はよく分かりませんが、カメラデバイスの取得などで競合しているのではないかと思います。

## NVIDIA Broadcast とは
公式のFAQを引用すると

>NVIDIA Broadcast は、標準的なウェブカメラとマイクを AI の力で優れたスマートデバイスにアップグレードし、あらゆる空間をホームスタジオに変えるアプリです。マイクの雑音や部屋の反響音の除去、バーチャル背景、ウェブカメラ自動フレーム、ビデオのノイズ除去といった AI 機能によって、ライブストリームのビデオと音声の品質が向上します。NVIDIA RTX GPU には専用 AI プロセッサである Tensor コアが搭載されており、その AI ネットワークにより高品質のエフェクトをリアルタイムで実行できます。

とあるように、グラフィックボードを用いた AI 機能によって、通話品質（ビデオ通話を含む）を向上させることができるソフトウェアになっています。例えば、キーボードのタイピング音やマウスのクリック音、ファンの音などの除去が可能になっています。他にも様々な機能があり、NVIDIA Broadcast ver1.4.0 で使用できる機能は以下の通りです。
* ノイズ除去：マイクに入ってくる背景雑音を除去する機能
* ルームエコー除去：反響音（ハウリング）を除去する機能
* オートフレーミング：頭の動きを追跡し、自動的に中央に顔が来るようにクロップしたり、ズームしたりする機能
* ビデオノイズ除去（ベータ版）：背景の除去、置換、ぼかしといった機能
* アイコンタクト（ベータ版）：目線をカメラへ自動補正してくれる機能
* ビネット：画面の中心から距離に応じて明るさや彩度を低下させる機能

注意点として、GTX 以前のグラボには対応しておらず、RTX 以上のグラボでしか使えません。

>NVIDIA Broadcast にはどのようなシステム要件がありますか?
GPU: NVIDIA GeForce RTX 2060、Quadro RTX 3000、TITAN RTX 以降

## 対応策
対応策として、1つ目に、RECentral は使わない方法と（OBS などを使用し、そこから映像キャプチャデバイス（キャプボ）を選択する）、2つ目に RECentral を使い終わった後は RECentral のサービスをその都度、停止することが挙げられます。2つ目について説明していきましょう。以下、サービスの停止手順になります。
1. [Windows] + [R] キーを同時押しし、"ファイル名を実行" を開く。
2. 名前に "services.msc" と入力し [OK] ボタンをクリック。
3. サービス一覧が開いたら、"RECentralService" を選択し、右クリックから [プロパティ] を開く。
4. プロパティを開いたら [停止] ボタンをクリックし、サービスを停止する
5. "スタートアップの種類" を "自動" から "手動" へ変更

![スクリーンショット 2023-02-08 22.53.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/85b411f5-a5aa-60d1-b029-3c67c6bb44b0.png)
![スクリーンショット 2023-02-09 16.27.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/2db109b1-e515-5ec3-6c51-4143769910fd.png)

以上で RECentral のサービスが停止され、正常に Broadcast のカメラが映るはずです（これでも映らない場合は他の不具合でしょう）。ただ、毎回この操作を行うのは面倒です。そこで、RECentral のサービスを停止してくれるバッチファイルを作成し、それを実行する形にしました。

```bat:ReCentral Kill.bat
taskkill /F /T /IM RECentralService.exe
```

たったの一行です。しかし、このバッチファイルを実行しても管理者権限がないため、サービスの停止ができません。右クリックから [管理者として実行] を選択すればよいのですが、これも面倒くさく、パッと簡単に済ましたいです。そこで、ショートカットを作成し管理者として実行させるようにします。以下にその手順を示します。

1. バッチファイルのショートカットを作成（どこに作成してもいいですが、私はデスクトップに置いてます）
2. 作成したショートカットを選択し、右クリックから [プロパティ] を開く
3. 出てきたプロパティから右下の [詳細設定] をクリック
4. 出てきた詳細プロパティの [管理者として実行] にチェックをつけて [OK] ボタンをクリック
5. 最後に [OK] ボタンをクリックし設定を保存

![スクリーンショット 2023-02-09 17.01.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/b5ee4f62-6ab9-99fe-4404-7f12163e522c.png)![スクリーンショット 2023-02-09 17.05.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1115291/61b7602d-3cf3-5fa7-fe88-f9ae787b89b3.png)

以上で、ショートカットを開くだけで RECentral のサービスを停止してくれるようになりました（バッチファイルに [管理者として実行] オプションがないのは何でなんでしょう）。


## 最後に
日頃から Broadcast のボイスエフェクトにはかなりお世話になっていましたが、特にビデオエフェクトに関しては興味がなかったのもあり、Broadcast のカメラが映らなくても放置していました（調べても出てこなかったのもありましたが…）。ふと、やっぱり Broadcast のカメラ使いたいなと思い、調べてみたら reddit にあったので解決してよかったです。

## 参考
Nvidia Broadcast (Camera) not working as a source in OBS Studio（"リンク先に投稿禁止の内容が含まれています"と表示されためリンク張ってません）
SQLでのinsertを死ぬほど楽にする関数を作った。

## INSERT、めんどくさいですよね?
あのプリペアドステートメントとか、苦しくて苦しくてやっていられるもんじゃあありません。
そこで、SQLでinsertを死ぬほど楽にする関数を作りました。
一応、セキュリティーを考えているつもりですが、何か問題点があったら教えてください。

## コード
```php
<?php
function insertData($db_name, $db_user, $db_pass, $db_tablename, $insert_data) {
    try {
        $conn = new PDO("mysql:host=localhost;dbname=$db_name", $db_user, $db_pass);
        $conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

        $keys = array_keys($insert_data);
        $values = array_values($insert_data);
        $num_values = count($values);
        
        $placeholders = implode(',', array_fill(0, $num_values, '?'));

        $stmt = $conn->prepare("INSERT INTO $db_tablename (" . implode(',', $keys) . ") VALUES ($placeholders)");
        $stmt->execute($values);

        $conn = null;
        return true;
    } catch(PDOException $e) {
        return false;
    }
}
?>
```

## デリケート
以前、この種の関数をqiitaに投稿した際、データベースパスワードなどのセキュアな情報をいちいち持ち運ぶのはどうか、という意見をいただいたことがあります。
その際は、pdoオブジェクトを自分で作ってしまい、それを組み込むように適宜変更してください。
（丸投げ）

## 終わりに
問題点・アドバイスがあれば、ぜひ教えていただけると助かります。
C# テキストファイルを最終行から行単位で読み出す

下の関連記事を書いた後、最終行から先頭に向けて一行ずつ読み出す方が使い易いんじゃないかと思い作ってみました。

行区切りが空白文字でないと期待どおり動かないと思います。


- 本記事のすべての情報は利用者の責任において使ってください。
- 本記事の情報が正しいこと、誤っていること、実行効率を検証していません。
- 動作することも、動作しないことも保証しません。

誤りや改善案はご指摘いただければ感謝します。が、修正できない場合があることをご了承ください。

```csharp:Program.cs
static void Main(string[] _)
{
    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

    using FileStream stream = new(
        @"C:\temp\新規 テキスト ドキュメント.txt",
        FileMode.Open,
        FileAccess.Read
        );

    var enc = Encoding.GetEncoding("shift_jis");

    foreach (var r in FileStreamExtension.Tail(stream, enc))
    {
        Console.WriteLine(r);
    }
}
```

```csharp:FileStreamExtension
public static class FileStreamExtension
{
    public static IEnumerable<string> Tail(
        this FileStream stream,
        Encoding encoding,
        string delimiter = "\r\n"
        )
    {
        if (string.IsNullOrEmpty(delimiter)) throw new ArgumentException($"empty delimiter");
        if (stream.Length <= 0) yield break;

        Stack<byte> stack = new();

        stream.Position = stream.Length - 1;

        while (stream.Position >= 1)
        {
            int buffer = stream.ReadByte();

            stack.Push((byte)buffer);

            if (buffer == delimiter[0])
            {
                // 読みだした文字が指定されたデリミタの先頭文字と同じ場合は検査する

                var s = encoding.GetString(stack.ToArray());
                if (s.IndexOf(delimiter, 0) == 0)
                {
                    ForwardPosition();
                    stack.Clear();
                    yield return s == delimiter ? "" : s.TrimStart();
                }
            }
            else
            {
                ForwardPosition();
            }
        }

        if (stream.Position == 0)
        {
            stack.Push((byte)stream.ReadByte());
            var s = encoding.GetString(stack.ToArray());
            yield return s == delimiter ? "" : s.TrimStart();
        }

        void ForwardPosition() => stream.Position -= 2;
    }
}
```

## 関連記事
https://qiita.com/monta31538/items/0f217aa43ce2c29b99f7
Databricksインテグレーションの概要

[Databricks integrations overview \| Databricks on AWS](https://docs.databricks.com/getting-started/connect/index.html) [2022/10/5時点]の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

本書では、Databricksで使用できるデータソース、BIツール、開発ツールからなる大規模なラインナップにどのように接続するのかに関して情報を提供します。

# Partner Connect

Partner Connectは、お使いのDatabricksクラスターやSQLウェアハウスとよりクイックかつ簡単にインテグレーションするための検証済みソリューションに対するユーザーインタフェースです。

詳細は[Databricks Partner Connectガイド](https://qiita.com/taka_yayoi/items/401f3d785f8262d53ea2)をご覧ください。

# データソース

DatabricksはCSV、[Delta Lake](https://qiita.com/taka_yayoi/items/685a6a142548d43f7782)、JSON、Parquet、XMLなどの様々なデータフォーマットや、Amazon S3、Google BigQueryやCloud Storage、Snowflakeなどのデータストレージプロバイダーのデータを読み書きすることができます。

接続手順を含む包括的なリストに関しては、[データ取り込みツール](https://docs.databricks.com/partners/partners.html#ingest)や[外部データの取り扱い](https://qiita.com/taka_yayoi/items/09348912a92a24441c40)をご覧ください。

# BIツール

Databricksでは、Power BI、Tableauなどを含むお使いのBIツールとのインテグレーションを検証しており、多くの場合、ローコードやノーコードの体験でDatabricksクラスターやSQLウェアハウスを通じてデータを操作することができます。

接続手順を含む包括的なリストに関しては、[BIと可視化](https://docs.databricks.com/partners/partners.html#bi)をご覧ください。

# その他のETLツール

すべてのタイプの[データソース](#データソース)に対するアクセスに加えて、Databricksではdbt、Prophecy、Azure Data FactoryのようなETL/ELTツール、Airflowのようなデータパイプラインオーケストレーションツール、DataGrip、DBeaver、SQL Workbench/JのようなSQLデータベースツールとのインテグレーションを提供しています。

接続手順に関しては、以下をご覧ください。

- **ETLツール:** [Data preparation and transformation](https://docs.databricks.com/partners/partners.html#prep)
- **Airflow:** [Orchestrate Databricks jobs with Apache Airflow](https://docs.databricks.com/workflows/jobs/how-to-use-airflow-with-jobs.html)
- **SQLデータベースツール:** [Use a SQL database tool](https://docs.databricks.com/dev-tools/index-sql.html)

# IDEとその他の開発ツール

Databricksでは、DataGrip、IntelliJ、PyCharm、Visual Studio Codeのような開発ツールをサポートしており、コードを記述することでDatabricks[クラスター](https://qiita.com/taka_yayoi/items/c5d99cd77fe4bfcf69f0)や[SQLウェアハウス](https://qiita.com/taka_yayoi/items/23d7789198c2dcd66381)を通じてデータを操作することができます。

# Git

Databricks Reposは、お使いのGitプロバイダーとのリポジトリレベルのインテグレーションを提供するので、Databricksノートブックでコードを開発し、リモートのGitリポジトリと同期することができます。[Databricks ReposによるGit連携](https://qiita.com/taka_yayoi/items/b89f199ff0d3a4c16140)をご覧ください。

### Databricks 無料トライアル

[Databricks 無料トライアル](https://databricks.com/jp/try-databricks)
App Store ConnectでApp共有シークレットを入手する方法

# この記事を書くに至った理由
方法を調べたところ、情報が古く、UIが変更されていたため取得が困難であったから。

# 結論

App共有シークレットを取得したいアプリページに移動

「App情報」

「App用共有シークレット」

「管理」
Google Play Consoleで収益化用のBase64エンコードのRSA公開鍵を取得する方法

# この記事を書くに至った理由

Google Play Consoleで収益化用のBase64エンコードのRSA公開鍵を取得する方法を調べたところ、情報が古く、UIが変更されていたため取得が困難であったから。

# 結論

2023年2月14日で有効な方法です。

Google Play Consoleにログイン

収益化用のBase64エンコードのRSA公開鍵を取得したいアプリに移動。

左側のバーにある「収益化のセットアップ」をクリック

「ライセンス」というところに求めているものがあります。

「アプリバイナリに追加する Base64 エンコードの RSA 公開鍵です。スペースはすべて削除してください。」と書かれているはずです。
【CloudWatch】comparison_operator

## 概要
AWSのCloudWatch metricsをterraformで書く際、指定する`comparison_operator`についてまとめる

### comparison_operatorとは
指定された統計としきい値を比較するときの算術演算

|設定値|意味|使える要件|
|-|-|-|
|GreaterThanOrEqualToThreshold|<=|通常使用|
|GreaterThanThreshold|<|通常使用|
|LessThanThreshold|>|通常使用|
|LessThanOrEqualToThreshold|>=|通常使用|
|LessThanLowerOrGreaterThanUpperThreshold|>= or <=|Anomaly detectionを使う時に使える|
|LessThanLowerThreshold|>|Anomaly detectionを使う時に使える|
|GreaterThanUpperThreshold|<|Anomaly detectionを使う時に使える|

## 資料
[[公式] terraform conparison_operator](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_metric_alarm#comparison_operator)
オフショア開発モデルサービス

オフショア開発会社は、会社の要件を満たすために特定のサービスも提供します。 サービスモデルは、顧客企業がオフショアチームから必要とする作業の種類に基づいて分類されます。

![872548-Staff-Augmentation-Alton-BLOG.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92345/a7cd7e7a-6b71-57e4-08db-0a88257aebd1.png)

<h2> オフショア開発とは？

オフショア開発（offshore development）とは、ソフトウェアやWebシステム、アプリケーションの開発を海外企業に委託することをいいます。企画や設計、納品は自社で行い、開発や実装を海外企業へ委託するなど分業化されることが多いです。

オフショア開発については以下の記事で詳しく紹介していますので、オフショア開発について気になっている方はこちらの記事もぜひご参照ください。

https://miichisoft.com/easy-to-understand-offshoredevelopment-from-consideration-to-introduction/


オフショア開発の目的は主に3つです。

(1) コスト削減
(2) リソース確保
(3) 優秀なエンジニアの確保

<h2> オフショア開発モデルサービス

<h3> スタッフ増強

スタッフ増強は、派遣社員を使用して社内の短期的な職務に就くアウトソーシングモデルの一種です。

オフショアスタッフ増強は、スケールアップの必要性、開発能力の向上、イノベーションの迅速な採用など、ビジネスの多くの課題を解決できます。ただし、スタッフ強化サービスの恩恵を受け、熟練したソフトウェア開発者を雇うには、多くの要因を考慮する必要があります。

アウトソーシングチームに最適な場所を定義し、ベンダーのポートフォリオを調べて、その評判を知ることが重要です。さらに、ITスタッフ増強会社は、成長のニーズを満たし、最も希少な人材を引き付けるのに十分な能力を備えている必要があります。

<h3> プロジェクトベース

このオフショア開発モデルは、コスト削減と時間短縮を求める企業に適しています。これは、中程度から低い複雑さの 1 回限りの、不規則な、およびパイロット プロジェクトを行う企業に適しています。タイムフレームが定義されており、開発プロセス中に変更が加えられない正確に定義された要件を持つプロジェクトに適しています。プロジェクトに変化が生じる可能性があると思われる場合は、他のモデルを使用することをお勧めします。

<h3> オフショア開発センター

このアウトソーシングモデルは、主に作業量の多い長期プロジェクト向けです。開発コストを削減しながら製品の品質を維持することを目指す企業に最適です。 この場合、彼らは熟練した専門家の専任チームを確保して、1つのプロジェクトに従事させ、独立して管理します。

Miichisoftでは、プロジェクトの詳細に応じて、経験豊富なチームまたは個々の従業員をプロジェクトに提供できます。アイデア段階から提案することができますので、気軽にご連絡ください。

Website: https://miichisoft.com/
[Java] デザインパターン - Adapter

## 目的
以下の書籍を読んで、デザインパターンについて学習したため、その備忘録として記事を作成する。

* Java言語で学ぶデザインパターン入門 第3版 (著: 結城浩)

## Adapterパターンとは
* 既に提供されているものと必要なものの間のずれを埋める変換器の役割を果たす
  * （例）直流xxボルトを交流xxボルトに変換するACアダプタ
* 既存のクラスには全く手を加えずに、目的のインターフェース（API）に合わせようとするもの

２種類のAdapterパターンがある。
| 分類 | 具体的な使用方法 |
|:-:|:-:|
| クラス | 継承 |
| インスタンス | 委譲 |

委譲については以下の記事が詳しい。

https://qiita.com/jesus_isao/items/4b6b7846ccf5eb46b1bc

### なぜこのデザインパターンを使うのか
* クラスを新規作成するよりも、テスト済みの既存のクラスを再利用した方がバグ発生時の調査範囲を特定しやすいため。
* 既存のクラスはテスト済みなので、バグが発生した時は、再利用先のクラス（Adapter）側を調べれば良い。

## 実際に使ってみる

練習問題2-2を解く。（委譲パターンで解く）

```mermaid
classDiagram
  
Target <|-- Adapter
Adaptee <--o Adapter
class Target {
  +targetMethod1()*
  +targetMethod2()*
}
class Adapter {
  -adaptee
  +targetMethod1()
  +targetMethod2()
}
class Adaptee {
  +methodA()
  +methodB()
  +methodC()
}
```

* Target: `FileIO`
* Adapter: `FileProperties`
* Adaptee: `Properties` （`java.util.Properties`）

### サンプルコード

* Target

```java:FileIO.java
package Adapter;

import java.io.IOException;

public interface FileIO {
  public void readFromFile(String fileName) throws IOException;
  public void writeToFile(String fileName) throws IOException;
  public void setValue(String key, String value);
  public String getValue(String key);
}
```

* Adapter

```java:FileProperties.java
package Adapter;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class FileProperties implements FileIO {
  Properties p = new Properties();
  @Override
  public void readFromFile(String fileName) throws IOException {
    p.load(new FileReader(fileName));
  }

  @Override
  public void writeToFile(String fileName) throws IOException {
    p.store(new FileWriter(fileName), "written by FileProperties");
  }

  @Override
  public void setValue(String key, String value) {
    p.setProperty(key, value);
  }

  @Override
  public String getValue(String key) {
    return p.getProperty(key, "");
  }
}

```

* 実行用

```java:Main.java
package Adapter;

import java.io.IOException;

public class Main {
  public static void main(String[] args) {
    FileIO f = new FileProperties();
    try {
      f.readFromFile("Adapter/file.txt");
      f.setValue("width", "1024");
      f.setValue("height", "512");
      f.setValue("depth", "32");
      f.writeToFile("newfile.txt");;
      System.out.println("newfile.txt is created");
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
```

## メリット
* AdapterではAdapteeのメソッドをまるっと利用（委譲）しているだけなので、エラーが発生した場合はAdapter側のクラスが問題であることがわかる。
  * => エラー箇所の切り分けがしやすい
* 実装を修正したい場合、Adapterクラスの修正だけで済み、Adaptee側のクラスを意識する必要がない。

## 感想
* 練習問題で解いたように、Adaptee側にJavaの標準ライブラリを使った場合は、バグの範囲が絞りやすいので今後使っていきたい。
* 委譲のおかげで「以上」の変換がしにくくなった。。

以上
SwiftUIのViewがなぜか更新されないときは `id(_:)` を付与すると解決するかもしれない

記事の内容はこちらを参照してください。
https://mishimay.github.io/d8b0f0821c2fa559f62e/
<!--

以下のような、画像を取得するObservableObjectを `@StateObject` で保持している画像表示のViewを作成することがままあると思います。

```swift
import SwiftUI

struct ImageView: View {
    @StateObject var imageProvider = ImageProvider()
    let url: URL

    var body: some View {
        ZStack {
            if let image = imageProvider.image {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFit()
            }
        }
        .frame(height: 300)
        .onAppear {
            imageProvider.fetchImage(url: url)
        }
    }
}

class ImageProvider: ObservableObject {
    @Published var image: UIImage?

    func fetchImage(url: URL) {
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let data = data, let image = UIImage(data: data) {
                DispatchQueue.main.async {
                    self.image = image
                }
            }
        }.resume()
    }
}
```

画像を一度だけ取得する場合は問題なのですが、以下のように任意の契機で別の画像を取得したい場合に期待通りに画像が更新されません。

```swift
import SwiftUI

struct ContentView: View {
    @State var pictureNumber = 0

    var body: some View {
        ImageView(url: URL(string: "https://picsum.photos/id/\(pictureNumber)/500/500")!)

        Button {
            pictureNumber += 1
        } label: {
            Text("Change")
        }
    }
}
```

画像取得処理が確実に呼ばれるようにImageViewのイニシャライザー内に `imageProvider.fetchImage(url: url)` を置いても特に改善しません。

```swift
struct ImageView: View {
    @StateObject var imageProvider: ImageProvider
    let url: URL

    init(url: URL) {
        self.url = url
        let imageProvider = ImageProvider()
        _imageProvider = StateObject(wrappedValue: imageProvider)
        imageProvider.fetchImage(url: url)
    }
...
```

結局以下のようにImageViewに `.id()` を付与することで解決しました。
idが更新されると対象のViewの状態がリセットされるとのことです。

```swift
struct ContentView: View {
...
        ImageView(url: URL(string: "https://picsum.photos/id/\(pictureNumber)/500/500")!)
            .id(pictureNumber)
...
```

## 参考
https://developer.apple.com/documentation/swiftui/view/id(_:)

https://swiftui-lab.com/swiftui-id/

## 環境
Xcode 14.1.0
Swift 5.7.1


-->
[OutSystems]LogMessageで何桁まで出力できるか

OutSystems標準のログ出力部品LogMessage Action (Systemにある）で出力したメッセージはDBに格納される。
DBということは、当然ながら長さに制限があるわけなので、それを確認したい。

# 確認環境
Personal Environment(Version 11.18.1 (Build 37884))
Service Studio(Version 11.53.38)

# サンプル
Forgeコンポーネント: [HousesoftSampleReactive](https://www.outsystems.com/forge/component-overview/7086/housesoftsamplereactive)のV1.0.14。

MainFlow > Log Screen

# Entity
標準ログは週別に別のテーブル（最大で10週分）に出力される。
標準で含まれているExtension PlatforLogsには、当週と前週のテーブルをポイントするビューに対するEntity (外部DB)がある。
当週のGeneralLog Entity(LogMessageで出力されるGeneralログを格納する）のMessage属性の型を確認すると、「NVarChar(2000)」。
つまりバイト数ではなく、2000文字格納できることになる。
なお、Personal EnvironmentはSQL Serverを使用しているため、この定義となっている。オンプレミス環境で別のRDBを使っていれば異なってくるかもしれない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/1d2bdfe0-9863-18cf-cee6-4e4c7aca5b84.png)

# LogMessageの呼び出し方法
(System)にあるLogMessage Action (ClientとServer双方のActionがある）を呼び出すだけ。
Input Parameterの指定は以下の通り。
- Message：ログメッセージ
- ModuleName：通常、Built-In Functionの「GetEntryEspaeName()」を設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/a3f4fb38-4721-1997-4a7e-8b90a42d876f.png)

# 出力の確認方法
PlatformLogsを参照してプログラムで確認することもできるが、通常はService Centerを使う。
Service Center > Monitoring > Generalページで確認できる。
結果一覧上で、Input Parameterで指定したMessageは同名の列に、ModuleNameで指定した値はSource列に表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/288e296d-afb4-7a5c-b372-4007ccee9cde.png)

# 動作確認
桁数は、OutSystemsのBuilt-in FunctionにあるLength、バイト数はJavaScript要素で以下のコードを使って確認している。

``` js
(new TextEncoder().encode(メッセージ)).length
```

## 英数字2000文字：出力できる
「abcde12345」という文字列を200回繰り返した文字列を出力してみると、そのままService Centerのログに表示された。

このとき
- 桁数：2000
- バイト数：2000

## ひらがな2000文字：出力できる
「あいうえおかきくけこ」という文字列を200回繰り返した文字列を出力してみると、そのままService Centerのログに表示された。

このとき
- 桁数：2000
- バイト数：6000

## 英数字2010文字：2000文字で切り捨てられる
「abcde12345」という文字列を201回繰り返した文字列を出力してみると、2000文字目までだけがService Centerのログに表示された。

このとき
- 桁数：2010
- バイト数：2010

## ひらがな2010文字：2000文字で切り捨てられる
「あいうえおかきくけこ」という文字列を201回繰り返した文字列を出力してみると、2000文字目までだけがService Centerのログに表示された。

このとき
- 桁数：2010
- バイト数：6030

## 特殊な文字は、素直に1文字と判定されないことがあるので注意
↓のページは、SQL Serverの場合だが、この記事と同じ問題がOutSystemsでも起きるようだ（テストに使うPEのバックエンドがSQL Serverだからかもしれない）。
https://note.gosyujin.com/2019/06/13/sqlserver-varchar-nvarchar/

例えば、「𠌂𪟟𫝃𫭟丸」という文字列を400回繰り返した文字列を出力してみると（つまり、5*400=2000桁）、960文字目までだけがService Centerのログに表示された。

このとき
- 桁数：2000
- バイト数：8000

# まとめ
(System)LogMessageで出力するメッセージは2000桁が上限。
英数字／日本語含めて2000文字までという意味で、これを上回る分はログ出力されずに捨てられる。
ただし、特殊な文字の場合は、1文字が2文字以上としてカウントされてしまうこともあるため、そうした文字を含む可能性があるなら、メッセージには十分余裕を持たせておく。
VSCodeからリモートアクセスできない場合の対処例

突然VSCodeからサーバーへリモートアクセスできなくなったのですが、ターミナルからサーバーへ接続し、VSCode-serverの接続情報を削除することで解決しました。入力コードは下記のとおりです。

```
rm -rf .vscode-server
```
参考: https://github.com/microsoft/vscode-remote-release/issues/312
2次元配列の列ごとの和の配列

アウトプットの練習も兼ねてメモ

プログラミングを一から学ぶつもりで色々やっています。趣味って言えるくらい楽しめるようになりたいです。
初歩的なことばかりですが温かい目でご覧ください。

# map関数を用いて
~~~
l = [[1, 0, 1],
     [1, 1, 1],
     [0, 0, 1]]

#各列の合計
columns_sum = list(map(sum, zip(*l)))
print(columns_sum)

#出力
#[2, 1, 3]
~~~

# リスト内包表記を用いて
~~~
l = [[1, 0, 1],
     [1, 1, 1],
     [0, 0, 1]]

#各列の合計
columns_sum = [sum(i) for i in zip(*l)]
print(columns_sum)

#出力
#[2, 1, 3]
~~~

書きやすそうなのは後者かも。
PHP8.1以降、opensslでerror:0308010C:digital envelope routines::unsupportedと言われた場合の原因と対策

# 発端

- PHP8.0以下からPHP8.1以上へバージョンアップした際に`openssl_encrypt`に失敗するようになってしまった
- 実際は `openssl_encrypt` が `false` を返してくるが、特にそれ以上に例外やエラーを直接吐いたりはしない
- `openssl_error_string` で失敗の原因を調べると `error:0308010C:digital envelope routines::unsupported` と返ってくる
```php:test.php
<?php
$enc = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);

if ($enc === false) {
  echo openssl_error_string(); // => error:0308010C:digital envelope routines::unsupported
}
echo $enc;
```
- しかし、全く心当たりがない

# 原因と対策

https://www.php.net/manual/ja/migration81.other-changes.php

> OpenSSL 3.0 がサポートされるようになりました。 多くの暗号が(レガシーなプロバイダの一部となっているため) デフォルトで有効ではなくなり、 引数の検証 (たとえば鍵の最小サイズ) が厳しくなっています。 

- PHP8.1でopenssl 3.0がサポートされるようになった影響
- openssl 3.0でlegacy扱いになった暗号アルゴリズムを利用していた場合に起こりえる問題だった
  - 利用している暗号アルゴリズムがサポートされているかどうかは`var_dump(in_array($method, openssl_get_cipher_methods()));` で確認できる

## 対策A: 暗号アルゴリズムの変更
- 暗号化/復号の際の暗号アルゴリズムの指定をlegacyではないものに変更する
  - `aes-256-xxx` など
- それまでに暗号化された文字列との互換性は当然なくなるので注意
  - ユーザ環境のアプリやブラウザなどに暗号化された文字列を既に保持しているような場合はかなり注意が必要
  - 場合によっては、旧暗号アルゴリズムと新暗号アルゴリズムをある程度の期間併存させつつ、徐々に移行させていくような対応が必要になる

## 対策B: legacyな暗号アルゴリズムをPHP8.1以降でも利用できるようにする
- openssl 3.0でlegacy扱いになった暗号アルゴリズムはデフォルトでは無効になっているものの、設定で有効にできる
- /etc/ssl/openssl.cnfを以下のように変更する

```/etc/ssl/openssl.cnf (変更前抜粋)
[provider_sect]
default = default_sect

[default_sect]
activate = 1
```

```/etc/ssl/openssl.cnf (変更後抜粋)
[provider_sect]
default = default_sect
legacy = legacy_sect

[default_sect]
activate = 1

[legacy_sect]
activate = 1
```

- php環境をdockerイメージで管理している場合、Dockerfileに以下のようなsedを追加すればOK

```Dockerfile:Dockerfile
RUN sed -ri -e '/^default\s*=\s*default_sect/a legacy = legacy_sect' \
    -e '/^\[default_sect\]/a activate = 1' \
    -e '/^\[default_sect\]/a \[legacy_sect\]' \
    -e '/^\[default_sect\]/a activate = 1' \
    /etc/ssl/openssl.cnf
```

# 参考

https://sehermitage.web.fc2.com/devel/openssl_v30.html