OmniAICreator/Qiita-1.07M huggingface dataset:
Android の WebView をコード上でズームイン/ズームアウトする

Android の WebView におけるズーム実装の情報が少なかったのでメモ。

# ズームインする

表示されている WebView の中心に向かって段階的にズームインする。

```kotlin
WebView.zoomIn()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227196/47654cfc-70b4-1582-75f0-ee35cfa2b188.gif" width="300px">

# ズームアウトする

段階的にズームアウトする。

```kotlin
WebView.zoomOut()
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227196/874803ab-1b1a-9946-5424-cd85102d2565.gif" width="300px">

# 可能な限りズームインする

```kotlin
WebView.zoomBy(100f)
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227196/9eedb264-0299-3238-b35b-ed62506b3b1a.gif" width="300px">

# 初期状態までズームアウトする

```kotlin
// 0.01f より大きな値を設定する必要があるため、0.011f を指定
WebView.zoomBy(0.011f)
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227196/16acddb3-fb87-089b-3a5b-ce2a554a647c.gif" width="300px">

# API リファレンス

https://developer.android.com/reference/android/webkit/WebView#zoomBy(float)
ReactプロジェクトでAPIモックサーバ「MSW」を使ってみた

開発中に外部に接続せずにAPIを叩くためのモックサーバを使ってみました。
なんだか、単体テスト時にも起動させることが出来るみたいで、後々試してみたいと考えています。

今回使ってみたのは「Mock Service Worker」略称「MSW」です。
他に有名なモックサーバの「json-server」と違い、別プロセスで起動の必要がないこと、ネットワークリクエストをインターセプトして設定したデータをレスポンスとして送ることなどが特徴です。

## 導入
プロジェクト配下で以下を実行し、MSWを導入。設定ファイルを作成
```bash
$ npm i msw --save-dev
$ npm install
```

ブラウザでMSWを実行するための設定ファイルを作成
```bash
$ npx msw init public/ --save
```

プロジェクトのsrc配下にmocksディレクトリを作成します。
その中に、browser.ts, handlers.js を作成。

各ファイルの内容は以下です。

browser.tsはmswのインスタンスを生成するためのものです。
```ts:browser.ts
import { setupWorker } from "msw";
import { handlers } from "./handlers";

export const worker = setupWorker(...handlers);

```

handler.jsはリクエストハンドラの設定です。
エンドポイントやメソッド、モックデータなどなど。。。
今回は簡単に
/api-sample にGETメソッドでアクセスしたときに
```json
{
    "id": 1,
    "name": "taro"
}
```
を返す設定です。


```js:handlers.js
import { rest } from "msw";

export const handlers = [
  rest.get("/api-sample", (req, res, ctx) => {
    return res(
      ctx.status(200),
      ctx.json({ id: 10, name: "taro" })
    );
  }),
];

```

API呼び出しを行うコンポーネントを作成します。
本記事では、ApiSample.tsxとして作成します。
```tsx:ApiSample.tsx
import { FC } from "react";
import axios from "axios";

type userType = {
  id: number;
  name: string;
};

const ApiSample: FC = () => {

  const callApi = async () => {
    const url = "./api-sample";
    const res = await axios.get(url);
    const sampleData: userType = res.data;
    console.log(sampleData);
  };

  return (
    <>
      <button onClick={callApi}>APIを呼出す</button>
    </>
  );
};

export default ApiSample;

```

最後に、アプリ内でMSWが使えるようにトップコンポーネント（main.tsx）で読み込みます。
開発時のみ読み込むものなので、以下のように記載します。
なお、本記事はviteで構成しているので読み替えを適宜お願いします。
 ```tsx:main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";

if (import.meta.env.DEV) {
  const { worker } = await import("./mocks/browser.ts");
  worker.start();
}

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

```

## 動作確認
最後のお楽しみポイントですね。起動からデータの取得まで出来ていることを願いましょう。
```bash
$ npm run dev
```
開発者ツールを開いた状態でアプリにアクセスして、コンソールを見てみます。
以下のように「[MSW] Mocking enabled.」と出力されていれば、MSWの起動は成功しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779836/97fed060-7d96-9130-dce4-92e8e737e295.png)

今回はボタン押下時にAPIを呼出すようにしたので、ボタンを押してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779836/7aacc412-4ffa-39bc-fbad-85a4dd19e73b.png)

console.log()で出力が出来てますね。
念のため、ネットワークタブも見てみます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779836/47a57933-d899-c771-ef9c-7e92f07ab5dc.png)

いいですね。望んだとおりに動作してくれています。

今回はここまでで終わりとします。

## 最後に
下記画像のように、「Ctrl」+「c」でアプリ停止後に永遠とServiceWorkerのエラーログが流れ続けるのだけはなんとかしたい（模索中）
該当のブラウザでタブを削除or別サイトに遷移する
という方法で一時しのいでいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2779836/55113c41-4afe-bbb9-5dc8-fd454bf576fc.png)
ラストチャンスFree Certification for OCI（2023年9月末まで）

