OmniAICreator/Qiita-1.07M huggingface dataset:
Windowsでのpyrebaseライブラリ設置

## 設置エラー詳細

Collecting jws>=0.1.3 (from python-jwt==2.0.1->pyrebase)
  Using cached jws-0.1.3.tar.gz (8.1 kB)
  Installing build dependencies ... done
  Getting requirements to build wheel ... error
  error: subprocess-exited-with-error

  × Getting requirements to build wheel did not run successfully.
  │ exit code: 1
  ╰─> [21 lines of output]
      Traceback (most recent call last):
        File "C:\Users\staff\AppData\Local\Programs\Python\Python312\Lib\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 
353, in <module>
          main()
        File "C:\Users\staff\AppData\Local\Programs\Python\Python312\Lib\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 
335, in main
          json_out['return_val'] = hook(**hook_input['kwargs'])
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "C:\Users\staff\AppData\Local\Programs\Python\Python312\Lib\site-packages\pip\_vendor\pyproject_hooks\_in_process\_in_process.py", line 
118, in get_requires_for_build_wheel
          return hook(config_settings)
                 ^^^^^^^^^^^^^^^^^^^^^
        File "C:\Users\staff\AppData\Local\Temp\pip-build-env-sel3mby8\overlay\Lib\site-packages\setuptools\build_meta.py", line 325, in get_requires_for_build_wheel
          return self._get_build_requires(config_settings, requirements=['wheel'])
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "C:\Users\staff\AppData\Local\Temp\pip-build-env-sel3mby8\overlay\Lib\site-packages\setuptools\build_meta.py", line 295, in _get_build_requires
          self.run_setup()
        File "C:\Users\staff\AppData\Local\Temp\pip-build-env-sel3mby8\overlay\Lib\site-packages\setuptools\build_meta.py", line 480, in run_setup   
          super(_BuildMetaLegacyBackend, self).run_setup(setup_script=setup_script)
        File "C:\Users\staff\AppData\Local\Temp\pip-build-env-sel3mby8\overlay\Lib\site-packages\setuptools\build_meta.py", line 311, in run_setup   
          exec(code, locals())
        File "<string>", line 17, in <module>
        File "<string>", line 5, in read
      UnicodeDecodeError: 'cp932' codec can't decode byte 0x93 in position 502: illegal multibyte sequence
      [end of output]

  note: This error originates from a subprocess, and is likely not a problem with pip.
error: subprocess-exited-with-error

× Getting requirements to build wheel did not run successfully.
│ exit code: 1
╰─> See above for output.

note: This error originates from a subprocess, and is likely not a problem with pip.

## 解決方法

$env:PYTHONUTF8 = 1
pip install pyrebase
Resoniteのキャッシュの消し方

Resoniteのキャッシュの消し方
1.Windowsキー と Rキーを押す。
2.%USERPROFILE%\AppData\Local\Temp\Yellow Dog Man Studios\Resoniteを入力かコピーする。
3.EnterキーまたはOkボタンを押す。
4."Cache"フォルダを選択します。
5.Shiftキー を押しながら Deleteキー を押す。キャッシュが非常に大きく、一時ファイルから構成されているため、ごみ箱をスキップして削除できます。
Ardupilotドローン制作④ ～VIFLY Cam Switcherの利用設定～

カメラを2つ装備し、プロポから切り替える設定を行います。  