2023年6月に紹介した **Free Certification for OCI**は、いよいよ9月末の終了まで一週間。無料で2回受験できるというメリットしか考えていなかった。
* [Free Certification for OCIが始まったよ（2023年9月末まで）](https://qiita.com/yamada-hakase/items/f7c9553637312d52ed70)

ところが先日Oracle University Communityから **"You earned the Sector 2 badge"** というメールが送られてきた。 **よく読んでみると、1年間有効の無料受験チケットを2023/9/30以降にもらえるらしい。** 
![スクリーンショット 2023-09-22 201644.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266049/02e21960-8204-4c8e-30e4-7843703041c5.png)
あらためて今回のイベントを確認すると、受験チケットがもらえることが書いてある（下図の赤枠部分）。気にしていなかっただけにうれしいお知らせ。
![ociCert01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266049/9164b73e-6d56-d613-dcfe-0abf3f4b14ed.png)
ただし、あと1週間なので準備も簡単ではない。また、受験日枠が少なく予約できない可能性もある。今月中の受験は厳しいだろう。そこでオススメは以下の2つの試験。難易度が低いだけでなく、受験予約不要ですぐに受けられる。Digital Courseのコンプリートは、早送りを駆使すれば1、2時間でなんとかなるだろう。

* [Oracle Cloud Infrastructure 2023 Foundations Associate.](https://mylearn.oracle.com/ou/learning-path/become-an-oci-architect-associate/122195)
* [Oracle Cloud Data Management 2023 Foundations Associate.](https://mylearn.oracle.com/ou/learning-path/become-an-oracle-cloud-data-management-foundations-associate-2023/122101)

**ということで、十分な試験勉強の時間はないけれど、1年間有効のチケットがほしいというかたは上記2つの試験がオススメです。**

<!--
https://community.oracle.com/ou/badge/sector_2
-->
PythonでSP-APIを使って商品の価格を取得する

SP-APIを使う機会があったのでSKUを指定して簡単に商品の価格を取得するコードを残しておきます。

# アクセストークン取得
SP-APIでアクセストークンが必要になりますが、有効期限があって使いまわせないので最初にアクセストークンを取得します。  

```SP-API.py
import requests

callAccessToken():
    refresh_token = 'refresh_token'
    client_id = 'client_id'
    client_secret = 'client_secret'

    url = f'https://api.amazon.com/auth/o2/token?grant_type=refresh_token&refresh_token={refresh_token}&client_id={client_id}&client_secret={client_secret}'

    response = requests.post(url).json()
    token = response['access_token']
    return token
```


`response`を表示させると全部のjsonが確認できます。
アクセストークンは`access_token`キーにあるので`token`に入れておきます。

エラーになった場合はリフレッシュトークンやクライアントIDなどが間違っている可能性があるので確認してください。
それぞれ確認の仕方は以下の投稿で解説されてます。

https://qiita.com/grapefruit1030/items/b109f958de55e04fb554


# 価格を取得する
試しに以下の商品の価格を取得してみます。

![セラー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3563208/19a85385-e95b-6839-1cca-fb9630471cb7.png)

`callAccessToken`で取得したアクセストークンを使って以下のように書いていきます。
`cindition`は中古であれば`Used`、新品であれば`New`を指定します。

```SP-API.py
def getSkuPrice(acceess_token,condition,sku):
    url = f'https://sellingpartnerapi-fe.amazon.com/products/pricing/v0/price?MarketplaceId=A1VC38T7YXB528&Skus={sku}&ItemType=Sku&ItemCondition={condition}'
    #MarketplaceIdは日本であればA1VC38T7YXB528

    headers = {
        'x-amz-access-token':access_token,
        'content-type':'application/x-www-form-urlencoded'
    }

    response = requests.get(url,headers=headers).json()
    print (response)

condition = 'Used'
sku = 'E4CN0-SI-D230923-EK010-0080'

getSkuPrice(access_token,condition,sku)
```

### 結果
色々ありますが、`Product`=>`Udentifers`=>`MarketplaceASIN`=>`Offers`=>`BuyingPrice`=>`ListingPrice`=>`Amount`に金額が載っています（一番下）。


![レスポンス.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3563208/64f13ec1-9c24-a253-7ace-e15f9d82259e.png)

# まとめ
複雑なコードを書くことなく簡単にSP-APIを使うことができました。
SKUの部分をASINで試したら価格が出てこなかったので詳しい方いらっしゃいましたら教えていただきたいです。
hEXルーター：EoIP over IPsec

# hEXルーターでEoIP over IPsec
「[hEXルーターでEoIP(L2TPライク)](https://qiita.com/infinite1oop/items/d1f05732a984243d09f1)」の続き。IPsec上でEoIP(Ethernet over IPsec)を実現したときのメモ。

## ネットワークトポロジー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/9d29586a-aaeb-b04c-bf8f-d8adb65f5b89.png)
両端が同一ネットワーク、ルーター間がIPsec化されており、その上にEoIPが位置する。

## 設定
以下、Router1における設定である。

### EoIP
#### IP Address
![IPaddressのコピー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/33e05fea-7a4f-a3ec-92df-c108193b1365.jpeg)
WAN側（Port1）のIPアドレスを設定する（10.1.1.1/24）。

#### Brdige
![Bridgeのコピー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/71dd0407-60be-e181-90a2-9f54e806a49d.jpeg)
新しく”bridge1”という名称のBridgeを作成。

#### Ports
![Portsのコピー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/68c92754-1c21-2048-3597-be45bc4f5342.jpeg)
Port5(ether5、末端のPCを接続するポート)と後述するEoIPインターフェース（eoip-tunnel1）とを、”bridge1”とに所属させる。

#### EoIP
![EoIP1のコピー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/e8925861-41a1-578f-cf9e-d15524872346.jpeg)
”eoip-tunnel1”という、EoIPインターフェースを作成。

![EoIP2のコピー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/a2666c42-492b-5833-8db2-6df32b7153f6.jpeg)
自機IPアドレス”10.1.1.1”と相手機IPアドレス”10.1.1.2”、トンネルID”０”、IPsecの事前共有鍵を設定。”Allow Fast Path”のチェックは外す必要があるようだ。

![EoIP0のコピー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/f00139d0-1689-8d64-f5c7-dab67630d4f6.jpeg)
最終的に、EoIPインターフェースは上記となる。

#### Interface
インターフェースの最終形は下記となり、設定完了。
![Interfaceのコピー.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/8d4d587b-7522-92f1-335c-fe81cbdfbbd2.jpeg)

### 参考：IPsec項目に自動的に設定されるもの
上述した設定を行うと、IPsec項目に自動的に設定されるものを記載。基本的に、自機IPアドレス及び相手機IPアドレス関連情報が見える。

#### Policy
![IPsecPolicy.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/d379b38f-a83b-e819-d1d8-46efe4bb6a5a.jpeg)
![IPsecPolicy1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/c0212898-a97e-4b15-c996-7ac148bd06d5.jpeg)
![IPsecPolicy2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/8d01768c-3821-dedf-c4d8-82a612e298fc.jpeg)

#### Peer
![IPsecPeer.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/746d0c3f-58c9-e9c1-bf9c-3c493be4b8bd.jpeg)
![IPsecPeer1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/ae8a0a3e-abcd-93d3-d460-9182859aa8fd.jpeg)

#### RemotePeer
![IPsecRemotePeer.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/5fc6d2ce-50e7-4638-d949-d71a47f58be2.jpeg)
![IPsecRemotePeer1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/5bc3e1f8-e009-b6ca-0708-ca428fe9c169.jpeg)

#### SA
![IPsecInstalledSA.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/3d4fc9b0-f74c-626a-12eb-a02ffa3bdf8e.jpeg)
![IPsecInstalledSA1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634816/1bad51e2-2132-abc1-27fd-2f4777b267a5.jpeg)

### キャプチャデータは略
ESPパケットが流れるだけなので、Wiresharkデータは省略。

EOF
QNAP Dockerの単一コンテナ上で複数のDokuwikiを動かす話

# Docker上の単一コンテナで複数 DokuWikiを動かしたい。
bitnamiがDokuWikiのimageを用意してくれているが(linuxserverも人気があるみたいだがUpできなかった)、1コンテナ1wikiで動くのが基本っぽい。
都合により、複数のWikiを動かしたいが(会社でやっていることの動作テスト)同じapacheやphpを積んだコンテナを複数起動するのもなんか変な感じなので一つのコンテナ上で複数のDokuWikiを動かしてみた。
QNAP上でやったのは何か意図するものがあるわけではなく、私の環境で手っ取り早く実行できる環境がQNAPだっただけです。


# どうしたか
php8.2+apacheなimageからコンテナを作成。そこにDokuWikiを展開して起動する。以上。

# ディレクトリ構造
``` console
qnap@qnap:~/docker/dokuwiki$ tree -L 2
.
├── docker-compose.yml
├── Dockerfile
└── html
    ├── wiki1   ★dokuwiki～～.tgzを展開済のディレクトリ。
    ├── wiki2   ★dokuwiki～～.tgzを展開済のディレクトリ。
    └── dokuwiki-a6b3119b5d16cfdee29a855275c5759f.tgz
```

# docker用ファイル
``` docker-compose.yml
version: '3'
services:
  dokuwiki:
    container_name: 'dokuwiki'
    hostname: 'dokuwiki'
    build:
      context: .
      dockerfile: ./Dockerfile
    ports:
      - "8082:80"
    volumes:
      - ./html:/var/www/html
```

``` Dockerfile
FROM php:8.2.10-apache-bullseye

ENV LANG     ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV TERM     xterm
```
dokuwikiはテキストファイルで直接加工できることが非常にメリットなので、ローカルディレクトリをコンテナにマウントしています。


# dockerコンテナの作成
何も考えずにDockerコンテナ作成&起動します。
``` console
qnap@qnap:~/docker/dokuwiki$ docker compose up -d
[+] Building 41.4s (7/7) FINISHED
~~snip~~
[+] Running 2/2
 ⠿ Network dokuwiki_default  Created      3.3s
 ⠿ Container dokuwiki        Started      3.5s
 
qnap@qnap:~/docker/dokuwiki$ docker ps -a
CONTAINER ID  IMAGE              COMMAND                  CREATED         STATUS         PORTS                 NAMES
2e20002bXXXX  dokuwiki-dokuwiki  "docker-php-entrypoi…"  13 seconds ago  Up 10 seconds  0.0.0.0:8082->80/tcp  dokuwiki
```

# dokuwikiのdownload＆展開

dokuwikiのtarをDLし、`html`ディレクトリに設置後展開、好きなWIKI名にします。
[Dokuwiki Download Page](https://download.dokuwiki.org/)

``` console
qnap@qnap:~/docker/dokuwiki/$ cd html
qnap@qnap:~/docker/dokuwiki/html$ curl -s https://download.dokuwiki.org/out/dokuwiki-a6b3119b5d16cfdee29a855275c5759f.tgz -o dokuwiki-a6b3119b5d16cfdee29a855275c5759f.tgz
qnap@qnap:~/docker/dokuwiki/html$ tar -xf dokuwiki-a6b3119b5d16cfdee29a855275c5759f.tgz
qnap@qnap:~/docker/dokuwiki/html$ mv dokuwiki wiki1
qnap@qnap:~/docker/dokuwiki/html$ cp -r wiki1 wiki2
```

# 起動したら
うまく起動してくれたら、後はdokuwikiの`install.php`をたたけばOk。
`http://<serveaddress>:8082/wiki1/install.php`
`http://<serveaddress>:8082/wiki2/install.php`

::: note info
※もしかしたらパーミッションでなにかエラー出るかも。以下のページなどを参照して適切に処置する。
めんどくさがって`chmod 777 -R *`とかやって重篤なセキュリティ問題を引き起こしても知らない。
[dockerでvolumeをマウントしたときのファイルのowner問題](https://qiita.com/yohm/items/047b2e68d008ebb0f001)
:::

dokuwikiはwebサーバとphpさえ動いていれば起動させるまでのハードルは低いので、専用イメージを使うよりもこちらのほうが汎用性あるかもしれないかな。
dokuwikiのハードルはむしろ起動後のPluginのInstallかもしれん。
店舗別に店長さんの名前を調べられるLINEbot

店舗別に店長さんの名前を調べられる　LINEbotを作りました。スマホですぐ調べることができて便利です。



## 作ったわけ

「〇〇店の店長さんって誰だったかな？」の不意な質問にみんなで「えーっと確か…」200店舗以上の全ての店長さんの名前は、なかなか全ては覚えられません。覚えても異動してまた分からなくなってしまいます。結局一覧表で確認するために今はパソコンを開いて、フォルダを何回も何回もクリックしないと辿り着けません。一覧表をあてにしていますが、Lineですぐに答えてくれたらずいぶん楽なんじゃないかと思って作りました。


## 使用したツール
MAKE
LINE
Googleスプレッドシート
<font color="#FF99FF">SSSAPI</font>

### SSSAPI

<font color="#FF99FF">SSSAPI</font>をつかってGoogleスプレッドシートをAPI化します。

https://sssapi.app/


### MAKE
MAKEのシナリオです。

![Screenshot 2023-09-23 00.14.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534558/e884e9ff-1e92-a0b3-8fff-a22e4445b105.png)



### スプレッドシート
A2の値（店舗名）を送るとB2（店長の名前）とC2の値（電話番号）を返してくれます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534558/b0097413-235a-9e0a-2909-15e59b568a2a.png)









#### これで完成です。
#### ところが・・・・・
試してみるも返って来ません。Send a Reply Messageのモジュールがエラーです。


全く原因が分からずでしたが**なんとここ‼︎**でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3534558/2090e542-97cb-3078-890b-b27dae958f5c.png)


まさかの「filter__店舗__exact」の　_（アンダーバー）が半角のところ全角で入力してました。半角だと思い込んでいたのでなかなか気がつきませんでした。

#### 今度こそ完成。
店長さんの名前の他にお店の電話番号が返ってくるようにしました。出先で電話するときにパソコンを開かなくていいようにです。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">あと少し <a href="https://t.co/fmqFI7mJp6">pic.twitter.com/fmqFI7mJp6</a></p>&mdash; yu-san°C (@yusanC1) <a href="https://twitter.com/yusanC1/status/1705485139789459878?ref_src=twsrc%5Etfw">September 23, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>




## 作ってみて思ったこと
とてもシンプルなLINE botができました。無駄なものがなくて完璧です。が、もっといろんなことが出来たらおもしろいものが出来そうです。ちょっとしたゲームを作っているみたいでした。難しいけど、動いたときは嬉しかったです。

APIって何、参考記事や動画に出てくる言葉も分からず、検索しながら作りました。フォローいただきながら、なんとかひとつできました。そんなレベルですが、ひとつ作ってみるとそれとは違うLINE botを作ってみたい気持ちになります。こういうものをサクサク作るエンジニアってすごいなぁって改めて思いました。


## 反省

理解してないからアウトプットができていません。自分の言葉で記事が書けるようになりたいです。
【備忘録】DjangoでLAN内でサイト公開する方法(Windows)

ローカルで開発しているときに少し手間取ったのでメモ程度に残しておこうと思う。

## settings.py
```python
ALLOWED_HOSTS = ['*']
```

## runserver
```bash
manage.py runserver 0.0.0.0:8000
```

## Windowsファイアウォール
コントロールパネルからWindows Defender Firewallの設定を開き、受信の規則を開く。

python.exeの設定(TCP)を探し、通信がブロックされていないことを確認する。

ブロックされている場合は、右クリックしてプロパティを開き、通信を許可する。

## まとめ
上記の設定をしたところ、僕の環境ではローカルからアクセスできることを確認できました。
[iOS] Localizable.strings からString catarogsに移行する

# はじめに
`Localizable.strings`使って多言語対応していたものをXcode15から使える`String catarogs`に移行してみました。

https://developer.apple.com/documentation/Xcode/localizing-and-varying-text-with-a-string-catalog


# 環境
Xcode 15.0

# 内容

まずは既存の`Localizable.strings`を`Migrate to String Catalog`していきます

<img width="500" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/b91bd43b-67d5-193b-e60e-32831f24a615.png">

`Localizable.strings`を選択してMigrate

<img width="300" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/167b77a2-10a7-fb23-4b5b-f115641f4e1f.png">

`Localizable.strings`が`Localizable.xcstrings`に変わりました
とても簡単
コメントは修正する必要がありそうですが、各行にコメント書いていたら綺麗に入りそうです

<img width="600" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/c6bec67a-0db4-63eb-5210-ea9ae39b384d.png">

文字表示部分の実装を変更します

もともとR.Swiftを使っていました

iOS15.0以上で使える`String(localized:)`を使います

```diff_swift
    var headerTitle: String {
        switch self {
        case .gameModeSection:
-           return R.string.localizable.mode()
+           return String(localized: "Mode")
        case .gameResultSection:
-           return R.string.localizable.gameResult()
+           return String(localized: "Game Result")
        case .courtSection:
-           return R.string.localizable.court()
+           return String(localized: "Court")
        case .pairingSection:
-           return R.string.localizable.pairing()
+           return String(localized: "Pairing")
        case .playerSection:
-           return R.string.localizable.player()
+           return String(localized: "Player")
        }
    }
```

SwiftUIの場合はそのまま使えますね

```swift
// SwiftUI localizable text.
Text("Welcome")
```

iOS15.0未満をサポートしている場合はこれまで通りの方法で
`NSLocalizedString(_:tableName:bundle:value:comment:)`

https://developer.apple.com/documentation/foundation/1418095-nslocalizedstring?changes=_9

String catarogsに移行後の動作確認をします
`Edit Scheme`から
<img width="600" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/85766dcb-d15f-ccdc-7025-808763022918.png">
`Run` -> `Options` -> `App Language`の設定を変えることでローカライズの表示確認ができました
<img width="600" alt="" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/631585/c0c11d94-8144-b6dd-c16b-8b5798749776.png">

無事ローカライズされた文言の表示が確認できました🎉


公式ドキュメントには他にも参考になることが書いてありました

- [プロジェクトへのString catarogsの追加方法](https://developer.apple.com/documentation/Xcode/localizing-and-varying-text-with-a-string-catalog#Add-a-string-catalog-to-your-project)

- [複数形の追加方法](https://developer.apple.com/documentation/Xcode/localizing-and-varying-text-with-a-string-catalog#Add-pluralizations)

# おわりに
`Localizable.strings`から一瞬で`String catarogs`を生成してくれるのはとても楽でありがたかったです

# 参考
https://www.fuwamaki.com/article/494

https://speakerdeck.com/tony1224/string-catalog?slide=20

https://qiita.com/uhooi/items/41ead3ad632dc911a4f9
メディアプレーヤ VLC media player のインストール

「VLC media player」（通常「VLC」として知られています）はオープンソースでマルチプラットフォームのメディアプレーヤーソフトウェアです。様々な動画ファイルや音声ファイル、ネットワークストリームを簡単に再生できるため、初心者から上級者まで幅広く利用されています。ここでは 18 歳の新入社員向けに VLC の Windows 11 へのインストール手順を説明します。

## VLC の目的
VLC は以下のような用途で使われます。

### 多様なファイル形式のサポート
VLC は、多くのビデオとオーディオ形式をサポートしています。これには有名なフォーマットのみならず珍しいフォーマットも含まれます。

### ネットワークストリーム再生
VLC は、HTTP、RTSP、MMS などのプロトコルを通じてのネットワークストリーミング再生をサポートしています。

### ディスク再生
DVD、Audio CD、VCD、Blu-Ray など、さまざまなディスクメディアからの再生もサポートしています。

## VLC のシステム要件
VLC for Windows は以下の環境で動作します

- オペレーティングシステム: Windows XP SP3, Vista, 7, 8, 8.1, 10, 11

ここでは VLC for Windows 3.0.18 を例にして Windows 11 へのインストール方法を紹介します。

## VLC のインストール手順
### ダウンロード
窓の杜の VLC の紹介ページにアクセスし、OS のバージョンに合ったインストーラを「**窓の杜からダウンロード**」します。

[https://forest.watch.impress.co.jp/library/software/vlcmedia_ply/](https://forest.watch.impress.co.jp/library/software/vlcmedia_ply/)

![01_officialsite.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/661e5392-5f94-a9af-6563-b6790f78b875.png)

### インストーラの起動
インストーラをダウンロードしたら、ファイルを実行します

![11_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/deb1b635-19d8-0fba-44e9-2e9ef16f1278.png)

### ユーザーアカウント制御の確認
ユーザーアカウント制御が表示されたら、「**はい**」をクリックします。
![20_UAC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/b5097e3d-01f0-a9d2-7477-b929bbf92302.png)


### インストーラ言語の選択
インストーラ言語の選択画面が表示されたら、「**OK**」をクリックします。

![21_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/92b51a11-6342-6cb2-1dc0-abd66fb0c1ad.png)

### セットアップ画面の確認
インストーラのセットアップ画面が表示されたら、「**次へ**」をクリックします。

![22_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/20ee1993-7bf3-5240-dac8-b248f2d7cca5.png)

### ライセンスの同意
使用許諾契約の内容を確認して、「**次へ**」をクリックします。

![23_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/bbade89b-e7d9-0d38-ffac-b3f3d10ff5a1.png)

### インストールタイプの選択
インストールタイプの選択画面が表示されたら、「**次へ**」をクリックします。

![24_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/8621ffc1-2491-a2cb-daba-70aaaadeb386.png)

### インストール先フォルダの選択
インストール先フォルダの選択画面が表示されたら、「**次へ**」をクリックします。

![25_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/1f9a1c56-a4b2-659c-a566-a346b43889e0.png)

### インストール終了
ファイルのインストールが完了したら、「**完了**」をクリックします。

![27_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/00d510b1-eeba-92fb-bb23-61a4bc5e5a29.png)

## 初回起動時の設定
VLC の初回起動時にネットワークアクセスポリシーが表示されます。「**メタデータのネットワークアクセスを許可**」と「**定期的に VLC のアップデートを確認**」のチェックを外し、「**続ける**」をクリックします。

![31_tool.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/8141e417-0514-2608-1e9d-78f4f9e80464.png)

VLC のメイン画面が表示されたら、インストール作業の完了です。

![32_tool.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/839dd969-d064-deed-7da7-0858b5d6bb5c.png)
JavaScriptで配列をチャンク分割

```javascript
const chunks = (a, size) =>
    Array.from(
        {length: Math.ceil(a.length / size)},
        (_, i) => a.slice(i * size, i * size + size)
    );

const array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];

console.log(chunks(array, 3));
console.log(chunks(array, 2));
```


forとかwhileとかreduceで条件分岐するのは、なんだろう読みづらいです。個人的な感想です

参考

https://stackoverflow.com/a/11318797/5602117

https://selfnote.work/20210720/programming/javascript-array-chunk/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/435735/5440725e-9c51-b769-1e07-982c61ff4906.png)
Slackの新UIでワークスペース一覧をサイドバーに常に表示したい

# Slackの新UIが来ました
複数のワークスペースを日常的に飛び回って使われる方にとっては、急に他のワークスペース一覧が表示されなくなって、他のワークスペースに「Ctrl+数字」で飛びにくくなった、飛べるけど、なんでこんなことに数字を覚えなきゃいけないんだ、と思っている方向けです。

# ワークスペース一覧を常に表示する方法
Slack上で  **「Ctrl(Commmand⌘) + Shift + S」** で表示されるようになります。それだけです。
PowerShellスクリプトをダブルクリックで実行する設定

# 0. 動機 
PowerShellスクリプトを実行するのにコンソールを起動したり右クリックしたりせず、またバッチファイル経由でもなく、ダブルクリックで即実行できるようにしたいな、と思ってやりました。


# 1. 設定後の環境
* スクリプトのショートカットをダブルクリックするとスクリプトが動作するようになる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/f197d1a8-860a-6be5-4497-54601e3882b6.png)



# 2. 設定方法
* 適当なスクリプトを適当な場所に用意 (今回はSample_r0.ps1を利用)
    * ディレクトリは"C:\Users\ユーザー名\Documents\XX_ForQiita"と考えて下さい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/15f1b09d-ac3d-f935-3b47-510b497ae023.png)

* スクリプトのショートカットを作成 (右クリックから「ショートカットの作成」 or Shift + F10 → s)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/4e8e735b-82ee-a1b2-6578-d36e2f2e7e9a.png)

* ショートカットのプロパティを開く (右クリックから「プロパティ」 or Alt + Enter)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/217c1ed5-d152-1466-ec0f-4741f8903414.png)


* プロパティの「リンク先」の接頭に"PowerShell"と入力し、デフォルトのパスをダブルクォーテーション("")で囲う
    * 例における具体的な文字列：PowerShell "C:\Users\ユーザー名\Documents\ForQiita\Sample_r0.ps1"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/52b98100-ce74-4e0d-1374-5483289f4af1.png)



* ショートカットのアイコンが濃い青色のPowerShellアイコンに変化したら設定完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/da2160dd-8519-7027-9e4f-e9272599814b.png)


# 3. おわりに
基本自分用メモですがどこかの誰かの役に立てば幸いです。Qiitaは編集しやすくていいですね。

私は非IT企業の非IT職種(専攻も非IT)の人間なのですが、PowerShellの件気軽に相談できる人が周り(社内)にいないこともあり、もし「こここうやってやるともっと楽だよ」とか「こういう改善加えてみました」みたいな意見もしあれば是非教えてください！


# 4. 余談

* パスを通してスクリプトをさらに素早く起動したい場合は下記参照

https://qiita.com/_Sasaki_/items/8384ba3eda0aa6fe6547
GA4用語まとめ

## このページは
業務でGA4でのアクセス解析をすることになったので、備忘録を兼ねて用語をまとめる。
## アクセス解析
| 項目 | 種別| 内容|
| -- | --| --- |
|KGI|基本|「重要目標達成指標」組織やプロジェクトの目標とする指標。売上や収益など|
|KPI|基本|「重要業績評価指標」KGIを分解して出す中間目標。PVなど|
|ユーザー数（UU）|指標|同一デバイス同一ブラウザのアクセスを1ユーザーとしたユーザー数。別ID（独自ID、Googleシグナルなど）で紐づけできれば端末横断で集計できる。|
|セッション数|指標|セッション（一連のユーザー行動）の集計。デフォルト30分操作がなければタイムアウトする。|
|ページビュー|指標|ページが表示された回数|
|CVR|指標|コンバージョン（CV）率。購入や資料ダウンロードなど設定したコンバージョンが達成された割合。一般的に分母はセッション数|
|直帰率|指標|従来はセッションに対してページビューが1のみ（何もせず離脱した）割合|
|ARPU|指標|ユーザー当たりの平均収益|
|ARPPU|指標|有料ユーザー当たりの平均収益|
|DAU|指標|1日のアクティブユーザー数|
|WAU|指標|1週間のアクティブユーザー数|
|MAU|指標|1ヶ月間のアクティブユーザー数|
|LTV|指標|顧客の生涯収益。自社との関係開始から終了までのトータルの利益|

## GA4
直感的にわかりづらいものをまとめる
| 項目 | 種別| 内容|
| -- | --| --- |
|アカウント|基本|アナリティクスのアカウント。GAもGoogleアカウントに紐づく|
|プロパティ|基本|アクセス解析を収集・分析するための設定単位。|
|データストリーム|基本|計測対象「ウェブサイト」「iOSアプリ」「Androidアプリ」|
|イベント|基本|ユーザーが起こした行動・操作。GA4ではイベント単位で計測される|
|コンバージョン|基本|イベントに設定する必要がある|
|ユーザー|基本|User-ID（独自ログインID）、Googleシグナル、デバイスIDでユーザー識別（モデル化データの利用も選択可能）|
|新規ユーザー|基本|first_openまたはfirst_visitのイベントが記録されたユニークユーザー|
|オーディエンス|基本|ユーザーをディメンション、指標、イベントに基づいてセグメント化|
|セッション|基本|GA4ではユーザーが(1)アプリ/サイトをフォアグラウンドで開く(2)アクティブなセッションがない(タイムアウトなど)時にページ/スクリーンを表示する、のどちらかの行動をとった際に開始される|
|エンゲージメントセッション|基本|10 秒以上継続したorコンバージョンしたorページビューが2件以上発生したセッション|
|デフォルトチャネルグループ|ディメンション|ルールに基づいて分類されるトラフィックソース。詳細は[こちら](https://support.google.com/analytics/answer/9756891)|
|メディア|ディメンション|ユーザーの訪問（流入経路の分類）。organic、referralなど|
|参照元|ディメンション|メディアの詳細。organicであればgoogleなど流入に至った参照元|
|コンテンツグループ|ディメンション|ページ/スクリーンをカスタムした分類に分類する|
|ランディングページ|ディメンション|セッションの最初のページビューに紐づけられたページ階層とクエリ文字列|
|新規/既存|ディメンション|過去7日間に初めて訪問したユーザーか既存ユーザー。新規か既存の値を返す|
|閲覧開始数|指標|あるページ/スクリーンでのセッションの最初のイベントの発生回数|
|離脱数|指標|あるページ/スクリーンでのセッションの最後のイベントの発生回数|
|表示回数|指標|ユーザーが閲覧したページ/スクリーンの数。同じページ/スクリーンを繰り返し表示しても集計される|
|ARPU|指標|（広告収入合計＋購入による収益＋アプリ内購入による収益＋サブスクリプションによる収益）÷アクティブユーザー数。ユーザー当たりの平均購入収益額は別に存在（購入による収益のみ、ARPPUも購入による収益のみ）。|
|エンゲージメント率|指標|GA4から追加された指標。エンゲージメントのあったセッションの割合|
|直帰率|指標|エンゲージのなかったセッションの割合|
|アクティブユーザー|指標|サイト/アプリにアクセスした個別のユーザー数。エンゲージメントセッションor初訪問orユーザーエンゲージメントが記録されたユーザーの数。|
|ユーザーエンゲージメント|指標|ページ/スクリーンがフォアグラウンドに表示されている時間の長さ(1秒以上続いたとき)|
|平均エンゲージメント時間|指標|ユーザーエンゲージメントの合計時間÷アクティブ ユーザー数|
|LTV|指標|ユーザーの全期間の収益合計|

## memo
* UA -> GA4
  集計軸：セッションのヒット -> ユーザーのイベント
  UAで各種項目に分かれていたログをイベントという形式で取得、イベント名で区別する形に
### 参考
* Google アナリティクスヘルプ

https://support.google.com/analytics/?hl=ja#topic=10737980

* Demos & Tools

https://ga-dev-tools.google/ga4/

* 書籍：1週間でGoogleアナリティクス4の基礎が学べる本

https://book.impress.co.jp/books/1120101144

* できる逆引き Googleアナリティクス Web解析の現場で使える実践ワザ

https://dekiru.net/article/17756/

* 株式会社HAPPY ANALYTICSのGA4ガイドサイト

https://www.ga4.guide/glossary-help/
streamlit helloを実行しようとすると、zsh: command not found: streamlitとでてしまう件

会社でpythonを使った開発を始めようということで、streamlitを勉強しようと思い、streamlitを起動しようとstreamlit helloしたら、
```
zsh: command not found: streamlit
```
の文字が、、。
パスが通ってないのか。。
調べてみると
pip install streamlit実行時にどこにインストールされたのかが、吐き出されるwarningに出力されているとのこと。
```
WARNING: The script pygmentize is installed in '/Users/ユーザー名/Library/Python/3.8/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
WARNING: The scripts f2py, f2py3 and f2py3.8 are installed in '/Users/ユーザー名/Library/Python/3.8/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
WARNING: The script normalizer is installed in '/Users/ユーザー名/Library/Python/3.8/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
WARNING: The script markdown-it is installed in '/Users/ユーザー名/Library/Python/3.8/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
WARNING: The script jsonschema is installed in '/Users/ユーザー名/Library/Python/3.8/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
WARNING: The script streamlit is installed in '/Users/ユーザー名/Library/Python/3.8/bin' which is not on PATH.
  Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
```
ということで私のPCでのStreamlitのパスの位置は、
/Users/ユーザー名/Library/Python/3.8/bin
ということらしい。（ユーザー名はPCごとに設定された値に合わせてください。）

ではそこのパスを設定しようということで、
```
export PATH=$PATH:/Users/ユーザー名/Library/Python/3.8/bin
```
ここはzshの場合export、bshの場合はechoとなります。
自分のPCの設定に合わせてみて下さい！（私のPCはzshで設定しています。よくここの部分で間違える、、😢）

ようやくここで
```
streamlit hello
```
と打つとstreamlitが起動します！
パスの設定は、初めたての時はよく分からなかったりするので、参考になればと思い書いてみました！

是非ご活用下さい！
JavaScriptで日の出日の入り

以前[MATLABで日の出日の入り](https://qiita.com/yamori813/items/99ce6a8f9b8a0e9be9e8)を計算したのですが、オリジナルのスクリプトはJavaScriptだったのでおうちモニターにつっこんでみました。

Chart.jsはv2.9.3を使っています。

日照計の三日分のグラフに線を引いてみます。

まずsuncalc.jsを読み込むようにします。

いくつか方法があるようですが、現在のコードの変更が少なくて済むpluginで実装してみます。

```javascript
const verticalLinePlugin = {

  afterDatasetsDraw: function (chart, easing) {
      if (myChart.config.lineAtIndex) {
// NAOJ Mitaka
        lat = 35 + 40 / 60 + 30/76 / 60 / 60;
        lon = 139 + 32 / 60 + 16.27 / 60 / 60;
        var times = SunCalc.getTimes(new Date(), lat, lon);
        risetime = times.sunrise.getHours() * 60 + times.sunrise.getMinutes();
        risestr = String(times.sunrise.getHours()) + ":" + String(times.sunrise.getMinutes()).padStart(2, '0');
        settime = times.sunset.getHours() * 60 + times.sunset.getMinutes();
        setstr = String(times.sunset.getHours()) + ":" + String(times.sunset.getMinutes()).padStart(2, '0');
        const scale = myChart.chartArea;
        wid = scale.right - scale.left;
        sunrise = (wid / (23 * 60)) * risetime;
        sunrise += scale.left;
        sunset = (wid / (23 * 60)) * settime;
        sunset += scale.left;
        const context = myChart.chart.ctx;
        // render vertical line
        context.beginPath();
        context.strokeStyle = 'rgba(255, 10, 0, 0.6)';
        context.moveTo(sunrise, scale.top);
        context.lineTo(sunrise, scale.bottom);
        context.stroke();
        context.beginPath();
        context.strokeStyle = 'rgba(255, 10, 0, 0.6)';
        context.moveTo(sunset, scale.top);
        context.lineTo(sunset, scale.bottom);
        context.stroke();
        context.fillStyle = "#ff0000";
        context.textAlign = 'center';
        context.fillText(risestr, sunrise, (scale.bottom - scale.top) / 2 + scale.top);
        context.fillText(setstr, sunset, (scale.bottom - scale.top) / 2 + scale.top);
      }
  }
};

Chart.plugins.register(verticalLinePlugin);
```

を追加してChartのプロパティにlineAtIndex: 1を追加すると以下のようなグラフになります。

![filename(22).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/507f94d0-8a9c-3119-8105-b451794efad6.png)

ちょっと見た目重いので、点線にしてみました。

```
context.setLineDash([2, 2]);
```

![filename(23).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/d160de97-21df-5e23-9a11-215f5b77c301.png)

国立天文台の緯度経度にしてありますが、おうちの緯度経度はNTP ST1/GPSのclockstatの中の$GPRMCセンテンスのログに含まれています。

0時台の平均値が0の位置にあるのはちょっと不自然な気がしたので、調べて調整してみました。barと同じように真ん中でプロットするようにしてみました。

```javascript
options: {
    ...
    scales: {
        xAxes: [{
            gridLines: {
                offsetGridLines: true
            }
        ]}
    }
}
```

![filename(28).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/a50c2b3f-73ba-e941-ad48-bc888caae08d.png)

ここのところ晴れて、直射があたるので、値がおかしいですが、気にしないでください。

ずれたぶん、線を移動してなくてバグってるし、なんか分かりにくいので、こうしてみました。

![filename(29).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/738818c1-2feb-5ca6-3a1b-599c64351ce9.png)
API Managementに対する正常性プローブ設定

Application GatewayやFront DoorのバックエンドにAPI Managementを配置する場合、正常性プローブで監視するパスを指定する必要があります。200応答を返す監視用のAPIを準備する方法も考えられますが、API Managementではデフォルトで正常性確認用のパスが用意されています。

## 正常性確認用のAPI Managementのパス
~~~
 /status-0123456789abcdef 
~~~
こちらのパスをApplication GatewayやFront Doorの正常性プローブで指定します。

本当にこれ正しいパス…？と思わせる文字列が並んでいますが、正規に用意されている正常性確認用のパスです。他の用途で使う名前と絶対被らせないようにあえてこんなパスにしているのかなと想像します。

Application Insightsの可用性テストを使って、API ManagementのURL監視をする目的にもこのパスは使えるかと思います。

## 公式ドキュメント
このパスで検索するとわかりますが、既定で用意されているにもかかわらず、公式ドキュメントには全然載っていません…。記載があるのは以下のドキュメントくらいのようです。


https://learn.microsoft.com/ja-jp/azure/api-management/api-management-howto-integrate-internal-vnet-appgateway

> API Management の ContosoApi ゲートウェイ ドメイン エンドポイントに対するカスタム プローブを作成します。 パス /status-0123456789abcdef は、API Management のすべてのインスタンスでホストされる既定の正常性エンドポイントです。 api.contoso.net をカスタム プローブのホスト名として設定し、TLS/SSL 証明書を使用してセキュリティで保護します。
ファイル圧縮・解凍ソフト 7-Zip のインストール

「7-Zip」は Windows で動作するオープンソースのファイル圧縮・解凍ソフトウェアです。多くの圧縮フォーマットに対応しています。ここでは 18 歳の新入社員向けに 7-Zip のインストール手順を説明します。

## 7-Zip の目的
7-Zip は以下のような特徴があります。

### ファイルの圧縮・解凍
 7-Zip は、大きなファイルや複数のファイルを一つのアーカイブファイルにまとめて圧縮することができます。これにより、ファイルのサイズを小さくして、ストレージスペースの節約や、メール添付などの転送を容易にすることができます。

### 多様なファイル形式の展開
主に Linux で使われる .gz（GZIP 形式）、1985-2000年代に日本で主流だった .lzh（LZH 形式）など多くのファイル形式に対応しています。対応しているファイル形式を以下に示します。

- 圧縮/解凍(展開): 7z, XZ, BZIP2, GZIP, TAR, ZIP, WIM
- 解凍(展開)のみ: APFS, AR, ARJ, CAB, CHM, CPIO, CramFS, DMG, EXT, FAT, GPT, HFS, IHEX, ISO, LZH, LZMA, MBR, MSI, NSIS, NTFS, QCOW2, RAR, RPM, SquashFS, UDF, UEFI, VDI, VHD, VHDX, VMDK, XAR, Z

### 世界 87 言語に対応
87言語のメニュー表示に対応しています。世界の各地域で使える共通ソフトウェアとして導入することができます。

以上の特徴により、7-Zip は個人の日常的な作業からグローバル企業の開発現場まで、幅広い場面で利用されています。

## 7-Zip のシステム要件
7-Zip は以下の環境で動作します。

- オペレーティングシステム: Windows 2000, XP, Vista, 7, 8, 8.1, 10, 11

ここでは 7-Zip Version 23.01 を例にして、Windows 11 へのインストール方法を紹介します。

## 7-Zip のインストール手順
### ダウンロード
窓の杜の 7-Zip の紹介ページにアクセスし、OS のバージョンに合ったインストーラを「**窓の杜からダウンロード**」します。

[https://forest.watch.impress.co.jp/library/software/7zip/](https://forest.watch.impress.co.jp/library/software/7zip/)

![01_officialsite.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/5e0055e8-ab0e-1a2c-b00b-02ae9a9aec73.png)

### インストーラの起動
インストーラをダウンロードしたら、ファイルを実行します

![11_download.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/32f88d39-5eb5-5276-15c0-6ae93a52dff1.png)

### ユーザーアカウント制御の確認
ユーザーアカウント制御が表示されたら、「**はい**」をクリックします。
![19_install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/4f6ccf5b-95a8-d68a-f79d-07ccb6bb1136.png)

### インストール先フォルダの選択
インストール先フォルダの選択画面が表示されたら、、「**Install**」をクリックします。

![21_installer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/88f823ae-4670-22a7-13e4-40219de404df.png)

### インストール終了
インストールが完了したら、「**Close**」をクリックします。  

![22_installer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3160433/fc5c3c76-acf0-13e6-ce2c-fedff2202904.png)

<!--
# 7-Zip の使い方

## ファイル圧縮のやり方

Windowsエクスプローラーで圧縮したいファイルやフォルダを選択します。

選択したファイルやフォルダを右クリックして、コンテキストメニューを開きます。

コンテキストメニューから「7-Zip」を選びます。

「7-Zip」メニューからいくつかの圧縮オプションが表示されます。一般的なオプションは以下の通りです：

- 「圧縮」: 新しいアーカイブを作成し、選択したアイテムをそのアーカイブに追加します。
- 「***.zipに追加」: 新しいZIPアーカイブを作成し、選択したアイテムをそのアーカイブに追加します。

「圧縮」オプションを選んだ場合、圧縮設定ウィンドウが表示されます。ここで以下の設定を行えます：

- アーカイブ: 新しいアーカイブファイルの名前を指定します。
- 圧縮レベル: 通常、最大、無圧縮など、異なる圧縮レベルを選択できます。
- 圧縮方法: 通常、LZMA, LZMA2, PPMdなどの異なる圧縮方法を選ぶことができます。
- 分割: アーカイブをいくつかの部分に分割することも可能です。
- 設定が完了したら、「OK」をクリックします。

設定を完了した後、圧縮プロセスが始まります。圧縮が完了すると、新しいアーカイブファイルが指定した場所に作成されます。

## ファイル展開のやり方

解凍したいアーカイブファイル（.zip, .rar, .7z 等）を右クリックして、コンテキストメニューを開きます。

コンテキストメニューから「7-Zip」を選びます。

「7-Zip」メニューから適切な解凍オプションを選択します。いくつかのオプションがあります：

- 「ここに解凍」: 現在のフォルダにファイルを解凍します。
- 「***に解凍」: 新しいフォルダを作成し、そのフォルダにファイルを解凍します（***はアーカイブファイルの名前です）。
- 「解凍...」: 解凍先のフォルダを指定してファイルを解凍します。

選択したオプションに基づいて、ファイルの解凍が開始されます。解凍が完了したら、指定したディレクトリで解凍されたファイルを見つけることができます。

以上で、7-Zipを使用してファイルを解凍するプロセスは完了です。

7-Zip は多くの高度な機能を提供しており、さまざまなアーカイブ形式で作業するための非常に便利なツールです。
-->
TableViewが空の状態の時、説明画像を表示しよう！

## メリット・実際の活用例
ユーザーエクスペリエンスを高める！　
・初めてユーザーがアプリを使用する際、ユーザーを誘導することができる。
今回は、左画像のようなごほうびリストに追加がない際に、右のような画像を表示する方法を説明します！
 <img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/ed0669d2-d757-dc99-b1c8-4ed469dd4632.jpeg"width=600>

その他の活用例として、インターネット通信を行うアプリで、インターネット接続がうまくいかなかった際にユーザーに知らせる画像を表示させることによく使用されます。
## TableViewが空の状態の時、画像を表示する方法
1.下記のようなTableViewの上の階層にUIViewを追加する!
 <img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/f6865c59-2844-3d26-5081-e78d2df64846.png"width=600>

2.UIViewの追加する場所に注意する！赤枠のように追加！
3.UIViewの制約：UIViewをSafeAreaに対して上下左右全て0で制約する！
 <img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/7066fa59-039d-1524-32ab-4b2620043f6f.png"width=800>

4.先ほど作成したUIViewの中にUIImageViewを追加！
5.UIImageViewの制約：　UIImageViewを先ほど作成したUIViewに対して上下左右全て0で制約する！
 <img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/c2c6ba5d-d007-5cbd-d3c9-f2efdef9134c.png"width=800>

6.表示したいImageを選択する
 <img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/78b647d3-46be-caaa-6b9d-b801788f464b.png"width=800>

7.コントロールを押しながらViewControllerにOutlet制約を行う！
 <img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/cf12b062-bd2e-43d9-0746-4bb56e0ee074.png"width=800>

8.　セルの行数を表す下記のtableView(_:numberOfRowsInSection:)内にコードを追加する！

```swift:RewardViewController
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return RewardList.count
    }

```
9.リストの追加がなく、cellが0件の際に、先ほど作成したemptyViewを表示し、画像を画面に表示する。cellが1件以上追加された場合、emptyViewを非表示にすることで、画像を隠す！

```swift:RewardViewController
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if RewardList.count == 0 {
            emptyView.isHidden = false
        }
        else {
            emptyView.isHidden = true
        }
        return RewardList.count
    }
```
10.TableViewが空の状態の時、画像を表示することに成功しました！
 <img src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/ba50c04e-3612-bae0-c1bb-1bc9e15e0489.png"width=300>

## 参考文献

https://qiita.com/k-yamada-github/items/af0c4bce7a2ed1b47c43

https://tympanus.net/codrops/2013/01/09/designing-for-the-empty-states/

https://medium.com/@mtssonmez/handle-empty-tableview-in-swift-4-ios-11-23635d108409

https://qiita.com/rd0501/items/5aca559df3423335fe40


https://developer.apple.com/documentation/uikit/uitableviewdatasource/1614931-tableview
Windows OSで動く遊戯王モンスターがいた


# 遊戯王とは
遊戯王とはそもそも何かという説明は省きます。  
私は現在30代半ばなので、アニメ、カードド世代でした。
約10年前に出ていたカードが不思議なOSで動いていたようなので、ちょっと見てみました。

## フレーバーテキスト
他のカードゲームでもそうですが、世界観を表す説明文です。
遊戯王の場合は、効果を持っていないモンスターにのみ設けられています。
効果を持っていなくても強いやつもいます。

# 実際のカード
https://www.db.yugioh-card.com/yugiohdb/card_search.action?ope=2&cid=11353&request_locale=ja

フレーバーテキストは以下
```bat:クリフォート・ツール
システムをレプリカモードで起動する準備をしています...
C:¥sophia¥sefiroth.exe 実行中にエラーが発生しました。
次の不明な発行元からのプログラムを実行しようとしています。
C:¥tierra¥qliphoth.exe の実行を許可しますか？ ...[Y]
システムを自律モードで起動します。
```

別のカード

https://www.db.yugioh-card.com/yugiohdb/card_search.action?ope=2&cid=11540&request_locale=ja

フレーバーテキストは以下
```bat:クリフォート・アセンブラ
qliphoth.exe の 0x1i-666 でハンドルされていない例外を確認。
場所 0x00-000 に書き込み中にアクセス違反が発生しました。
このエラーを無視し、続行しますか？ 〈Y/N〉...[ ]
===CARNAGE===
たッgなnトiのoモdる知rヲu悪o善yりナnにoウよyノrりgトnひaノれsワiれワdはo人Gヨ見
イdなoレo知lもfカるeキr生iにf久永gベn食iてrッb取もoラtか木tノn命aベw伸ヲd手nはa彼
```
# 解説と考察
exe実行しており、Cドライブで実行しているので、Windows OSで間違いないでしょう。
直下にフォルダがあるところを見ると、自分で開発していてその場で動かしているのか。

## 不明な発行元
この文言はリアルでもよく目にしますよね。
Windowsで言えばUAC(ユーザーアカウント制御)有効時にプログラム実行を行った場合、そのモジュールに署名がない場合出る場合があります。
## レプリカモード
一般的な用語ではないと思っています。知っている方いらっしゃいましたら教えてください。
レプリケーションであればDBやADなどで使用されます。
別サーバーとデータを同期させておく冗長化です。
## 例外のアドレス
C,C#でよく見かける例外です。
0x00-000にアクセス違反があったと言っていますが、アドレス0のメモリは使わないので、メモリの状態がおかしいというよりはアクセス先のポインタが異常な状態になっているのでしょう。
また、0x1i-666は例外となった実行アドレスのようです。
昨今ほとんどのマシンは64bitマシンとなりアドレスは`0x00007ffdb4a31000`など8byteであらわされます。
32bitでもメモリは`0x67264000`など4byteなので、このアドレスは異常と言えます。
5byteでもない。20bit？
こんなアーキテクチャに対応してるWindowsあるんか

また、16進数ではない`i`が見えますね。おそらく虚数。
現実世界のアドレスには存在しませんが、創作物なのでこれはこれで味があると思います。

## 暴走
プログラムからの確認に対して1枚目では`Y`を入力している形跡がありますが、2枚目では入力する前に実行が行われているように見えます。
また、見慣れない英単語なので調べてみたら結構物騒でした。
**CARNAGE→大虐殺**

### 問題
最後のメッセージは文字化けしてるように見えますが、復元できます。
シーザー暗号ですらないので、子供になったつもりで解いてみてください。
<details><summary>答え</summary><div>
半角文字を削除して、逆から読めばいいらしいです。

```
見よ人は我々の一人のようになり善悪を知る物となった
彼は手を伸べ命の期からも取って食べ永久に生きるかも知れない
```
</div></details>

# 終わり
お読みいただき、ありがとうございます。
次回はサイバース族でも眺めて見ます。

https://www.db.yugioh-card.com/yugiohdb/card_search.action?ope=2&cid=13082&request_locale=ja
Windowsで「パスを通す」ための操作 - by GUI and CUI

# 0. 動機 
PowerShellスクリプトをWindowsキー + Rの「ファイル名を指定して実行」からすぐに起動できるようにしたいな、と思ってやってみました。この記事以外にもパスの通し方について解説している記事はごまんとあるのは承知ですが、一応私の言葉でも書いておこうと思います。
<br>
なお個人PCと社内PCでは設定する変数が違う可能性があるので、設定する際は自分のPCの画面をよく見てトライしてみてください。(私はド素人なので最初この辺ごっちゃになって結構混乱しました)


# 1. Windowsのエディション
* Windows 10 EnterPrise

# 2. 設定後の環境
* 「ファイル名を指定して実行」で追加したパスに保存されているスクリプト名を入力すると実行できるようになります。起動までの工程が少なくて非常に楽です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/f40cd75e-9362-5b1c-71da-08acf410f24a.png)


# 3. GUIでの設定方法 
* 「設定」を開いて(Windowsキー + i)から「システム」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/d716126c-4996-1492-190a-237624d0668a.png)

* 「詳細情報」から「システムの詳細設定」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/7b80237d-23f8-6ca5-ba61-4765e4319415.png)

* 「詳細設定」タブから「環境変数」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/eb52afe2-6e5a-7d7d-3386-6d7410a3d484.png)

* 「ユーザー環境変数」または「システム環境変数」の"Path"(変数名)にスクリプトが保管してあるパスを追加 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/eee1bb0c-bc2b-7c96-0170-586dcea994d4.png)


* 参考：追加したパスの中身
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343332/99eb7382-f40b-dec6-ce51-f5979e2d3baf.png)


# 4. CUIでの設定方法 

* PSコンソールを管理者権限で起動後下記を実行。
```PowerShell
#既存の環境変数Pathを変数に格納 (ユーザー環境変数の場合は最後の"Machine"を"User"に変更)
$oldPath = [System.Environment]::GetEnvironmentVariable("Path", "Machine")

#自分が追加したいパスを追加
$oldPath += 'C:\Users\XX_Path\;'

#既存の環境変数Pathを変数に格納 (システム環境変数の場合は最後の"Machine"を"User"に変更)
[System.Environment]::SetEnvironmentVariable("Path", $oldPath, "Machine")

```


# 5. おわりに
基本自分用メモですがどこかの誰かの役に立てば幸いです。Qiitaは編集しやすくていいですね。

私は非IT企業の非IT職種(専攻も非IT)の人間なのですが、PowerShellの件気軽に相談できる人が周り(社内)にいないこともあり、もし「こここうやってやるともっと楽だよ」とか「こういう改善加えてみました」みたいな意見もしあれば是非教えてください！