[遥か昔](https://odekake-p.hateblo.jp/entry/20200226/1582647645#FC%E8%B3%BC%E5%85%A52%E5%9B%9E%E7%9B%AE)にiNavで使っていたFCにはオンボードでこの機能があったのですが、時は流れて...  

100g以上は無人航空機とする国土交通省航空局が適当なお気持ちで制定した法令がこの日本には強いられているので、20*20のタワーFCを使うのがちょっと嫌です。。  

そのため、外付けパーツを使ってカメラ切り替え機能を搭載し、Ardupilotで機能させるための設定を行いました。

## 使用機器

[VIFLY Cam Switcher](https://ja.aliexpress.com/item/1005003344198879.html)

![スクリーンショット 2024-02-02 034609.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341104/bc8163b4-a798-5956-1239-edb09a9eea86.png)


pwm/sbus/ibus/dsmxで制御可能なカメラ切り替え基板です。  
現状これしか良い感じのが売ってなく、軽くて安いので文句なく購入しました。

## 設定方針

Ardupilotにおいて、TXポートからPMWを出す方法は無いらしい？  
しかし、サーボ向けにSbus出力することは可能らしい
[参考](https://discuss.ardupilot.org/t/convert-uart-to-pwm-output/53998)  

→あれ、VIFLY Cam Switcherってsbusでも制御できるじゃん！もしかしてこれ上手くいくのでは...?

## VIFLY Cam Switcherの設定

マニュアルに従い、電源とコントロール線、2台のカメラを配線します。  
今回はSbusで制御するので、ショートさせるパッドはショートさせず、`S.B`のパッドとFCのTXポートをはんだします。

## シリアルポートの設定

任意のTXポートとVIFLY Cam Switcherの`S.B`のパッドをはんだします。

n=つないだTXの番号とし、以下の設定を行います

```
SERIALn_PROTOCOL=15 (SBus servo out)
SERIALn_OPTIONS=2 (InvertTX)
```

GUIからでも設定可能です。

![スクリーンショット 2024-02-02 015749.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341104/e126b5e6-ffcb-bda1-e36d-5491d3bc15aa.png)


この設定を行うことで、TXポートからサーボモーター制御用のSbus信号を出す準備が整います。

## サーボの動作設定・プロポ設定

サーボ信号を出すための設定を行います。  

### RCチャンネルとサーボの関係

RCチャンネルとサーボの数値は一致します。  
例えば、RCチャンネル8で制御するサーボの番号は8です。  
逆に言えば、サーボ7をRCチャンネル8で制御するという設定はすることができません。  
  
なにを言いたいのかというと、サーボを使いたいスイッチを設定するためには、サーボ番号を調べ、その番号に対応する送信CHをボタンに割り当てるよう、プロポ側の設定を変更する必要があります。

## サーボの番号調査

1. Mission Plannerの上部メニュー`フライト・データ`をクリック
1. `サーボ`タブを開く
1. 5~16の`Low,Mid,High,トグル`のボタンを操作し、VIFLY Cam Switcherが機能するサーボ番号を特定する

![スクリーンショット 2024-02-02 033741.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341104/ba1ec796-07c7-321a-17ef-cb4747f90f6a.png)


### プロポのスイッチとRCチャンネルの対応を確認

1. プロポの電源を入れ、レシーバーの電源をいれバインドされている状態にする
1. Mission Plannerの上部メニュー`初期設定`をクリック
1. `必須ハードウェア`のメニューを選択
1. `ラジオキャリブレーション`のメニューを選択
1. プロポのボタンを操作し、RCチャンネルとの対応を確認する

![スクリーンショット 2024-02-02 015742.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341104/15cfa532-86d2-0cd3-4337-44c499d6f564.png)


### プロポ側の設定

`サーボの番号調査`で判明した数字のCHで、操作に使いたいスイッチが数値を送信するように設定を行う。

### サーボの操作値を受信機からの値にする設定

n=`サーボの番号調査`で判明した数字とし、以下の設定を行います

```
SERVOn_FUNCTION=1 (RCPassThru)
```

GUIからでも設定可能です。

![スクリーンショット 2024-02-02 015754.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3341104/0b924ad3-429c-3038-9d5c-48243f4e2d7f.png)


## 完了！

以上で設定を完了し、機体の電源を入れ、プロポのスイッチからVIFLY Cam Switcherが機能し、カメラ映像を切り替えることが確認できたら成功です。
Ardupilotドローン制作③ ～Wifiモジュール～

## 使用機器

### ESP8266-SLIM

- 開発ボード
- 載っているwifiモジュールはESP-WROOM-02D
- USB端子があるためファームフラッシュがしやすい
- 開発ボード側に電源とTXRXをつなぐだけでArdupilotですぐ使える
- 5.6g

### ESP-WROOM-02D

- wifiモジュールのみ
- ファーム書き換え用の配線・実行用の配線をしてあげる必要がある
- ファームフラッシュにはFTDIのUSBモジュールを繋げて行う
- 起動に必要な配線込みで2.4g程度になる。当然軽い


### (共通)ファームフラッシュ

[公式](https://ardupilot.org/copter/docs/common-esp8266-telemetry.html)参照。ESP8266用のMavlink2を使えるファームが配布されている

### (共通)接続してみる

前回の記事で

```
PC未接続の機体に通電させ、wifiモジュールに給電すると、Ardupilotアクセスポイントが出現する。
パスワードardupilotを入力すると接続でき、Mission PlannerとUDPで接続できる。
無線で機体のチューニングを行ったり、飛行中の機体に自動航行の航路を指示できるようになる。
```

としてましたが、これは正確にはESP8266用のMavlink2を使えるファームをあらかじめフラッシュしている場合で、でした
Wifiモジュールにファームを焼いて初めてアクセスポイントが出現し、使うことができます。  
Wifi接続方法は上記内容の通りです。

### (ESP-WROOM-02D)配線

モジュールのみの最小構成で使う場合、開発ボードがやってくれていた配線のうち、ファーム書き換え用・実行用の配線だけは使用においてやってあげる必要があります。  

[このサイト](https://shimanuki-farm.net/archives/1614/)  
または[このサイト](https://www.petitmonte.com/robot/howto_esp_wroom_02.html#google_vignette)
が画像で解説されており、わかりやすかったです

- ファーム書き換え用の配線
  - 各GNDをすべて繋げておく
  - IO0,IO2,ENに10kΩの抵抗
  - IO2,EN,3V3に3V通電
  - IO0とGNDを接続
  - IO0とRSTを手でショートできるようにしておく
  - TXDとRXDをFTDIアダプターとつなげ、PCとUSB接続
- 実行用の配線
  - 各GNDをすべて繋げておく
  - IO0,IO2,ENに10kΩの抵抗
  - IO0,IO2,EN,3V3に3V通電
  - TXDとRXDをフライトコントローラとつなげ、FCに給電すると起動する
Windows: 管理者権限にチェックを入れて起動しているはずなのにPermissionError: [Errno 13] Permission deniedが出る

# 結論
プロパティ -> ショートカット -> リンク先 を編集する必要があった。

```
path --user-data-dir="フォルダのパス"
```

参考：

https://zenn.dev/nondescript/articles/11e750f159e1d2

プロパティ -> 互換性 -> 管理者としてこのプログラムを実行する にチェックを入れるだけではだめだったようです。

# 環境
Windows, VScode 

# 経緯
JupyterNotebookでモデルのトレーニングをしていたところ、チェックポイントの保存の際にパーミッションエラーで中断が起きた。
権限も問題なく、チェックポイントの一部は保存できていたため不可解だったがこれで解決した。
コード関係かと思い脱線してたどり着くまで2週間かかった

# 他、参考になるかもしれないもの
だめな気がして試していませんが、似た状況っぽいもの。

https://discuss.huggingface.co/t/a-very-strange-error-when-saving-the-checkpoint/17215
https://stackoverflow.com/questions/76239374/permissiondenied-saving-files-via-a-pyinstaller-executable-works-in-vs-code
Grasshopperでサーフェスのエッジを取得する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662314/d0bb2b4c-c1e6-7fde-9c30-13e12f9c2f9f.png)
## Brep Edges
通常はこちらのコンポーネントを使用するかと思います。
ちなみにoutputは、
* Naked：直訳は「裸の」という意味。つまり外周部分のエッジ
* Interior：直訳は「内部の」内側のエッジ
* Non-Manifold：これは数学的な用語で、非多様体のエッジ部分を意味します。
非多様体の厳密な説明ではないですが、体積が定義できないような図形のことで、辺で接したり点で接するような部分を含む形状を指します。

https://japan.mimaki.com/support/faq/operation/entry-394644.html?iframe=true&width=100%&height=100%

こちらが参考になるかと思います。

## Curve
実はこちらのコンポーネントを使用するとサーフェスのエッジを取得できます。
これはプログラミングの概念でいうところのキャストになるかと思います。
キャストでいうと数字データをブール値（真偽）に変換したりする小技があったりします。

## コンポーネント集
https://qiita.com/hiz0000/items/844c124527fd3dd2c7d6
短絡評価をif文で書き直してみた

# はじめに
短絡表現について学習しましたが、「これってif文だとどう書くんだっけ？」と思い、書き直してみました。

言語はJavaScriptを使用しています。

# `&&` 演算子

`&&`演算子は、条件が真のときにのみ後続の処理を行いたい場合に使われます。

**短絡評価の例:** 

```javascript
condition && doSomething();
```



この式は、`condition`が真（truthy）の場合にのみ`doSomething()`関数を実行します。

**if文で書き換える** 

```javascript
if (condition) {
  doSomething();
}
```


# `||` 演算子
`||`演算子は条件が偽のときにのみ処理を行いたい場合に使われます。

**短絡評価の例:** 

```javascript
condition || doSomething();
```



この式は、`condition`が偽（falsy）の場合にのみ`doSomething()`関数を実行します。

**if文での書き換える:** 

```javascript
if (!condition) {
  doSomething();
}
```
AWS ALB で 相互認証(mTLS) をする

# 概要

ALB で X509 証明書を提示する相互認証クライアントをサポート

https://aws.amazon.com/jp/blogs/news/mutual-authentication-for-application-load-balancer-to-reliably-verify-certificate-based-client-identities/


# 今まで

mTLS認証を⾏うためには NLB で HTTPS をパススルーして Apache 等 でクライアント認証を⾏う必要がありました


# 相互認証 (mTLS)

|パススルー|トラストストアで検証|
|---|---|
|証明書全体が HTTP ヘッダーとしてバックエンドターゲットに送信され、クライアント証明書が信頼できるかどうかが検証される。|ロードバランサーとクライアントは互いの ID を検証し、TLS 接続を確立して互いの間の通信を暗号化する。|


# トラストストアで検証

トラストストアで検証を試してみた。

## 秘密鍵の作成

```rootCA_key.pem
openssl genrsa -out rootCA_key.pem 2048
```

## CA 証明書の作成

```rootCA_cert.pem
openssl req \
    -x509 \
    -new \
    -nodes \
    -sha256 \
    -days 365 \
    -key rootCA_key.pem \
    -out rootCA_cert.pem
```
 
## クライアント証明書の作成

```client_cert.pem
openssl req \
    -x509 \
    -CA rootCA_cert.pem \
    -CAkey rootCA_key.pem \
    -days 365 \
    -new \
    -nodes \
    -keyout client_key.pem \
    -out client_cert.pem
```

## CA 証明書を S3 に保存

```
aws s3 rootCA_cert.pem cp s3://bucket/cert/
```

## トラストストアを作成

理解のために今回は AWS コンソールから操作していきます。

EC2 > トラストストア を開き、「トラストストアを作成」
![スクリーンショット 2024-02-01 22.50.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/cbf7edef-e8a1-a038-17ff-adba50285ea8.png)

先程 S3 にアップロードした CA 証明書を選択して登録します。
![スクリーンショット 2024-02-01 23.28.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/e0dda005-a499-8875-8222-3656f1fda419.png)

## ALB での設定

「トランストアで検証」を選択し、先程作成したトランスストアを指定します。
![スクリーンショット 2024-02-01 23.34.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280929/acb62228-ff18-9645-26b3-d1064f4e5cd6.png)

## 検証

クライアント証明書を指定してアクセス

```
[ec2-user@ip-172-31-30-120 ~]$ curl https://www.XXX.com
curl: (56) Recv failure: Connection reset by peer
```

クライアント証明書を指定せずアクセス

```
[ec2-user@ip-172-31-30-120 ~]$ curl https://www.XXX.com --key client_key.pem --cert client_cert.pem
test
```
【nginx】動的にリクエストを振り分ける


## やりたいこと

以下のようにリクエストを振り分けたい

|リクエスト先|プロキシ後のリクエスト先|
|---|---|
|http://sample.test.com:80/hello|http://sample2.test.com:81/|
|http://sample.test.com:80/hello/goodbye|http://sample2.test.com:81/goodbye|

要はリクエストパスからhelloの部分を取り除いて別のサブドメインの別ポートにリクエストしたい

## やり方

以下のようにmapを使う

```bash
    server {
        listen       80;
        listen       [::]:80;
        server_name _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        # 最後の”/"を忘れない！
        location /hello/ {　
            resolver    <your resolber address> valid=5s;

            # /hello以下の部分を取り出す
            set $new_uri $uri;
            if ($uri ~ ^/hello(/.*)$) {
              set $new_uri $1;
            }
            
            # 81の後に"/"は不要
            proxy_pass http://sample.test.com:81$new_uri;
        }
```

これでリクエストを送るとちゃんとプロキシ後のリクエスト先に届いている。
railsで作成したアプリに投稿日時を表示させる方法

# 投稿日時を表示させるには

以下の記述をすることで投稿日時を表示できる。
```ruby:〇〇.html.erb
<%= テーブル名.created_at %>
```

ただし、この記述だとUTC(世界標準時刻)の時間が表示され、日本時間で表示されないため、日本時間への変更が必要になる。

# 日本時間を表示させる

## 手順1
日本時間を表示させるには aplication.rb を以下のように編集する。
```ruby:aplication.rb
module TaskMemo
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 7.0
    
    config.i18n.default_locale = :ja
    config.time_zone = 'Tokyo'
    # 省略
  end
end
```

```
config.i18n.default_locale = :ja
config.time_zone = 'Tokyo'
```
上記の1行目で __アプリのデフォルトの言語設定が日本語となり__、
2行目の記述で __地域を東京に設定している__。

## 手順2

```ruby:config/locales/ja.yml
ja:
  time:
    formats:
      default: "%Y/%m/%d %H:%M:%S"

```
上記の記述までを行うことで、時刻の設定ができた。

# 時刻を表示させる記述を編集

最後に以下のよに記述することで日本時間の表示ができる。
```ruby:〇〇.html.erb
<%= l テーブル名.created_at %>
```

l(エル)メソッドを使用して、表示する時刻へ反映させることができる。
lメソッドは、日付や時刻を表示するRailsのメソッドで、国や地域によって時刻は異なるが、lメソッドを使えば指定した現地時間に対応できる。

※コード編集後は `rails s` でサーバーを再起動しないと日本時間が表示されない。
仮想環境venvについて

pythonでの開発を仮想環境で行うときの手順

作業ディレクトリにはいる
```
python3 -m venv env

```
こうすると作業ディレクトリにenvフォルダができる
以下のコマンドをうつと仮想環境に入れる
```
source env/bin/activate
```
あとはここで必要なものをインストールしまくるだけ
辞めたい時は
```
deactivate
```

疑問
このフォルダはそこそこ大きいためgit hubにはあげたくない
その場合gitignoreを使えば良いのだろうか
OpenAPI逆引きメモ

必要に迫られたOpenAPI関連の逆引きメモになります

## サンプルの複数記述

examples フィールドはOpenAPI Specification 3.0以降でのみ使用可能

```yaml
paths:
  /your/path:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/YourRequestBodySchema'
            examples:
              example1:
                summary: サンプル1の説明
                value:
                  # ここにサンプル1のリクエストボディを記述
              example2:
                summary: サンプル2の説明
                value:
                  # ここにサンプル2のリクエストボディを記述
```

## yamlをhtmlへ変換

DockerでRedocly CLIを使用してyamlをhtmlへ変換

```posh
docker run --rm -v "C:\.swagger:/spec" redocly/cli build-docs /spec/test_api.yaml -o /spec/test_api.html
```
os.pipe()が返すファイル記述子について

# 疑問

パイプを使ってプロセス間でメッセージをやり取りするプログラムを作成していました。

その中で、`os.pipe()`の返すファイル記述子が毎回`3`と`4`になっているのはなぜか疑問に思いました。

```
import os

r, w = os.pipe()
print(r)
print(w)

# 3
# 4
```

# 回答

プロセスが開始されると、標準入力（stdin）、標準出力（stdout）、標準エラー出力（stderr）が自動的にファイル記述子0、1、2に割り当てられるためでした。

これにより、最初にプログラムが開くファイルやパイプのファイル記述子は、上記の標準ストリームの次の番号、つまり3から始まります。

```python
import os
import sys

r, w = os.pipe()
pid = os.fork()

# 親プロセス
if pid > 0:
    print("stdinのファイル記述子:", sys.stdin.fileno())
    print("stdoutのファイル記述子:", sys.stdout.fileno())
    print("stderrのファイル記述子:", sys.stderr.fileno())
    print("読み取り端のファイル記述子:", r)
    print("書き込み端のファイル記述子:", w)
    os.close(r)
    message = "Message from parent with pid {}".format(os.getpid())
    print("Parent, sending out the message - {}".format(message))
    os.write(w, message.encode("utf_8"))
# 子プロセス
else:
    os.close(w)
    print("Fork is 0, this is a Child PID:", os.getpid())
    f = os.fdopen(r)
    print("Incoming string:", f.read())

# stdinのファイル記述子: 0
# stdoutのファイル記述子: 1
# stderrのファイル記述子: 2
# 読み取り端のファイル記述子: 3
# 書き込み端のファイル記述子: 4
# Parent, sending out the message - Message from parent with pid 88290
# Fork is 0, this is a Child PID: 88291
# Incoming string: Message from parent with pid 88290
```
【JavaScript】配列に条件に一致する値が存在するか検証する

以下のように記述することで配列に条件に一致する値が存在するか検証できます。

```js
const array = [1, 3, 5, 7, 9]
const test1 = num => num > 8
const test2 = num => num > 10

console.log(array.some(test1)) // true
console.log(array.some(test2)) // false
```

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/some
【Kotlin】retry

次のようにすることで、`getResponse()` が成功するか もしくは設定した回数失敗するまでリトライすることができる。


```Kotlin
val result: Result<Response> =
    flow {
        emit(
            getResponse() // 失敗（例外をスロー）する可能性がある suspend 関数
        )
    }
        .retry(retries = 10) { e -> // 最大 10 回リトライする（初回を合わせると最大11回試行する）
            e is IOException // IOException がスローされた場合のみリトライする
        }
        .runCatching {
            first() // 最初の結果が得られたら終了する（リトライ上限を超えたら最後の例外をスローする）
        }
result
    .onSuccess { response: Response ->
        // 成功時の処理
    }
    .onFailure { e: Throwable ->
        // 失敗時の処理
    }
```

／以上
[Oracle DB]トランザクションロックを解除する

Oracleでトランザクションロックを解除する流れの覚え書き。

## 環境

- Oracle Database 21c

## 手順

### (1) トランザクションロックのかかっているテーブル情報の確認

以下のクエリを実行し、トランザクションロックのかかっているテーブル情報を確認します。
SIDとSESSION#は(2)で使うので、トランザクションロックを解除したいテーブルのものを控えておきます。
なお、DO.OWNERがスキーマ名、DO.OBJECT_NAMEがテーブル名になります。

```sql
-- 全ロックを表示: ロックしているもの
SELECT DO.OWNER || '.' || DO.OBJECT_NAME AS OBJECT,
  DECODE(LO.LOCKED_MODE,
     1, 'NULL',
     2, '行共有(SS)',
     3, '行排他(SX)',
     4, '共有(S)',
     5, '共有行排他(SRX)',
     6, '排他(X)',
     '???' ) AS LOCKED_MODE,
  TO_CHAR(L.CTIME / 60,'99990.9') AS MIN,
  LO.ORACLE_USERNAME,
  LO.OS_USER_NAME,
  LO.PROCESS,
  S.SID,
  S.SERIAL#
FROM DBA_OBJECTS DO, V$LOCKED_OBJECT LO, V$LOCK L, V$SESSION S
WHERE DO.OBJECT_ID = LO.OBJECT_ID AND
  LO.SESSION_ID = L.SID AND
  LO.XIDSQN = L.ID2 AND
  LO.PROCESS = S.PROCESS AND
  LO.XIDUSN > 0;
```

### (2) SESSIONのKILL

(1) で控えておいたSIDとSESSION#を使って、SESSIONをKILLします。

```sql:
-- SESSIONをKILLする: [例] SID = 177, SERIA#L = 4335 の場合
 ALTER SYSTEM KILL SESSION '177,4335';
```

上記のDDL文実行には、ALTER SYSTEM システム権限が必要です。
実行ユーザーに権限がない場合は、SYSTEMユーザから以下のDCL文を実行して権限を付与して（もらって）ください。
```sql:
-- ユーザー: ELEONORAに権限を付与する場合
GRANT ALTER SYSTEM TO ELEONORA;
```

これで、トランザクションロックが解除される（はず）

## 参考

本記事の内容は、以下のページたちの組み合わせ。
先人の皆さまに感謝！

- [全ロックを表示する方法(ロック時間付き)| www2.odn.ne.jp](http://www2.odn.ne.jp/~cag07740/tech_info/oracle_ans34.html)
- [OracleのFOR UPDATEの排他ロックを解除する | きり丸の技術日記](https://nainaistar.hatenablog.com/entry/2021/07/30/120000)
- [Oracle 特定のセッションをKILLする | コピペで使う](https://oha-yo.com/oracle/kill_session/)

なお、関連するテーブルの公式リファレンスのページは、以下の通り。

- [DBA_OBJECTS | Database Reference](https://docs.oracle.com/en/database/oracle/oracle-database/21/refrn/DBA_OBJECTS.html#GUID-4EB0E78C-EA23-4D7B-98F2-D0583C7C1F62)
- [V$LOCKED_OBJECT | Database Reference](https://docs.oracle.com/en/database/oracle/oracle-database/21/refrn/V-LOCKED_OBJECT.html#GUID-3F9F26AA-197F-4D36-939E-FAF1EFD8C0DD)
- [V$LOCK | Database Reference](https://docs.oracle.com/en/database/oracle/oracle-database/21/refrn/V-LOCK.html#GUID-87D76889-832C-4BFC-B8B0-154A22721781)
- [V$SESSION | Database Reference](https://docs.oracle.com/en/database/oracle/oracle-database/21/refrn/V-SESSION.html#GUID-28E2DC75-E157-4C0A-94AB-117C205789B9)
チェックボックスをトリガーとして活性・非活性を切り替える

# はじめに
私は初学者のエンジニアです。
実務を通しての気付きなどを記事にしていきます。
これからエンジニアを志す方や同じ初学者の方の力になれれば幸いです。
# チェックボックスでフォームの活性・非活性を切り替える
・チェックボックスでのアンケートで、「その他」を選んだ際のみ理由を記述してもらう

入力フォームをデザインしている中で、こうった実装が必要となりました。
「その他」の場合のみ入力した内容を受け付けたいので、チェックが入っていない時に書かれたら困る。
バックエンドでフラグを使って実装しても良いですが、わざわざ書くまでもないしそもそも入力出来ないようにしたらええやん、と。

そこで、
① 遷移した段階の初期画面ではテキストボックスは非活性で入力できない
②「その他」にチェックが入ったらテキストボックスを活性化させる
③「その他」のチェックを外したら再び非活性とする

これをJavaScriptで書くことにしました。

# 実装方法
まずはHTMLでチェックボックスとテキストボックスを実装します。
```HTML:HTML
<div>
    <input class="form-check-input" id="chkbox" type="checkbox" />
    <label class="form-check-label" for="chkbox" >その他</label>
    <input class="form-control" id="textbox" type="text" disabled="disabled" />
</div>
```
`< タグ名 disabled="disabled" >`で要素を非活性化します。
これで①の非活性なテキストボックスが出来ました。

次に、②と③の実装をします。
こちらはJavaScriptで記述していきます。

```JavaScript:JavaScript
$(function() {
  $('#textbox').attr('disabled', 'disabled');
  
  $('#chkbox').click(function() {
    if ($(this).prop('checked') == false) {
      $('#textbox').attr('disabled', 'disabled');
    } else {
      $('#textbox').removeAttr('disabled');
    }
  });
});
```
チェックボックスがクリックされたら関数が実行されます。
チェックが入っているかをtrue or falseで判断します。
trueならばdisabled要素がremoveされるので、テキストボックスが活性化します。

これで実装が完了しました。
# 活性化してる時だけplaceholderを入れる
これも実装します。
要領は一緒ですね。

```JavaScript:JavaScript
$('#chkbox').click(function() {
    if ($(this).prop('checked') == true) {
        $('#textbox').attr('placeholder', '入力してください');
    } else {
        $('#textbox').removeAttr('placeholder');
    }
});

```

チェックが入っている時だけplaceholderを表示します。

# 最後に
以上がJavaScriptを使用して活性・非活性を切り替える方法でした。

最後まで読んでいただきありがとうございました。
ONLYOFFICEデスクトップエディタ8.0をMoodleサーバに接続する方法

[ONLYOFFICEデスクトップアプリ](https://www.onlyoffice.com/ja/desktop.aspx?utm_source=qiita&utm_medium=cpc&utm_campaign=desktop_to_moodle_htf)は、テキスト文書、スプレッドシート、プレゼンテーション、フォームおよびPDF用エディタで構成される、オープンソースオフィススイートです。オフラインでの作業だけでなく、アプリケーションをクラウドに接続し、オンライン文書コラボレーションを行うことも可能です。スイートのソースコードは、AGPL v.3.0ライセンスの下、GitHubで公開されています。 

最新のバージョン8.0では、複数の機能が強化されています：
- 記入可能なPDFフォーム
- 双方向テキストとRTLインターフェース；
- アラビア語ローカライズ；
- PDFファイルのパスワード保護
- ゴールシーク、チャートウィザード、シート内のシリーズ；
- スクリーンリーダーの強化；
- プラグイン用UIの更新；
- ローカルインターフェイステーマの読み込み。

![スクリーンショット 2024-02-01 23.13.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2655664/05fb812f-72df-5e04-ce77-77d164417993.png)

バージョン8.0では、サポートされるクラウドプロバイダのリストにMoodleが追加されました。これは、あなたのMoodleプラットフォームに保存されたドキュメントをデスクトップアプリから直接編集し、ローカルプラグイン、フォント、印刷サービスおよびスペルチェッカーを含む、デスクトップスイートで利用可能なすべての機能を使用できることを意味します。

このチュートリアルでは、ONLYOFFICEデスクトップアプリをMoodleインスタンスに簡単に接続する方法をご紹介します。

## 前提条件

- インストールおよび設定されたMoodleサーバインスタンス
- ONLYOFFICE Docs + Moodleコンボ（統合アプリ経由で接続）*
- インターネット接続

 *＊ONLYOFFICE DocsとMoodleを統合するには、[この手順](https://www.howtoforge.com/how-to-integrate-onlyoffice-docs-with-moodle-4/)を参照してください。*

## ステップ1：ONLYOFFICEデスクトップエディタをインストールする

このアプリを初めて利用する場合、.debおよび.rpmパッケージ、AppImage経由、snapパッケージ、Flatpak経由など、適切な利用可能オプションの中から選択してインストールしてください。[コンパイル](https://www.howtoforge.com/how-to-compile-onlyoffice-desktop-editors-7-3-for-ubuntu/)、[Linux Mint](https://www.howtoforge.com/how-to-install-onlyoffice-desktop-editors-on-linux-mint/)用、[Zorin OS](https://www.howtoforge.com/how-to-install-onlyoffice-desktop-editors-on-zorin-os/)用のインストールチュートリアルが用意されています。

ONLYOFFICEデスクトップアプリをDebian、Ubuntu、および派生OSにインストールするには、以下の手順に従ってください。

GPGキーを追加します：
```
mkdir -p -m 700 ~/.gnupg
gpg --no-default-keyring --keyring gnupg-ring:/tmp/onlyoffice.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys CB2DE8E5
chmod 644 /tmp/onlyoffice.gpg
sudo chown root:root /tmp/onlyoffice.gpg
sudo mv /tmp/onlyoffice.gpg /usr/share/keyrings/onlyoffice.gpg
```
リポジトリを追加します：
```
echo 'deb [signed-by=/usr/share/keyrings/onlyoffice.gpg] https://download.onlyoffice.com/repo/debian squeeze main' | sudo tee -a /etc/apt/sources.list.d/onlyoffice.list
```
パッケージマネージャのキャッシュを更新します：
```
sudo apt-get update
```
エディタをインストールします：
```
sudo apt-get install onlyoffice-desktopeditors
```
ターミナルコマンドでアプリを実行します：
```
desktopeditors
```

## ステップ2：ONLYOFFICEデスクトップアプリとMoodleの接続

ONLYOFFICE Desktop Editorsを起動し、左サイドパネルのConnect to cloudセクションに切り替えてください。

「Moodle」ボタンをクリックし、クラウドオフィスURLフィールドにあなたのMoodleサーバドメイン名またはIPアドレスを入力してください。準備ができたら、「今すぐ接続」ボタンをクリックしてください。

![スクリーンショット 2024-02-01 23.11.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2655664/fb403edf-3c4b-e087-bcfd-81abbd98a878.png)

Moodleメインページがデスクトップエディタインターフェースの新しいタブで開きます。コースが拡張アクセスを必要とする場合、あなたのユーザ名およびMoodleアカウントへのアクセスに使用するパスワードを指定してログインしてください。

![スクリーンショット 2024-02-01 23.13.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2655664/cea00dd7-ce63-f064-38f2-2e960d58d36d.png)

完了です！これでONLYOFFICEデスクトップエディタがMoodleサーバに接続されました。既存のファイルを編集したり、新しいファイルを作成したり、他のMoodleユーザとドキュメントを共有したり、共同作業することができるようになりました。
【dbt】dbt coreのバージョンアップのメモ

# 背景・目的
以前、[dbtを試してみた](https://qiita.com/zumax/items/e99dabf04d2ca0dc2437)という記事を公開してから、しばらくdbtに触れていませんでした。
気づいたときには、dbt cloudで使用しているdbt coreのバージョンがもうすぐEOSLを迎えることになりそうだったため、アップグレードした際のメモを残します。


# 実践
1. dbtにサインインします
1. 上部のメニューから①「Develop」、②「Environments」を選択します。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/7d00931a-b9cd-e4e5-467a-71e33dba825d.png)

1. バージョンは1.5になっています。クリックします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/af896e86-fa70-37d1-b3fe-c609c664a3cc.png)

1. 画面右上の「Settings」をクリックします
1. 「Edit」をクリックします
1. latestの「1.7」を選択し、「Save」をクリックします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/6f19aa9b-214b-e0f4-30f8-a5856d3e4f11.png)

1. ポップアップが表示されるので、「Continue」をクリックします。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/790b2ccb-98c2-57a8-4166-2cdb92ce9c19.png)

1. 更新されました。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/0314084a-adac-9401-d1d8-eae571940e6e.png)

# 考察
今回、dbtバージョンをアップグレードしました。今後も少しづつdbtに慣れていきたいと思います。

# 参考
https://docs.getdbt.com/docs/dbt-versions/upgrade-core-in-cloud
アプリ作成

## アプリのアイディア出し
アプリのアイディア出しをチームで行いました。
その結果、自分たちの身近な課題を解決できた方が良いよねという話になり日々のタスク管理アプリを作成することに決めました。

**スライド1**
![スクリーンショット 2024-01-31 21.37.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1838981/c7d69650-ab88-e855-43bd-ed7f1c163eda.png)

**スライド2**
![スクリーンショット 2024-01-31 21.39.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1838981/747d4a54-7f7a-3d50-04eb-3ba422355267.png)


## 今回使用した技術やツール
コード管理：Github
フロントエンド言語：HTML,CSS,JavaScript
バックエンド言語：Ruby
データベース：MySQL
## 遭遇した課題や問題、それをどのように解決したか
**課題と問題点**
①フレームワークを用いてのWebアプリの開発経験はあるものの、Rubyのみを用いてWebアプリを開発したことはなかったのでどのように進めていくかのイメージができなかった。
　
②チームメンバーがどこの機能を実装するのかのタスクの割り振りが上手くできなかった。
　
**どのように解決したのか**
①RubyのみでサーバーをたてWebアプリを作る方法をひたすら調べました。
その結果webrickを用いればなんとかなりそうだなという結果に落ち着く。
[見つけた記事](https://qiita.com/littlekbt/items/018a1630101e0521e518)

②チームメンバー全員がお互いに遠慮してしまった感じがするので、それが良くなかったなと思います。
もっとお互いに意見を出し合い全員が積極的に開発に関われれば良かったです。

## プロジェクトを通じて学んだことや個人的な成長。
**チームとしての経験から得られた教訓**
チームメンバーに対してもっと意見をはっきりと言って良い。
遠慮をして何も言わないでいると後々問題になるので、何かに気づいたらその場ですぐに言う。


## 完成したアプリケーションの機能や特徴。
**機能や特徴**
カレンダーにその日行ったタスクを登録できるようになっています。
もともと考えていた機能はほとんど実装することができず、不甲斐ない結果になってしまいました。

（当初考えていた機能）
![スクリーンショット 2024-02-01 22.28.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1838981/d2cd44b7-6c01-31e0-3a6c-75d7bc5450e1.png)
 
- **今後の計画やアップデート**
ユーザーの嗜好に合わせたおすすめレシピの表示 Google Discoverの様な機能
おつまみの投稿機能と、アレンジレシピとの紐づけ機能
レシピのお気に入り機能
特集ページの設置（例：家飲み、キャンプ、ノンアルコールなど）
商品名からの検索機能

**アプリケーションのスクリーンショット**
![スクリーンショット 2024-02-01 22.49.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1838981/ee44f51d-d1df-12fc-04cd-35f9f78a2f4f.png)
DataFrameに対する理解確認メモ

データサイエンス100本ノックをやりながら、理解を深めるための個人的メモに近いもの。

https://github.com/The-Japan-DataScientist-Society/100knocks-preprocess


## DataFrameに対する表示列の指定

```
> df_receipt
sales_ymd	sales_epoch	store_cd	receipt_no	receipt_sub_no	customer_id	product_cd	quantity	amount
0	20181103	1541203200	S14006	112	1	CS006214000001	P070305012	1	158
1	20181118	1542499200	S13008	1132	2	CS008415000097	P070701017	1	81
2	20170712	1499817600	S14028	1102	1	CS028414000014	P060101005	1	170
```
上記DataFrameに対してstore_cdの列を取得するには単純に下記のようにすると取得できる
```
> df_receipt["store_cd"]
0         S14006
1         S13008
2         S14028
3         S14042
4         S14025
```
このときの型はpandas.core.series.Seriesになっている
ちょっとよくわかっていないのが、この状態からこの用に書くと
```
> df_receipt[["store_cd"]]
	store_cd
0	S14006
1	S13008
2	S14028
3	S14042
4	S14025
```
となり、ほぼほぼ見た目は変わらないが型はpandas.core.frame.DataFrameになる。
結果から推察すると、
df_receiptに対して、配列を渡すことで、渡した配列を結合した行列が出来上がるということか。
カギカッコが連続してある場合の、分解というか理解というかはちょっとなれないなぁ
【G検定】人工知能についてとその歴史を学習してみた

今回は、G検定の勉強ということで、「人工知能（AI）について」と「人工知能の歴史」の学習をしました。

# 参考書籍：
・**深層学習教科書 ディープラーニング G検定(ジェネラリスト)公式テキスト**


# 学習内容
## 人工知能（AI）の定義
### 人工知能(Artificial Intelligence)とは
　1956年にアメリカで開催された**ダートマス会議**において、著名な人工知能研究者である**ジョン・マッカーシー**が初めて使った言葉。この会議以降「人工知能」というものが学術的な研究分野の1つとして認められた。
　**推論、認識、判断など、人間と同じ知的な処理能力を持つ機械(情報処理システム)** であるという点は、大多数の研究者の意見は一致している。しかし、「人工知能とは何か」については、「知性」や「知能」自体の定義がなく、「人間と同じ知的な処理能力」の解釈が研究者によって異なるため、**共有されている定義は未だにない。**
 
### 人工知能の大まかな分類

　「エージェントアプローチ人工知能」(共立出版)は **周囲の状況（入力）によって行動（出力）を変えるエージェント（プログラム）** として人工知能を捉えた。このような視点から人工知能をレベル別に分類した。

###### レベル1：シンプルな制御プログラム
　**すべての振る舞いがあらかじめ決められている**製品。制御工学やシステム工学で長年培われた技術。さまざまな製品に古くから利用されている。
 　例：エアコンの温度調節、洗濯機の水量調節 etc

###### レベル2：古典的な人工知能
　**探索・推論、知識データを利用することで、状況に応じて極めて複雑な振る舞いをする**製品。特定の分野で高い有用性を示し、広く実用化されている技術。
 　例：掃除ロボット、診断プログラム etc

###### レベル3：機械学習を取り入れた人工知能
　**非常に多くのサンプルデータをもとに入力と出力の関係を学習**した製品。パターン認識という研究をベースに発展し、2000年代にはビックデータの時代を迎え、ますます進化している。
 　例：検索エンジン、交通渋滞予測 etc

###### レベル4：ディープラーニングを取り入れた人工知能
　**どのような特徴が学習結果に大きく影響するかを知ることを特徴量**と呼ぶ。この**特徴量と呼ばれる変数を、自動的に学習するサービスや製品。**
 　例：画像認識、音声認識、自動翻訳 etc

### AI効果
　人工知能で何か新しいことが実現され、その原理がわかってしまうと、人工知能(AI)ではないと結論付ける心理的な効果のこと。

### 人工知能とロボットの違い
　**ロボットの脳に当たる部分**が人工知能。
　人工知能の研究とは、**「考える（知的な処理能力）」という「目に見えないもの」** を中心に扱っている学問。

## 人工知能研究の歴史
###### エニアック(ENIAC)
　1946年にアメリカのペンシルバニア大学で開発された、巨大な電算機。

###### ダートマス会議
　1956年にアメリカで開催され、コンピュータ・プログラムの実現可能性について議論された。また、この会議で「人工知能」という言葉が初めて使われた。

###### ロジック・セオリスト
　ダートマス会議にて、アレン・ニューウェルとハーバード・サイモンによりデモンストレーションされた世界初の人工知能プログラム。
　コンピュータを用いて、数学の定理を自動的に証明可能であることを示した。

###### 第1次AIブーム（推論・探索の時代）
　1950年代後半～1960年代。**トイ・プロブレム(おもちゃの問題)** は解けるが、複雑な問題は解けないことが明らかになり、ブームは冬の時代を迎える。

###### 第2次AIブーム（知識の時代）
　1980年代。**エキスパートシステム**(データベースで専門知識を管理)が作られた。また、日本では **「第五世代コンピュータ」** と名付けられたプロジェクトが推進された。知識の蓄積・管理の大変さから1995年ごろからは再び冬の時代を迎える。
 
###### 第3次AIブーム（機械学習・特徴表現学習時代）
　2010年～現在。**ビッグデータ**を用いて人工知能が自ら知識を獲得する**機械学習**が実用化された。また、**特徴量を人工知能が自ら習得するディープラーニング(深層学習)** も登場した。しかし、**シンギュラリティー**(人間を超える超知性の誕生)に対する懸念も広がった。
ペンのテストケース（Test Cases for Pen）

先日、テスト設計書のフォーマットについて考えていたとき、ふと「英語圏ではどのようなフォーマットで、どのような項目名でテストが書かれるのだろう？」というのが気になり、テスト関連の英文記事を読んでいました。

その際に、たまたま面白い記事があったので紹介しようと思います。

https://artoftesting.com/pen

ペンに対するテストを書いて、テストについて学ぼう！　という記事です。

どうもテストエンジニアの面接において、提示されたものに対してどのようなテストが考えられるか、というのを問われることがあるそうで。

自分は海外でテストエンジニアの面接を受けたことがないので実態は分かりませんが、ともあれ面白い話だなと思いました。

我々QAあるいはテストエンジニアの多くは、基本的にはソフトウェアに対してテストを行なっています。しかし普段扱っているテスト対象から遠く離れることで、テストというものがどのようなものかについて、より純粋に考えられるようになるのではないでしょうか。

そんなわけで、上記の記事内容について簡単にご紹介したいと思います。

紹介許可を取っているわけではないので、全文翻訳はせず、意訳によるざっくりとした紹介に留めておきます。

詳しく知りたい方は上のURLから読んでみてください。

## Test Cases for Pen

ペンのテストを以下のようにテストタイプを分けて書かれています。

- ペンのUIテスト
- ペンの機能テスト
- ペンの異常系テスト
- ペンの性能テスト

以下、それぞれのテストタイプで書かれているテストを紹介していきます。

### ペンのUIテスト

- ペンの長さと直径が仕様通りであることの確認
- ペンのボディが要求仕様で指定された金属、プラスチック、その他の材質で作られていることの確認
- ペンのボディの色が仕様通りであることの確認
- ペンのボディに製造会社の企業名、ロゴがはっきりと見えることの確認


### ペンの機能テスト

- ユーザーがさまざまな種類の紙にはっきりと書けることの確認
- ペンの重さが仕様通りであることの確認
- 文字を書いたときのペンのインクの匂いの確認
- 紙やゴムの表面など、紙以外のものに文字を書けることの確認
- ペンを逆さまにしたときにインクが漏れないことの確認
- ペンで書かれた文字が、仕様に記載された時間内に消えないことの確認
- ペンが複数の替え芯をサポートできるかどうかの確認


### ペンの異常系テスト

- 超高温、または超低温環境下でのペンの機能の確認
- 無重力状態でのペンの機能の確認
- 極度の圧力を加えてペンの機能の確認
- ユーザーが逆さまにペンを使用して書くことができるかどうかの確認
- ユーザーがガラス、プラスチック、木材などのサポートされていない表面に書き込もうとしたときのペンの機能の確認

### ペンの性能テスト

- ユーザーがサポートされている表面上でペンを使用してどれだけ速く書くことができるかの確認
- 1本の替え芯でユーザーが書ける文字数の確認

---

なるほど、ペンに対してテストタイプが適用されているのはなんだか面白いですが、確かにこのように考えることもできそうです。

同様に、テストレベルみたいなものも想像できますね。製造におけるそれぞれの工程でのテストがあり、やはりというか当然ながら、より上流でテストを行なっていけるのが良いのだろうなあと月並みに思ったりします。

## ソフトウェアではないもののテストを考えること

「Test cases for pen」と検索すると、ペンについてのテストを考えた記事が他にもたくさん出てきます。より詳細にテストを考えているサイトもありました。

さらに今回紹介したサイトにも、キーボードやドアのようなソフトウェアではないプロダクトから、病院の経営（！）のような物ですらないものについて書かれた簡単なテストが紹介されていて面白いです。

日本でも「政策」に対するテストを考える、というのをやってみた方がおられますね。

https://nihonbuson.hatenadiary.jp/entry/2023/12/19/090000

政府が昨年12月11日に発表した「こども未来戦略」案に対して、要件定義のレビューからテスト分析、テスト設計までを行うなど、今回紹介した記事より詳細なレベルでテストを考えていて大変面白かったので、こちらの記事も大変おすすめです。

---

「ソフトウェアテスト」という専門職、あるいは専門技術に馴染みがあると、ついついその範疇でものを考えてしまいます。

しかし「テスト」という言葉だけを聞けば、想像するのはどちらかというと、学校での学力試験なんかだったりします。本来的には、テストというものは何もソフトウェアに限ったものではなく、この世界のあらゆる場所で、さまざまな形で行われていて、もっと普遍的な活動のはずです。

たまには「ソフトウェアの」という冠を外して、「なんらかの価値を、なんらかの方法によって確かめる技術」としてテストのことを考えてみると、より理解が深まって良いなあと思いました。

面接での簡単な試験としても、こうしたお題は確かに良いかもしれませんね。日本でもこうしたお題が出されると良いんじゃないでしょうか。（とかいって、自分が面接でこれを訊かれたら絶対に嫌ですけど）
go test時に"%1 is not a valid Win32 application"とエラーが出る

# 概要

題名通り、ある日 `go test` を実行したら
```
fork/exec C:\Users\XXX\AppData\Local\Temp\go-build2550557918\b001\YYY.test: 
%1 is not a valid Win32 application.
```
とエラーが出て失敗するようになったので、その原因と解消方法を調べた結果をメモした記事です。

蓋を開けると非常にしょうもない＆滅多にない原因なので記事としての価値は微妙ですが、同じような事態に遭遇した方のために書き残しておきます。

# 原因: GOOS、GOARCHの設定がおかしかった
結論から言うと、上記エラーが出る前、Goのクロスコンパイルについて調べたり遊んだりしていて[^1]、
```zsh
go env -w GOOS=linux GOARCH=arm64
```
を実行したまま後始末していなかったのが原因でした[^1-2]。

Goはビルド時の想定OSや`go install`のインストール先など諸々の環境依存の設定を環境変数で管理しており、`go env -w` はその環境変数を編集するためのコマンドです[^2]。

中でも `GOOS` と `GOARCH` は、`go build`時にどのOS・どのアーキテクチャ向けにビルドするかを指定する変数になります。デフォルトでは、Goをインストールした環境に合わせた値になっています。


しかし、今回は上記のコマンドで `GOOS=linux GOARCH=arm64` を設定してしまったので、私のコンピュータのOS(Windows)およびアーキテクチャとの乖離が生じてしまいました。これにより、 `go build` などでビルドされたバイナリは、私のコンピュータ上ではうまく実行できなくなります。

たとえテストでも実行にはコンパイルが必要なので、 `go test` も内側ではビルドを行っています[^3]。その際の生成物もやはりLinux＆ARM64用のものとなっているので、私の環境では動作しません。そのため、上記の `%1 is not a valid Win32 application` エラーが発生したのです。しょーもな。

# 対処: 設定を元に戻す
Go環境変数を元に戻す場合、 `go env -u [環境変数名]` コマンドを用います。
今回の場合なら、
```zsh
go env -u GOOS GOARCH
```
です。これを実行したあとに再度 `go test` を行ったところ、無事テストが実行されました[^4]。めでたしめでたし。


[^1]: 当時主に参考にした記事は「[Go開発環境のバラつきと対策](https://zenn.dev/nobonobo/scraps/2a46c96dbee7b7)」です。
[^1-2]: 原因特定に当たっては [go - Running unit test in golang error: %1 is not a valid win32 application | Stack Overflow](https://stackoverflow.com/questions/57921360/running-unit-test-in-golang-error-1-is-not-a-valid-win32-application) にお世話になりました。
[^2]: [go command - Print Go environment information](https://pkg.go.dev/cmd/go#hdr-Print_Go_environment_information)
[^3]: 余談ですが、大規模なソフトウェアで `go test` の実行が時々やたら遅くなるのは、このコンパイルによる部分が大きいようです。そのため、2回目以降の実行は速くなります。
[^4]: テスト自体は失敗でしたが。
rebaseしたらnon-fast-forwardになったけどどうすんだっけ

## 経緯
rebaseしたらnon-fast-forwardになったとき、
久しぶりに遭遇した&新しいチームでの作業でちょっと不安になった

## 前提
developブランチからfeatureブランチを切って作業してる
featureブランチでは自分しか作業してない

## 結論
`git push -f origin <ブランチ名>` でいいよ
※それでも不安だったら周りに聞こうね（私は聞きました）
1/20 tecHub（IT交流会）を開催しました！

![tecworks.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3670233/b3704e05-107c-51cc-ed67-6110f942db6f.jpeg)

1/20にtecHubを秋葉原で開催しました。
参加者は約80名となり大盛況でした！

日付：1/20(土)　14:00～16:30
場所：LIFORK秋葉原Ⅱ1階ラウンジ
参加者：79名
内容：IT交流会　

<h3>毎月開催</h3>
tecHubは、3ヶ月に1度は以前と同じくLT(ライトニングトーク)と懇親会の2部制のイベントを開催していましたが、
昨年からtecHubを開催しない月にもIT交流会のみのイベントを開催し、毎月イベントを行っています。毎回80名近くの規模で開催しており人気のイベントとなっております。

IT交流会では、ピザとドリンクを片手に、参加者同士が情報交換や、他の分野の話を聞いたり、キャリアの相談をしたりと、
普段の職場では聞けない話が聞けることで、毎月参加する方も会を重ねるごとに増えています。

<h3>参加者の声</h3>
今回も、参加者は幅広い分野のIT関係者が集まり会社員からフリーランスエンジニア、法人化されている人まで様々な人が参加していました。
幅広い分野と様々なキャリアの人がいることで、転職やフリーランス転向などキャリアアップを考えている人にも今後の働き方をイメージ出来て参加してよかったなど、勉強になったとの声もいただいています。

終始、会話が弾み盛り上がっていました。
さらにIT業界に興味ある他業界の方も参加しており、IT業界の話が聞けて良かったとの声もありました。


<h3>今後について</h3>
tecHubは、今後も毎月イベントを開催していきます。
他にもITエンジニアがより活躍し、理想のキャリアを切り拓けるように新たな取り組みをしていく予定です。

今後もtecHubをよろしくお願いいたします。



<h3>次回の開催</h3>
次回の開催は以下の日程を予定しております。

日時：2/17(土)　14:00～17:00　
場所：ワイム貸会議室 神田 Room7A
内容：2部構成（LT＋懇親会）
詳細とお申込みは以下をご確認ください。

<p><a href=”https://tecworks.connpass.com/event/307156/”>tecHubのお申し込みはこちらから</a></p>
Google Geminiを使用したQ&Aチャットボット

# Google Geminiを使用したQ&Aチャットボット

## 概要
このプロジェクトは、GoogleのGemini AIを活用して、質問に対する回答を生成するQ&Aチャットボットです。Streamlitフレームワークを使用してWebアプリケーションとして実装されており、ユーザーからの入力に基づいてリアルタイムで回答を提供します。

## 特徴
- GoogleのGemini AIによる高品質な回答生成
- Streamlitを使用した直感的なウェブインターフェース
- 環境変数によるAPIキーの安全な管理

## 必要条件
- Python 3
- Streamlit
- Google Generative AI

## インストール方法
1. このリポジトリをクローンまたはダウンロードします。
2. 必要なライブラリをインストールします。`pip install -r requirements.txt`を使用することをお勧めします。
3. `.env`ファイルにGoogle APIキーを設定します。

## 使用方法
1. スクリプトを実行します: `streamlit run app.py`
2. ブラウザで表示されたWebアプリケーションにアクセスします。
3. テキストボックスに質問を入力し、「質問する」ボタンをクリックします。
4. 画面に表示された回答を確認します。

## コード
```python
# Q&Aチャットボット

from dotenv import load_dotenv

load_dotenv()  # .envファイルから環境変数を取得する。

import streamlit as st
import os
import textwrap

import google.generativeai as genai

from IPython.display import display
from IPython.display import Markdown

# テキストをMarkdown形式に変換する関数
def to_markdown(text):
  text = text.replace('•', '  *')
  return Markdown(textwrap.indent(text, '> ', predicate=lambda _: True))

# Google APIキーを環境変数から取得し、genaiを設定する
os.getenv("GOOGLE_API_KEY")
genai.configure(api_key=os.getenv("GOOGLE_API_KEY"))

## OpenAIモデルをロードし、応答を取得する関数
def get_gemini_response(question):
    model = genai.GenerativeModel('gemini-pro')
    response = model.generate_content(question)
    return response.text

## Streamlitアプリを初期化する
st.set_page_config(page_title="Q&Aデモ")
st.header("Google Gemini アプリ")
input=st.text_input("入力: ",key="input")
submit=st.button("質問する")

## 質問するボタンがクリックされた場合
if submit:
    response=get_gemini_response(input)
    st.subheader("応答")
    st.write(response)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3690124/fa0ecd37-846d-fb36-0409-ed2a254edafe.png)
GoogleMapsApiの勉強[1]

# GoogleMapsAPIの勉強
## ジオコーディングとは
**ジオコーディング**は住所を地理座標に変換する処理のこと。
逆の処置も行える(リバースジオコーディング)。

## ジオコーディングリクエスト
* `google.maps.Geocoder`をコード内で使用することにより、Google Maps API ジオコーディングサービスにアクセスできる。
* `Geocoder.geocode()`メソッドによって、ジオコーディングサービスへのリクエストを開始する。また、入力語句を含む `GeocoderRequest`オブジェクトリテラルと、回答の受け取り時に実行するコールバックメソッドを渡す。
* `GeocoderRequest`オブジェクトリテラルのフィールドは以下のようになっている。  
``` 
{
 address: string,
 location: LatLng,
 placeId: string,
 bounds: LatLngBounds,
 componentRestrictions: GeocoderComponentRestrictions,
 region: string
}
```
  
**必須パラメータ** (1つだけ指定する必要がる)
`address` or `location` or `placeId`  

**省略可能パラメータ**
`bounds`、`componentRestrictions`、`region `

## ジオコーディングのレスポンス

> ジオコーディング サービスには、ジオコーダの結果を取得する際に実行されるコールバック メソッドが必要です。このコールバックでは、`results` と `status` のコードをこの順序で保持する 2 つのパラメータを渡す必要があります。

**ジオコーディングの結果**
`GeocoderResult`オブジェクトは、1つのジオコーディングの結果を表す。

```
results[]: {
 types[]: string,
 formatted_address: string,
 address_components[]: {
   short_name: string,
   long_name: string,
   postcode_localities[]: string,
   types[]: string
 },
 partial_match: boolean,
 place_id: string,
 postcode_localities[]: string,
 geometry: {
   location: LatLng,
   location_type: GeocoderLocationType
   viewport: LatLngBounds,
   bounds: LatLngBounds
 }
}
```

[各フィールドの説明はこちらを参照してください。](https://developers.google.com/maps/documentation/javascript/geocoding?hl=ja#GeocodingResponses)

**ステータスコード**
> * `"OK"`は、エラーが発生せず、住所が正常に解析され、少なくとも 1 件のジオコードが返されたことを示します。
> * `"ZERO_RESULTS"` は、検索は成功したものの結果が返されなかったことを示します。これは、実在しない address がジオコーダに渡された場合に発生することがあります。
> * `"OVER_QUERY_LIMIT"` はリクエストが割り当て量を超えていることを示します。
> * `"REQUEST_DENIED"` はリクエストが拒否されたことを示します。ウェブページではジオコーダを使用できません。
> * `"INVALID_REQUEST"` は一般的に、クエリ（address、components、latlng）が不足していることを示します。
> * `"UNKNOWN_ERROR"` はサーバーエラーでリクエストが処理できなかったことを示します。再度リクエストすると、成功する可能性があります。
> * `"ERROR"` は、リクエストがタイムアウトしたか、Google サーバーへの接続中に問題が発生したことを示します。再度リクエストすると、成功する可能性があります。

## 公式ドキュメント
https://developers.google.com/maps/documentation/javascript/geocoding?hl=ja
【DevConainer】開発コンテナー構成 '.devcontainer/devcontainer.json' ファイルは既に存在します。

かなりはまったので残しておく。

DevContainerでプロジェクトを開こうとしたときに、「開発コンテナー構成 '.devcontainer/devcontainer.json' ファイルは既に存在します。」というメッセージが表示されて、DevContainerが立ち上がらない問題。

![スクリーンショット 2024-02-01 212020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92383/2a870bf9-5534-40b4-c6aa-7f06b14c2591.png)


# 原因

docker-compose.ymlのバージョンに対して、docker-composeのバージョンが古かった

以下のようなログが出力されていたので、docker-composeのバージョンを調べてみると `1.6.2` だった

>Version in "/home/ec2-user/terraform-tutorial/.devcontainer/docker-compose.yml" is unsupported. Either specify a version of "2" (or "2.0") and place your service definitions under the `services` key, or omit the `version` key and place your service definitions at the root of the file to use version 1.

```bash
$ docker-compose -v
docker-compose version 1.6.2, build 4d72027
```
一方、[devcontainer.json](https://github.com/ng3rdstmadgke/terraform-tutorial/blob/main/.devcontainer/devcontainer.json) の `dockerComposeFile` に設定されている[docker-compose.yml](https://github.com/ng3rdstmadgke/terraform-tutorial/blob/main/.devcontainer/docker-compose.yml)のバージョンは `3.2` だった。

```.devcontainer/docker-compose.yml
version: '3.2'
services:
# ... 略 ...
```

# 対応

既存のdocker-composeの削除

```bash
# 既存のdocker-composeを削除
$ type docker-compose
docker-compose is /usr/local/bin/docker-compose

# docker-composeを削除
$ sudo rm -f /usr/local/bin/docker-compose
```



docker-composeの再インストール

https://docs.docker.com/compose/install/standalone/

```bash
$ sudo curl -SL https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
$ sudo chmod 755 /usr/local/bin/docker-compose

$ docker-compose --version
Docker Compose version v2.24.5
```

`Open Folder in Container` で開きなおしたところうまくいった。

![スクリーンショット 2024-02-01 211955.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92383/1b183181-bd78-55e6-d278-293a414935fa.png)
【Xcode】関数にドキュメントコメントを追加したい時のショートカット

# はじめに
記事のネタないかなと思って、Editorを見ていたら、「Add Documentation」というものを見つけました。
今までドキュメントコメントを自分で書いていたのでめっちゃ使えそうです。

![スクリーンショット 2024-02-01 21.34.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/7fe396fe-a6b2-7f23-700e-b387c71b8107.png)

ショートカットって他人から教わらないとなかなか知る機会がないですよね
こんなのあったんだって言うのが割とあります

# こんな感じ
引数と戻り値があることを考慮して、
ParametersとReturnsも追加してくれるなんて優しい！
![画面収録-2024-02-01-21.39.22_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/e3d6b0ed-a58d-682c-ec26-bf4e02079f78.gif)

ちなみに、引数がなかったり、戻り値がなかったらドキュメントコメントも省略してくれます。
![画面収録-2024-02-01-21.41.11.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/bcaaa53c-9391-2f3d-52a5-2043de7fbb01.gif)

![画面収録-2024-02-01-21.43.19.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/b7813458-300e-7ebb-8add-605c83cefda0.gif)

![画面収録-2024-02-01-21.43.53.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/3f3cebcd-c2df-ef28-8014-b9d160e0d286.gif)

# やりかた
option + command + /

# おわり
こんな便利な技を今まで知らなかったなんて、、、
Tableau DesktopからPower BI セマンティックモデルへの接続

業務で調査・検証した内容ですが、地味に需要があるとのことなので投稿します。

## 前提条件
- ユーザーライセンス：Power BI Pro
- Power BI ワークスペース設定：Microsoft Fabric試用版（Power BI Premium）

## 手順
#### **1. 接続したいセマンティックモデルが存在するPower BI ワークスペースを開き、「…」から「ワークスペースの設定」を選択します。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478068/6fe82b34-0bf0-0f2c-9fb9-ad08cc344d55.png)

#### **2. 「プレミアム」の「ワークスペース接続」にある文字列をコピーします。**

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478068/2dceccc0-c2b3-014e-fc7d-b9c7c60be044.png">

#### **3. Tableau Desktop を開き、新しい接続先として「Microsoft SQL Server Analysis Services」選択します。**

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478068/1aecf178-721d-f160-3ad7-f0ddfab038ab.png">

#### **4. サーバー名（手順3でコピーした文字列）・ユーザー名（メールドレス）・パスワードを入力してサインインをクリックします。**

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478068/dd3e7c9a-c071-6650-43c4-f9995ad62a7b.png">

「Windows認証を使用する」でサインインすると、エラーメッセージ↓が表示される場合は、「特定のユーザー名とパスワードを使用」でサインインを行う。

<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478068/67b2962f-712d-b4bb-b0aa-18592ffef5b8.png">

#### **5．データベース・セマンティックモデルの一覧が表示されます。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3478068/52a438dc-9ea8-17ab-4ea4-32f5be7aeeb9.png)
Googleワークスペース(Gmail)からExchangeOnlineに自動でデータ移行をした

## ・嗚呼、面倒だ。
新規企業の立ち上げに伴って筆者がシステム構築を任されたのだが、設立前は既存からの脱却を掲げてGoogleワークスペースが猛烈に推されたものの、いざ導入してみるととても使いづらかった。
それこそ運用でどうにかなるレベルじゃないぐらい使いづらかった。

こうして不満が一瞬にして大爆発したのでM365に戻すこととなったが、メールが既にGmailに流れ込んでいたため、移行作業が必要となった。

移行作業するにあたってExchange管理センターを眺めていたところ、自動化で移行してくれるようなオプションがあったがそれを解説する記事がなんと翻訳ガバガバのDocsしか無かった。

じゃあQiitaに書くしかないな。

## ・前提条件
・M365管理センターとAzurePortalでドメインの所有権確認が完了していること
・ExchangeOnlineを含むライセンスが購入済みであること
・Googleワークスペース・M365共に管理者であること
**・GoogleCloudにて利用規約の同意が完了していること😡😡😡**

:::note warn
利用規約同意が済んでないと設定時にエラーが出ます（1敗）
:::

## ・レッツ移行
### ・Exchange管理センターへGO
[Exchange管理センター>移行バッチ](https://admin.exchange.microsoft.com/#/migrationbatch)に行こう。
ちなみにこのリンクはちゃんと移行バッチ画面が開くリンクにしているぞ☆

## ・さあ設定
#### ・素敵な名前をつけなきゃ
上部の「移行バッチの追加」をクリックし、名前を入力し次に行く。
↓
次に、移行の種類を種類を「Google Workspace(Gmail)からの移行」にして次へ

#### ・悪名高きアレ
Google Workspace の移行の前提条件が開くので開始ボタンを押し、Googleへのログインを行う。
2回ログイン画面が出る場合もあるので忘れずに。

:::note warn
ポップアップブロックでログイン画面が出ないときもあるから気を付けてね
:::

うまくいけば完了と共にjsonファイルのダウンロードが始まるので忘れずにダウンロードする。

:::note alert
**GoogleCloudで利用規約の同意を忘れていませんか？忘れるとここでエラーが出ますよ！**
:::

#### ・忘れることなかれ、権限設定
jsonファイルをダウンロード後、次に行きたくなる気持ちは十分にわかるが**次に進まず**新たに表示されたクライアントIDとリソースをコピーし、リンクボタンがあるのでそこをクリックする。

クリックすると、ドメイン全体の委任画面が表示されるので新しく追加をクリックし、クライアントIDとOauthのスコープにそれぞれクライアントIDとリソースをそのまま入力して承認を押す。

#### ・Exchange管理センターセンターへカムバック
権限設定が完了したら、管理センターへ戻って「次へ」をクリック後、「新しい移行エンドポイントの作成する」を選択し「次へ」を押す。

#### ・移行エンドポイントへ行こう(激うまギャグ)
移行エンドポイントの作成が始まるので「名前」を入れて「次へ」を押す。

#### ・伏線回収ポイント
表示された画面に「移行対象のメールアドレス」（管理者IDがオススメ）を入力し、**さっきダウンロードしたJSONファイルをアップロードする。**(失くしたって？じゃあ最初からやり直し)

:::note warn
ドメイン全体の委任設定を忘れるとここでエラーが出る。
:::
問題なく完了したら次へをクリック。

#### ・CSVって一から作るとめんどくさいよね
メールアドレス取り込み画面が表示されるのでCSVをアップロードする。

:::note info
**え？そんなもの無いって？わかるよカ〇ジ君…**
Excel開いて1行目に「EmailAddress」を書いて、その下からずーっと行方向にメールアドレスを羅列して最後にCSVで保存すればOK

もしくはGoogleワークスペースから出力したメールアドレスCSVにあるアドレス列の1行目を「EmailAddress」に変更してアップロード後、アップロード画面にある「不明な列を許可する」にチェックを入れてもOK
:::

#### ・ラストスパート
「ターゲット配信ドメイン」を記入して、移行対象にチェックを入れて次へ

:::note info
**裏技**
既定ではGoogleワークスペースからExchangeOnlineへの移行時はメール ルーティング用のサブドメインをGoogleワークスペース側に設定する必要があり、「ターゲット配信ドメイン」にはそれを入れなければならない。
参考リンク：[Exchange Onlineでの Google ワークスペースの移行の前提条件](https://learn.microsoft.com/ja-jp/exchange/mailbox-migration/google-workspace-migration-prerequisites)

だがこれはダウンタイムを無くして移行するためのお作法であり、ぶっちゃけダウンタイムを用意して**バツンと**切り替えることが許容できるのであればそれを設定せず、ドメインを「ターゲット配信ドメイン」にぶっこんでもぶっちゃけ動く。
:::

最後に、バッチのスケジュールを決めて実行後見守ればOK

## ・無事終わったわけだが、心に残るものは
こうして移行が無事完了を迎えた。
正直ちゃんとニーズを検証してユーザの要望を聞くというインフラの基礎の基礎を放り出した結果このような記事がこの世界に生まれてしまったわけである。

Googleワークスペースの設定を死に物狂いで終わらせて、いざ！という門出から一転してどん底に叩き落され、心あらず虚無中でM365を構築する筆者にあるものは・・・
>何なんすかねこれ
>プロデューサーさんは何だと思います？
>
>-アイドルマスター シャイニーカラーズ　芹沢あさひ 朝コミュ
CotEditor+AppleScriptで青空文庫のテキストからルビを削除する

## 青空文庫って
「青空文庫は、誰にでもアクセスできる自由な電子本を、図書館のようにインターネット上に集めようとする活動です。」（本家のサイトから）
ここでは、作品をブラウザで直接閲覧できるほか、検索・読書ができるアプリもあります。
また、作品のデータをテキスト形式（ZIP圧縮）とXHTML形式の2種類がダウンロードできるようになっています。

## ルビ付きが多い
公開されている作品には、読み仮名（ルビ）が付いていて、ブラウザや閲覧アプリではふりがなが振られて読みやすくなっています。
ZIP形式でダウンロードして解凍すると、通常のテキストファイルとして利用できて、色々と応用が効きます

## ルビが邪魔?
ルビは自分で読むときは良いのですが、読み上げソフトなどで読ませるようにテキスト化する（ZIPファイルを解凍）と、ルビがあると漢字を2回読むあげることになって邪魔になります。（漢字を間違えて読むのは許すとして）

## CotEditor + AppleScript
CotEditorは、Apache License, Version 2.0でライセンスされているオープンソースプログラムです。（macOS用）
　[CotEditor Project](https://github.com/coteditor) → github
 　リンク先のサイトを見ると、このエディターを詳しく知ることができます。特徴的なのは、AppleScript に対応していて、色々と操作の自動化などができることです。
この機能を使って、一瞬で青空文庫のテキストファイルからルビを削除できるようにしました。

## AppleScript のソース
前置きが長くなりましたが、具体的にはルビを削除するAppleScriptを書いて、CotEditor のスクリプトフォルダに置けば、スクリプトメニューからサッと実行ができてしまいます。
では、ソースコードです
```applescript:Ruby_Del.scpt
-- 青空文庫のテキストファイルから、ふりがな（ルビ）を削除する AppleScript
-- ルビは《ふりがな》　のようにやまかっこで囲まれている部分。
-- written by YNOMURA.COM
tell application "CotEditor"
	if not (exists front document) then return
	tell front document
		replace for "《.*?》" to "" with RE and all
	end tell

end tell
```

上記のコードをスクリプトエディタにコピぺして、分かりやすいところに保存します。保存したファイルを CotEditor のスクリプトメニューから「スクリプトフォルダを開く」で表示されるところに複製します。
これですぐに実行できるようになります。ほんの一瞬で終わってしまいます。

## ちょっと解説
このスクリプトでは、CotEditor で開いているテキストに対して検索・置き換えを指示しています。検索には「正規表現」で、「《」で始まって「》」で終わる文字列を空文字で置き換えるという指定をしています。（階層構造にななっていないので簡略化しています）

## 参考になれば
CotEditorをご利用で、AppleScriptによる自動化ができればと思っている方の参考になればと思います。