# 6. 余談

* PowerShellスクリプトをダブルクリックで実行できるようにしたい場合は下記参照

https://qiita.com/_Sasaki_/items/c8486e58df45333017d7
dockerで立ち上げたrailsがnon-JS module files deprecated.のエラーでブラウザ確認できない

なぜ解決したのかいまいちピンと来てないが、解決できたので、覚書き。

### 前提

バックエンドをrails api、フロントエンドをreactにて WEBアプリ制作中に直面したエラーについて。

rails s でサーバー立ち上げてブラウザ確認しようと思いlocalhostにアクセスすると、アクセスできずにコンソールに以下のエラーが表示されていた。

```
non-JS module files deprecated.
```

### 経緯

そもそも docker compose upをしたときに、

「railsのgemがインストールされてないよ」

というエラーが出ていた。

そのため、docker compose run api bashにて手動でコンテナ内部でbundle installして無理やりrailsサーバーを立ち上げていた。

したがって、gemのインストール周りが原因なのだろうとは予想できていた。

### 実際のymlファイルの中身

docker-compose.ymlファイルの状態を確認してみると以下の状態。

```
version: '3.9'
services:
  db:
    image: postgres
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_HOST_AUTH_METHOD=trust
    volumes:
      - db-postgres:/var/lib/postgresql/data
  api:
    build: ./backend
    command: /bin/sh -c "rm -f /api/tmp/pids/server.pid  && rails s -p 3000 -b '0.0.0.0' "
    ports:
      - 3000:3000
    volumes:
      - ./backend:/api
    depends_on:
      - db
  web:
    build: ./frontend
    tty: true
    command: bash -c " cd app-front && npm install && npm run dev --host "
    ports:
      - 5173:5173
    volumes:
      - ./frontend:/app
volumes:
  db-postgres:
```

確かにbundle installを立ち上げ時にできていないことが判明したので、ここでapiのcommandに

```ruby
bundle install
```

を書き加えた。

この状態でdocker compose upを実行すると、すんなり立ち上がった。

以下、うまくいったymlファイルの全容。

```
version: '3.9'
services:
  db:
    image: postgres
    environment:
      - POSTGRES_PASSWORD=password
      - POSTGRES_HOST_AUTH_METHOD=trust
    volumes:
      - db-postgres:/var/lib/postgresql/data
  api:
    build: ./backend
    command: /bin/sh -c "rm -f /api/tmp/pids/server.pid && bundle install && rails s -p 3000 -b '0.0.0.0' "
    ports:
      - 3000:3000
    volumes:
      - ./backend:/api
    depends_on:
      - db
  web:
    build: ./frontend
    tty: true
    command: bash -c " cd app-front && npm install && npm run dev --host "
    ports:
      - 5173:5173
    volumes:
      - ./frontend:/app
volumes:
  db-postgres:
```

### 所感

gemの読み込み順序とかの問題なのだろうか。

この辺りの理解は乏しいので、今後の課題。
Unity：PhotonでのGameManagerへの変更→Sceneの変更の間には、ディレイが必要っぽい

# はじめに
Photon初心者が躓いた所をメモ。
ボタンをクリックで、GameManagerへの変更→Sceneの変更を行い、
それを同期をさせたかったので以下のようなコードを書いた。

```GManaSceneChange.cs
using Photon.Pun;
using UnityEngine.SceneManagement;

public class GManaSceneChange : MonoBehaviourPunCallbacks
{
    public void Reflesh()
    {
        // 全てのプレイヤーのGameManagerChangeを同期させる
        photonView.RPC("GameManagerChange", RpcTarget.AllViaServer);

        // 全てのプレイヤーのSceneChangeを同期させる
        photonView.RPC("SceneChange", RpcTarget.AllViaServer);
    }

    [PunRPC]
    private void GameManagerChange()
    {
        GameManager.instance.Deck = 16;
        GameManager.instance.Hand = 0;
        GameManager.instance.Dust = 0;
        GameManager.instance.Public = 0;
    }
    [PunRPC]
    private void SceneChange()
    {
        SceneManager.LoadScene("Load");
    }
}

```

# しかし
上記のではSceneChangeは同期するものの、gameManagerChangeは同期されないみたい。
そこで、以下のように処理と処理の間にディレイを挿入することで変更されるようになった。

```GManaSceneChange.cs
using Photon.Pun;
using UnityEngine.SceneManagement;

public class GManaSceneChange : MonoBehaviourPunCallbacks
{
    public void Reflesh()
    {
        StartCoroutine(DelayedSceneChange());
    }
    private IEnumerator DelayedSceneChange()
    {
        // 全てのプレイヤーのGameManagerChangeを同期させる
        photonView.RPC("GameManagerChange", RpcTarget.AllViaServer);

        // 0.3秒待機
        yield return new WaitForSeconds(0.3f);

        // 全てのプレイヤーのSceneChangeを同期させる
        photonView.RPC("SceneChange", RpcTarget.AllViaServer);
    }
    [PunRPC]
    private void GameManagerChange()
    {
        GameManager.instance.Deck = 16;
        GameManager.instance.Hand = 0;
        GameManager.instance.Dust = 0;
        GameManager.instance.Public = 0;
    }
    [PunRPC]
    private void SceneChange()
    {
        SceneManager.LoadScene("Load");
    }
}

```


# たぶん
GameManagerChangeが同期完了していないのに、SceneChange同期が始まってしまうから、
処理がキャンセルされているor同期前のデータを読み込んでしまってる？とかなのかな。
よくわからないけど、時間を挟むといいみたい。
旅館問題を真面目に解いてみる

# 問題設定
いかがわしい旅館問題
[出典](https://twitter.com/duwaaa_uts1/status/1700890370379547018)

男3人、女5人のグループが、女2人、女2人、男女、男2人の4部屋に分かれて泊まる。

係員が無作為に部屋をノックしたところ、女の人の「今手が離せないから、あなたが出て」という声が聞こえた。

男の人が出てくる確率はどれくらいか？

# ありがちな解答
問いの仮定より、各部屋がノックされる確率は同様に確からしく、$1/4$である。
また、各人が、部屋がノックされた時に手が離せないと言う確率は同様に確からしいと仮定し、これを$p_b>0$とする。$ \cdots(\ast) $

女部屋をノックする事象を$R_a, R_b$、同様に男部屋は$R_c$、男女部屋は$R_d$とする。部屋から女の人の声が聞こえる事象を$B_F$とする。男の人、女の人がドアを開ける事象をそれぞれ、$O_M,O_F$とする。
求めるべき確率は、
$$ P(O_M|B_F) = \frac{P(O_M,B_F)}{P(B_F)}$$
である。分母を展開する。各部屋から女の人の声が聞こえる確率は、女の人の人数に比例することに注意して、
```math
\begin{align}
P(B_F) &= \frac{1}{4}\left(P(B_F|R_a) +P(B_F|R_b) +P(B_F|R_c) +P(B_F|R_d) \right) \\
&= \frac{1}{4}\left(2p_b + 2p_b +0 +p_b \right) \qquad \cdots(\star)\\
&= \frac{5p_b}{4}
\end{align}
```
次に分子を展開する。男の人がドアを開ける事象$O_M$は、$R_d$の場合に限って成立するため、
```math
\begin{align}
P(O_M,B_F) &= \frac{1}{4}\left(P(B_F|R_d) \right) \\
&= \frac{p_b}{4}
\end{align}
```
となる。従って求める確率は、
$$ P(O_M|B_F) = \frac{p_b/4}{5p_b/4} = \frac{1}{5}$$

めでたしめでたし。手が離せない確率$p_b$はちゃんと確率的な議論をするために導入しましたが、実際に評価するのも難しいので、これに依存しない結論になって良かったですね。

# 上記解答の問題点
でも果たして、本当にこれで正しい解答なのでしょうか？ずばりまずいのは、式$(\star)$です。たとえば、$p_b=0.7$とすると(みんなめっちゃ忙しい場合…)、女部屋から手が離せないって応答される確率が$P(B_F|R_a)$が1.4になってしまいます。

# 2人とも手が離せない可能性
この原因は、部屋の2人が同時に手が離せない可能性を無視したことによります。2人とも手が離せなくて、結局部屋が開けられないパターンもありえたわけです。
事象$B_F$の定義をより厳密にして、部屋から1人だけ女の人の声が聞こえる事象を$B_F$としましょう。

女部屋が事象$B_F$となる確率は、
$$ P(B_F|R_a) = P(B_F|R_b) = 2p_b(1 - p_b) $$
男女部屋は、
$$ P(B_F|R_d) = p_b(1 - p_b) $$
となる。

実は結局、この場合も
$$ P(O_M|B_F) = \frac{1}{5}$$
となります[^1]。

[^1]: $0<p_b<1$であることを暗に仮定しています。$p_b=1$だと、常に2人とも手が離せないので、どの部屋もドアが開くことはありません。

ちなみに、みなさんお気づきとは思いますが、手が離せない確率$p_b$が十分に小さいことを仮定すれば、部屋の1人が手が離せない事象と、もう1人が手が離せない事象は背反になるので、このような議論は不要になります。

# 男女の忙しさの違いを考慮
上記の解答で、数学的には誤りのない形式にはなったと思います(誤りがあればコメントください…！)。ところで試しに仮定を変えて、男女別に手が離せない確率が違うとしてみましょう(あまり現実的では無いもしれませんが)。男の人が手が離せない確率を$p_M$、女の人を$p_F>0$とします。

各部屋における事象$B_F$の確率を計算すると、女部屋は、
$$ P(B_F|R_a) = P(B_F|R_b) = 2p_F(1 - p_F) $$
男女部屋は、
$$ P(B_F|R_d) = p_F(1 - p_M) $$
となる。

ここから、
$$ P(O_M|B_F) = \frac{1-p_M}{5-4p_F-p_M}$$
が導ける。

極端な例として、$p_F=1, p_M=0$、つまり女の人は常に手が離せなくて、男の人はいつも暇している状況では、なんと
$$ P(O_M|B_F) = \frac{1-p_M}{5-4p_F-p_M} = 1$$
となります。この状況を直感的に説明すると、女部屋をノックしても常に2人とも手が離せないので、女部屋は決して開かない状況となります。女の人の声がして、且つ部屋が開く可能性があるのは、忙しい女の人と暇な男の人がいる男女部屋に限られるので、求める確率が1になるわけです。

ちなみに、$p_F, p_M$をうまく操作すれば、$P(O_M|B_F)$は0から1まで任意の値を取ることができます。

というわけで、この問題は(適切な仮定を置けば)1/5と答えても、1/3と答えても、1/4と答えてもみんな正解になります。めでたしめでたし。

# 結論:より厳密な設問にするためには
上記「ありがちな解答」の範囲では、男の人が手が離せない確率は陽に考えなくてよかったはずです。ただ、部屋に2人いて1人だけが「手が離せない」と発言する事象を厳密に扱う過程で、男の人が手を離せない確率も併せて考える必要が出てきました。更に、やや恣意的ではありますが、女の人と男の人で手の離せない確率に違いを持たせることで、男の人が出てくる確率を任意に操作できることが分かりました。

より厳密に設問を与えるとすれば、やはり仮定$ (\ast) $を問題文で明記する必要があると思います。

# 補足: 1/3説に対する弁護
正解が$\frac{1}{3}$ではないかとする説もあります。状況によっては、それが正解になる可能性も考えられます。
上で見たように、男の人と女の人で手の離せない確率が違う場合もそうですし[^10]、それ以外の可能性もあります。

[^10]: $p_M = 1, p_F = 0.5$でそうなります。

具体的には、女部屋と男女部屋のどちらをノックしても、女の人が手が離せないと言う確率が同様に確からしい場合は、そうなります。

$$ P(B_F|R_a) = P(B_F|R_b) = P(B_F|R_d) $$

例えば、部屋に1着だけ、着るのに時間がかかる着物が置いてあった場合とか…。全然現実的ではないけど。

やはり、仮定$ (\ast) $が明言されていない以上、このような解答も認めざるを得ないのではないかと考えます。