OmniAICreator/Qiita-1.07M huggingface dataset:
全国の熊出没情報をオープンデータ化したい

## はじめに
熊出没情報が都道府県や市区町村単位に別々で公開されていて不便だと感じました。
データの形式も様々で、解析する場合は日付の正規化から始める必要があります。

## 改善策
誰かがイイ感じのシステムを作って無料で公開すれば良いと考えました。
「とりあえずコレでいいや」程度の理由でデータを投入していけば、自動的にデータ形式の統一やオープンデータ化が実現できるという発想です。

具体的には
①熊出没情報を登録できる(自治体職員、一般人、誰ても登録可)
②熊出没情報をマップに表示できる
③熊出没情報をオープンデータとして取得できる
という機能があれば最低限使えるかと思います。

イイ感じかはともかく、システムの無料公開は実現できました。
将来もっと洗練されたシステムを作る方向けに技術的な情報を残します。

https://public.sharp9110.com/

## このシステムで期待できる効果
このサイトに熊出没情報を登録すれば、あとは自動的にマップに掲載され、オープンデータとして公開されます。

オープンデータ利用の一例として、登山者向けのアプリで「自分の周囲の熊出没位置」を表示したり、「最新の目撃情報」をプッシュ通知したりできます。
山や川は都道府県の境になっていることがありますが、都道府県境を跨いでデータを取得できる利点があります。

データの分析にも使えます。
日付の書式が統一されているため、月ごとの出没件数などが簡単に解析できます。

ここでは「熊」に限定して書いていますが、実際には「熊」は区分値のひとつに過ぎません。
区分値を増やせば「道路破損情報の通報」「特定外来生物目撃マップ」「不審者マップ」「消火栓の位置」など、いろいろなマップとして運用できます。
お金が無くて自前のシステムを導入できなかった町や村でも、無料で通報アプリっぽい物を使えるようになります。

## 弱点
無料なので管理者を常駐させるのが難しいです。審査機能は存在しますが、不正な投稿が紛れ込むかもしれません。
投稿した人に報酬を払う機能も実現困難です。(お金や支払先を安全に管理する体制が組めないので)

## 機能
主な機能の紹介と技術的なメモです。
### 投稿画面
#### 概要
座標、日時、コメントを入力して送信します。
画像や動画も添付できるようになっています。
投稿と同時にサーバー側で逆ジオコーディングを行っており、後から都道府県・市区町村で絞り込みできるようになっています。

逆ジオコーディングAPIは、無料・無制限・商用利用可の物が見当たらなかったので、自分で組みました。
国土交通省のサイトから「行政区域データ」をダウンロードして、「NetTopologySuite」等のライブラリで当たり判定を行う仕組みです。市区町村の判定程度であれば意外と簡単に実現できます。

#### 投稿画面 - 座標入力
マップはLeafletと地理院タイルを使っています。
システムを無料で公開する都合上、GoogleMapは選べませんでした。
熊出没情報は田舎の方が多いと思われますが、OpenStreetMapは田舎の建物が十分に表示されないため、地理院タイルの方が便利だと感じました。
スマホから登録する場合に備えて、位置情報APIを使ってスマホの現在地の座標を入力できるようにもしました。

#### オプション入力欄
熊出没情報の投稿時に限り、頭数などの入力欄が出るようにしました。
内部はゴリゴリの固定実装です。
自由に入力欄を組み立てられる仕組みにできれば、さらに汎用性が高くなるかもしれません。

### マップ表示

#### 投稿種別
生物 ＞ 野生動物の出没 ＞ 熊
という階層構造を持たせています。
「野生動物の出没」を選べば、その配下である熊・猪の出没情報を全部まとめてマップ上に表示できます。

#### マップ・マーカー
マーカーの描画には [Leaflet.PixiOverlay](https://github.com/manubb/Leaflet.PixiOverlay "Leaflet.PixiOverlay") を使っています。

熊出没情報は新潟県だけで3000件くらい存在します。
マップ上に万単位のマーカーを配置した状態でスムーズに動かすためには、imgタグを配置するのではなく、canvasに描画した方が良さそうです。

マーカーの表示件数を減らすという点では [Leaflet.markercluster](https://github.com/Leaflet/Leaflet.markercluster "Leaflet.markercluster") も使えます。
ですが、今回は熊出没地点の分布や出没件数の多さを知りたかったので採用しませんでした。

#### 画像
投稿時に添付された画像を表示する機能も理論上は実現できますが、どの程度のアクセスがあるか(料金がかかるか)予測できなかったため、一般向けには公開していません。

#### 検索機能
ちょっとした絞り込み機能を付けました。
ただし、コメントを対象にした文字列検索機能は、どの程度の負荷がかかるか見通せなかったので未実装となっています。
<table>
  <caption>パラメータ</caption>
  <thead>
    <tr>
      <th>名前</th> <th>説明</th>
    </tr>
  </thead>
  <tr>
    <td>prefecture</td>
    <td>都道府県コードを指定します。<br>
    画面タイトルが「○○県 熊出没マップ」となって、各都道府県の専用サイトのように見せかけることができます。
    </td>
  </tr>
  <tr>
    <td>city</td>
    <td>
    デジタル庁が公開している市区町村マスタのコード値(6桁)を指定します。<br>
    画面タイトルが各市区町村専用サイトっぽくなります。<br>
    例えば<br>
    「041017」と指定すると仙台市青葉区だけ。<br>
    「041009」と指定すると仙台市全域が対象になります。<br>
    内部的に市と区の対応表を持っており、市が指定された場合は配下の区全てを対象に検索する仕組みです。
    </td>
  </tr>
  <tr>
    <td>postid</td>
    <td>投稿データのIDを指定して、1件だけマップに表示する機能です。<br>
    行政から注意喚起情報を出すとき、このパラメータが付いたURLを公開すれば、データ1件のみを強調表示できます。
    </td>
  </tr>
</table>

### 管理者機能
投稿内容を審査する機能が存在します。
審査に合格したデータだけがマップ表示・オープンデータ化の対象になります。
審査を行わずに、登録された時点で審査済みにする設定も可能です。

## 最後に
この点全部が熊出没情報なんだぜ！
※これは研究目的で引用した物であり、オープンデータではありません。
![kumamap.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3830884/c7691089-14bb-289d-cc9e-eda2907d5e63.png)
Gitでファイルを管理対象から外す方法（コマンドライン）



Gitで管理されているファイルを管理対象から外したい場合は、次の手順を試してみてください。

## 手順

1. **ファイルをインデックス（ステージングエリア）から削除する:**
    
    ```
    git rm --cached <ファイル名>
    ```
    
    `<ファイル名>`には、管理対象から外したいファイルの名前を入力します。このコマンドを実行すると、ファイルがGitのインデックスから削除されますが、作業ディレクトリにはそのまま残ります。


    
2. **.gitignoreにファイルを追加する（オプション）:**
   これからもそのファイルがGitで管理されないようにするために、`.gitignore`ファイルに追加することをおすすめします。リポジトリのルートにある`.gitignore`ファイルを開くか、新しく作成し、以下のようにファイル名を追加します。
    
    ```
    <ファイル名>
    ```
    
    `<ファイル名>`には、無視したいファイルの名前を入力してください。



3. **変更をコミットする:**
   ファイルをインデックスから削除し、`.gitignore`ファイルを更新した後は、変更をコミットします。
    
    ```
    git commit -m "管理対象から<ファイル名>を外し、.gitignoreを更新"
    ```
    

## 例

例えば、`config.json`というファイルの管理を外したい場合は、以下の手順になります。

1. `config.json`をインデックスから削除します。
    
    ```
    git rm --cached config.json
    ```
    
    
2. `config.json`を`.gitignore`に追加します。
    
    ```
    echo "config.json" >> .gitignore
    ```
    
    
3. 変更をコミットします。
    
    ```
    git commit -m "管理対象からconfig.jsonを外し、.gitignoreを更新"
    ```
    

## 注意事項

- `git rm --cached`コマンドはファイルをインデックスからのみ削除し、作業ディレクトリからは削除しません。ファイルはそのままファイルシステムに残ります。
- ファイルを`.gitignore`に追加することで、将来そのファイルがGitで追跡されないようにすることができます。

もっと詳しく知りたい方は、[Gitのドキュメント](https://git-scm.com/doc)を参考にしてください。
model.to(device)でGPUに転送したモデルが占有するメモリを開放する

## はじめに
Pytorchでニューラルネットワークの学習を行う際には `model.to(device)` メソッドでGPUにモデルを転送する必要があります．単一のモデルを学習するだけならば，学習が終わり次第プログラムも終了し，GPUメモリは開放されます．しかし，複数のモデルを逐次に学習させたい場合，GPUメモリに転送したモデルを削除し，プログラム実行中にGPUメモリの開放をしたい場合があります．本記事では，GPUに転送したモデルを削除し，GPUメモリを開放する方法を説明します．

## 環境
* Python 3.11
* torch==2.3.0+cu118

基本的にGPUを使うことを前提としますが，GPUがない場合にもタスクマネージャーからPCのメモリ使用量を確認することでメモリが開放される様子を見ることができます．

## 方法
必要なライブラリをインポートし，使用するクラスや関数を記述します．
```Python
import torch
import torch.nn as nn
import subprocess

class MLP(nn.Module):
    def __init__(self):
        super().__init__()
        self.linear1 = nn.Linear(20000,20000)
        self.linear2 = nn.Linear(20000,20000)

def print_GPU_usage():
    result = subprocess.run(['nvidia-smi'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    output = result.stdout
    lines = output.split('\n')[8:10]

    for line in lines:
        print(line)
```
ニューラルネットワークモデルをインスタンス化します．
モデルをGPUに転送する前のGPUメモリ使用量は 1846MiB です．
```Python
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
model = MLP()

print("Before transfering the model")
print_GPU_usage()
# Before transfering the model
# |   0  NVIDIA GeForce GTX 1660 Ti   WDDM  | 00000000:0B:00.0  On |                  N/A |
# | 24%   41C    P0              36W / 120W |   1846MiB /  6144MiB |     29%      Default |
```
モデルをGPUに転送した後，GPUメモリ使用量を確認します．
GPUメモリ使用量は 4954 MiB に増加しました．
```Python
model.to(device)

print("The model transferred to GPU.")
print_GPU_usage()
# The model transferred to GPU.
# |   0  NVIDIA GeForce GTX 1660 Ti   WDDM  | 00000000:0B:00.0  On |                  N/A |
# | 25%   43C    P2              39W / 120W |   4954MiB /  6144MiB |     30%      Default |
```
モデルを削除し，GPUメモリ使用量を確認します．
モデルを削除してもGPUメモリを開放していないので，GPUメモリ使用量は 4954MiBのままです．
```Python
del model

print("The model was deleted.")
print_GPU_usage()
# The model was deleted.
# |   0  NVIDIA GeForce GTX 1660 Ti   WDDM  | 00000000:0B:00.0  On |                  N/A |
# | 24%   42C    P2              39W / 120W |   4954MiB /  6144MiB |     19%      Default |
```
GPUメモリを開放すると，GPUメモリ使用量は 1900MiB まで減少します．
```Python
torch.cuda.empty_cache()

print("Freed GPU memory.")
print_GPU_usage()
# Freed GPU memory.
# |   0  NVIDIA GeForce GTX 1660 Ti   WDDM  | 00000000:0B:00.0  On |                  N/A |
# | 24%   42C    P2              39W / 120W |   1900MiB /  6144MiB |     19%      Default |
```

以上の方法により，GPUメモリを開放することができます．
なお，`model` 自体を削除しても `model` に関連する値を変数で保持している場合には，その分のメモリは開放されません．

プログラムをgithub上にアップロードしているので，参考にしてください．
https://github.com/1taroh/free-GPU-memory/tree/main

## 結論
`del model`して，`torch.cuda.empty_cache()`すればよいです．
## 参考文献
公式フォーラムを参考にしています．
* Pytorch Cuda Free GPU Memory https://discuss.pytorch.org/t/pytorch-cuda-free-gpu-memory/74946
* How to clear GPU memory after using model? https://discuss.pytorch.org/t/how-to-clear-gpu-memory-after-using-model/137408
* Delete model from GPU/CPU https://discuss.pytorch.org/t/delete-model-from-gpu-cpu/123287
* How to free the pytorch transformers model from GPU memory https://discuss.pytorch.org/t/how-to-free-the-pytorch-transformers-model-from-gpu-memory/132968
【小ネタ】Copilot in Edgeで入力できないバグの対処法

Copilot in Edge、4回に1回くらい入力できず、イラっとすることがあります。。。（周りにもちょくちょくいるのですがうちの環境だけ？）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629241/c4ddb983-0121-9e73-5f44-299269907b10.png)

### 解決法
そんなときは一度検索バー（EdgeでURL入れるところ）に文字入力してから戻るとうまくいきます！

### 一応フィードバックだしてみた
フィードバックも出しておきました（いいね、よろしくお願いします！）

https://feedbackportal.microsoft.com/feedback/idea/8483755d-0454-ef11-b4ad-0022484d3ecc
【GitHub】コマンドでテンプレートを使用してIssueを作成する方法

以下のコマンドを実行するとテンプレートを使用してIssueを作成できます。

```terminal
gh issue create --template テンプレート名
```

以下の場合には `Sample` という名称のテンプレートを使用してIssueを作成します。

```terminal
gh issue create --template Sample
```

https://cli.github.com/manual/gh_issue_create
【Spring Boot】HTMLでページを表示する際にハマった点

# はじめに
Spring Bootの入門のため最小限の構成でプロジェクトを作ってみましたが、意外なところで引っかかったので初心者の方向けに残しておきます。
事象としては、以下のようにリソースがないというエラーが表示されていました。
```ERROR
There was an unexpected error (type=Not Found, status=404).
No static resource index. 
```

# 前提
最初に作成されるサンプルプロジェクトdemoをHTMLを表示できるようにする

# 結論
結論としては以下の２点が原因でした。

・resourcesディレクトリ以下がビルドされておらず、warファイルに含まれていなかった
・HTMLを読み込むのにテンプレートエンジンが必要であった


# 変更点
以下の２点の修正でうまくいきました。
pom.xmlを修正してresourcesディレクトリ以下をビルドパスに追加
pom.xmlを修正してテンプレートエンジン(今回はThymeleaf)をインストール

修正後の最終的なpom.xmlは以下の通りです。
修正後にMAVEN INSTALLを行えばHTMLがきちんと表示されると思います。
```pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
 
    <!--追記箇所-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    <!--追記箇所-->
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
 
    <build>
   
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>

     <!--追記箇所-->
        <resources>
            <resource>
                <directory>${project.basedir}/src/main/resources</directory>
            </resource>
        </resources>
     <!--追記箇所-->
     
    </build>
 
</project>
```
【備忘録】Ubuntu 24.04 で Python3.12 の Pip を利用する

# 導入
Pythonの仕様の変更とOSのバージョンにより，これまでの `pip3 install xxx` のように Ubuntu で pip コマンドが使えなくなった．これによって，基本的に Python は venv の仮想環境下でのみの実行しか行えなくなっている．例えば，`numpy` をインストールしようと，以下のようにコマンドを実行するとエラーが出力される．
```
$ pip3 install numpy
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.
    
    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3-full installed.
    
    If you wish to install a non-Debian packaged Python application,
    it may be easiest to use pipx install xyz, which will manage a
    virtual environment for you. Make sure you have pipx installed.
    
    See /usr/share/doc/python3.12/README.venv for more information.

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
```

ちなみに，`get-pip.py` https://pip.pypa.io/en/stable/installation/#get-pip-py を使った方法を試しても同様のメッセージが出力される．

# 方法
参考程度にPEP668を軽く眺めてみたところ，実質，仮想環境でしか Python を動かせなくなったと言って良いのかもしれない．後述するが，pipでのパッケージのインストールは仮想環境 venv でしか行えなくなり，さらにインストールしたパッケージは仮想環境上でしか動作しないのである．

以下に示す対処方法は基本的に下に挙げた参考記事のものと同様である．

1. `sudo apt install python3-venv` を実行し，仮想環境のための準備をする
1. `python3 -m venv 仮想環境のパス` で作成．例えば，ホームディレクトリ上に`venv_py`という名前の仮想環境を作成する場合は `python3 -m venv ~/venv_py` となる
1. `source ~/仮想環境のパス/bin/activate` で仮想環境をアクティベートする．上記の例では，`source ~/venv_py/bin/activate` となる
1. すると，Ubuntuのターミナルでは `$` マークの前に仮想環境の名前がカッコ付きで表れる．これが，仮想環境をアクティベートしている状態である
1. あとはいつもどおり，`pip3 install xxx`の形式でほしいパッケージをインストールすれば良い．ただし，導入でも前述したように，pipでインストールしたパッケージを用いたプログラムを実行する場合は仮想環境を立ち上げて置かなければならないことに注意する

私の理解不足でこれまで通り，仮想環境など気にせず python を実行できる方法はあるかもしれない．しかし，PEP668を見る限り，そもそもそのようなプログラムの実行はあまり推奨されていないのであろう．

# ちょっとした tips
以上のように，仮想環境上で python を動かすことが前提になった以上，いちいち terminal を立ち上げ，python3 を使うたびに `source ~/venv_py/bin/activate` などと打つのは面倒だろう．かと言って，`~/.bashrc` に書くのも，python を利用しないときまで，仮想環境が立ち上がるのはなんだか気持ちが悪い．

そこで私がおすすめしたいのが Tmux である．Tmuxであれば，tmux-resurrect を使うことで，ターミナルのセッションを閉じても，さらに言えば，シャットダウンしても残る（参考：https://zenn.dev/nbtk/articles/df7f64e3550b07 ）ので，python 用のセッションを tmux で作成しておけばよいだろう．

## 追記（20240808）
https://blog.jp.square-enix.com/iteng-blog/posts/00043-play-with-the-pep668/ には，仮想環境なしで強制的に pip3 でパッケージをインストールする方法が紹介されていました．

## 参考にした記事
[【私用】ubuntu24.04でpipしてもライブラリインストールできずPEP668になる件](https://qiita.com/AzukiImo/items/5c6dd7e870c9965e41f3)
["This environment is externally managed"のエラーが出たとき、python仮想環境をつかう。](https://note.com/lucky_gerbil210/n/n0de8721c05bc)
Scratch作成日記①さるかに合戦

ローコード・ノーコードでゲームを作成、プログラミングの学習にも使われるScratchを使って、短い時間で簡単なゲームを作ってみる。

・アイディアを思い浮かべてからコンセプトをつくるのに１日以内。
・ゲーム、もしくは何かを再現する事。
・創意工夫がある事
・原則、自力。
・できるだけ、Scratchで提供されている標準のアイコンを使う。

スタジオ
https://scratch.mit.edu/studios/29409443


最近作ったもの
「さるかに合戦」
https://scratch.mit.edu/projects/1051338570/

![スクリーンショット 2024-08-06 234432.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2907821/9639e833-a3c8-be31-5e90-a6b8e8a1f553.jpeg)


タイトルのとおり、昔話の「さるかに合戦」をテーマにしたゲーム。
主人公は、カニ。
サルが投げる柿をよけながら、木をハサミで切ってサルをやっつけるゲーム
ハサミで木を切るのはスペースキー。

１～２時間で作り上げた。

サル、カニ、木は標準のアイコンを使用。
柿のアイコンはなかったので、リンゴのアイコンを色を変えて使用。

カニは横移動のみ
柿はサルの場所からカニに向かって一直線

工夫した点
・ボタン長押しで一瞬でクリアするのを防ぐため、スペースキーを押してから、１秒間の待ち時間を設定
・スペースキーを押すごとに、アイコンを下移動させて木を切っている姿を再現
・木が小さくなると、柿が水平に投げられて回避不能になるのを避けるため、一定以上の高さから投げるように条件文を設定

こんな感じで紹介していきます。
MySQLの中間テーブルにPRIMARY KEYまたはUNIQUE KEYを設定するべきか？

中間テーブルは、データベース設計において多対多の関係を管理するためによく使用されます。中間テーブルには、他の二つのテーブルの主キーを含むことが一般的です。ここでは、中間テーブルにPRIMARY KEYまたはUNIQUE KEYを設定するかどうかについて説明します。

## 中間テーブルの基本構造
多対多の関係を表すために、中間テーブルは以下のような構造を持ちます

```SQL
CREATE TABLE example (
    entity1_id INT,
    entity2_id INT,
    PRIMARY KEY (entity1_id, entity2_id)
);
```
または、PRIMARY KEYを使用せずに、UNIQUE KEYを使用することもできます

```SQL
CREATE TABLE example (
    entity1_id INT,
    entity2_id INT,
    UNIQUE KEY (entity1_id, entity2_id)
);
```
### PRIMARY KEYを使用する利点
**データの一意性の保証:** 中間テーブルにPRIMARY KEYを設定すると、同じ組み合わせのレコードが重複して挿入されることを防ぎます。

**パフォーマンスの向上:** PRIMARY KEYはインデックスを自動的に作成するため、検索や結合操作が高速化されます。

**データ整合性の維持:** PRIMARY KEYを設定することで、データの整合性が保たれます。

### UNIQUE KEYを使用する利点
**重複防止:** UNIQUE KEYを使用することで、重複データの挿入を防ぐことができます。

**柔軟性:** UNIQUE KEYはPRIMARY KEYほどの制約を持たず、他のカラムにPRIMARY KEYを設定したい場合に有用です。

### PRIMARY KEYとUNIQUE KEYの比較

**PRIMARY KEY:** テーブルごとに一つしか設定できません。NULL値を許容しません。
**UNIQUE KEY:** テーブル内に複数設定できます。NULL値を許容します（ただし、NULL値は一意でなければなりません）。
実際の例
例えば、以下のようなユーザーとグループの関係を管理する中間テーブルを考えます。

```SQL
CREATE TABLE user_group (
    user_id INT,
    group_id INT,
    PRIMARY KEY (user_id, group_id)
);
```
または

```SQL
CREATE TABLE user_group (
    user_id INT,
    group_id INT,
    UNIQUE KEY (user_id, group_id)
);
```
## まとめ
中間テーブルにPRIMARY KEYまたはUNIQUE KEYを設定することは、データの一意性と整合性を保つために重要です。一般的にはPRIMARY KEYを使用することが推奨されます。UNIQUE KEYは、より柔軟な設計を必要とする場合に有用です。
AWS SESのサプレッションリストに入るメールアドレスはハードバウンス + 苦情のみ

## 概要

AWSのSESにはサプレッションリストなるものが存在する。これはバウンスしたメールアドレスや、苦情のあったメールアドレスが入るリストである。このリストに入っていると、そのSESを使ってメールを送ろうとしてもSESが自動的に判断してメールが送信されない。
このサプレッションリストに入る条件を勘違いしていたのでエビデンスとともに簡単にまとめる。

## 内容

実はバウンスには種類がある。主に下記の二種類だ。

- ハードバウンス → メールのToのアドレスが無効であり、永続的にメールが届かない状態
- ソフトバウンス → メールのToのアドレスの受信ボックスがいっぱいなどの、一時的にメールが届かない状態

このバウンスのうち、サプレッションリストに入るのは「ハードバウンス」のみである。

自身はソフトバウンスでもサプレッションリストに入ると勘違いしていた。

エビデンス↓

:::note info
>ハードバウンスのみが、アカウントレベルのサプレッションリストに追加されます。

https://docs.aws.amazon.com/ja_jp/ses/latest/dg/sending-email-suppression-list.html
:::

## 参考文献

https://docs.aws.amazon.com/ja_jp/ses/latest/dg/send-email-concepts-process.html#send-email-concepts-process-after-send

https://docs.aws.amazon.com/ja_jp/ses/latest/dg/sending-email-suppression-list.html
成果物を作ろう！【導入編】

学部3年，情報系なのにITさっぱりわからん！
なのにIT業界で就活しています！
なんでだよってまあもちろん理由はありますよ．
でも本題とずれるので割愛．

せっかく夏休み時間あるし，IT業界の就活準備の一環として，
何か成果物を作ってポートフォリオにまとめよう！
と思ったはいいものの……
成果物とは何を作ればいいの？難易度は？どれぐらいかかる？などなど，
わからないことだらけだったので
とりあえず大学の教授に相談してみたところ
GitHubで公開されてるOSSに参加してる人とかすごいんちゃう？
と言われ，頭が？？？となりました．
そんなレべチなこと言われても困ります．
これをレベチと言って困ってていいのだろうか……
多分かなりやばいな……

ピンチすぎてどうにもこうにもならんので
昨今話題のChatGPT君に相談しました．
就活においてこの子が一番頼りになると言っても過言じゃない．
さっそく質問してみたところ，
かなり詳しく回答してくれました！ありがとう！

で，まとめると，
**ゲーム開発とかアプリ開発から手付して，
API開発とか自動化スクリプトとかにチャレンジしたら？**
ということでした．
API開発とか自動化スクリプトとかあんまりわからんけど
まあ，どうにかしましょう！するしかない！

というわけで早速，次回から**unityテトリス編**です．


【蛇足　これまでの経験（大学）】
1年，C言語やり始めた
2年，C言語でゲーム開発
3年，Arduinoマイコンのロボット実験
3年の選択授業でJavaちょっとかじった
高校生までは全然PCとか触れてこなかった．
入学時点ではゲームもプログラミングも興味なし．
まじなんで情報来たんだ．

ちなみにこの記事の話は昨日か一昨日ぐらいの話で，
記録用でQiita始めました．

途中でやめるなよ！自分！
【学習記録　2024-08-06】

## 使用教材
Udemy「超JavaScript完全ガイド 2024」

## 学習内容
### 学習時間：0.5h
- lengthプロパティの仕組み
#### 学習メモ
配列には「疎な配列」と「密な配列」の2種類がある。疎な配列は添字が飛び飛びになっており、密な配列は連番になっている。疎な配列は**飛ばされた値もlengthに含まれる**（重要）。

疎な配列は処理速度が低下するため避けるべし。
```diff_javascript
// 疎な配列
const fruits = ["apple", "banana"];
fruits[2] = 'orange';

// 密な配列  2~9の値はemptyになる。
const fruits = ["apple", "banana"];
fruits[10] = 'orange';
```

delete演算子で要素が削除された場合は**lengthが更新されない**。
内部的にはlengthの値よりも**大きいインデックスにアクセスされたとき**に＋1される。
```diff_javascript
const fruits = ["apple", "banana", 'orange'];
delete fruits[2];
console.log(fruits.length); // 3
```

lengthに直接値を代入したとき、要素数よりlengthが大きい場合は足りない分emptyが追加され、小さい場合は切り捨てられる。自動的にlenghtプロパティの値がインデックスより小さくならないようになる。
```diff_javascript
const fruits = ["apple", "banana", 'orange'];
fruits.lenght = 100; // emptyが97個追加される。
fruits.length = 2;   // orangeが切り捨てられる。
```

## 所感
　delete演算子を使用したときにlengthが更新されないのに衝撃！！
 　結論、lenghtの値がインデックスよりも小さくならないようにするという処理を内部的に行っているのをしれたので配列についてまた一つ詳しく慣れました😊
続：.next()と.nextLine()の違いと、初心者向けのScannerの解説

## 前書き
超初心者向けのScannerの話です。
[.next()と.nextLine()の違いと、初心者向けのScannerの解説](https://qiita.com/hot-mock/items/08894e027cf0f8a0cb23)の記事の続きとなりますので、未読の方はこちらもご参考いただけると嬉しいです。

それでは、前回コメントアウトしていた部分のコメントアウトを外し、実行してみましょう。
▼コード
```
import java.util.Scanner;

public class ScannerNextMethod {
  public static void main(String[] args) {
    Scanner scNext = new Scanner(System.in);
    String a = scNext.next();
    String c = scNext.next();
    String d = scNext.nextLine();

    System.out.println("a:" + a);
    System.out.println("c:" + c);
    System.out.println("d:" + d);

    Scanner scNextLine = new Scanner(System.in);
    String b = scNextLine.nextLine();
    String e = scNextLine.next(); //前回コメントアウトしていた箇所
    System.out.println("b:" + b);
    System.out.println("e:" + e); //前回コメントアウトしていた箇所

    scNext.close();
    scNextLine.close();
  }
}
```

▼出力結果
```
aaaa bbbb cccc 
a:aaaa
c:bbbb
d: cccc
aaaa bbbb cccc
aaaa bbbb cccc
b:aaaa bbbb cccc
e:aaaa
```
▼実際の動き
<blockquote class="twitter-tweet" data-media-max-width="560"><p lang="en" dir="ltr">Java_ScannerMovie <a href="https://t.co/N1vFeyWMKX">pic.twitter.com/N1vFeyWMKX</a></p>&mdash; Mori Miki (@Morizo_Mikkoro) <a href="https://twitter.com/Morizo_Mikkoro/status/1820820688200147055?ref_src=twsrc%5Etfw">August 6, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

個人名を消すモザイクを入れているため、見づらくて申し訳ありません……
**Line15のあと、Line16実行時に入力待ちが発生していることにご注目ください！**
### 詳細説明
```
Scanner scNextLine = new Scanner(System.in);
```
scNextLineにユーザーが入力したものを全スキャンして入れています。
```
String b = scNextLine.nextLine();
```
scNextLineに入っているものを、改行コードまで読み込んで変数bに入れています。
これにより、
```
System.out.println("b:" + b);
```
で改行までの「aaaa bbbb cccc」全てが出力されます。
上記で、scNextLineに入っていた全てを変数bに渡しているため、scNextLineは未読込のものがない状態となっています。
そのため、
```
String e = scNextLine.next(); //前回コメントアウトしていた箇所
```
が実行された際、ユーザーの入力待ちとなります。
このコードは、scNextLineに入っているものを、空白（スペース）まで読み込んで変数eに入れています。そのため、下記の
```
 System.out.println("e:" + e); //前回コメントアウトしていた箇所
```
が実行されたときの出力結果は「e:aaaa」となります。

## まとめ
* Scannerは、コピー機のスキャナー機能のように、指定されたものを全て読み込む
* .next()は空白（スペース）まで、.nextLine()は改行までを読み取る
* 未読込のものがないときは入力待ちとなる

## 後書き
最初はインスタンス生成時にのみ、入力を受け取っているのだと勘違いしていたのですが、実際のところは、読み込むものがないときも入力待ち状態になっていたんですね。
これを理解しないまま、Scannerをリセットするために無闇に.next()や.nextLine()を記述して、Enterを押さないとプログラムが進行しない、なんてことも発生していたので、ようやく謎が解けた気分です。
.next()と.nextLine()の違いについても知ることができたので、色々なコードを書いて、更にJavaへの理解を深めていきます！
【AtCoder】配列要素をスライドさせる方法

## 例題

[044 - Shift and Swapping（★3）](https://atcoder.jp/contests/typical90/tasks/typical90_ar)

### 方法１：先頭の要素番号を記録する

```C++
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
#define rep(i, n) for (int i = 0; i < (n); ++i)

int main() {
  int N, Q; cin >> N >> Q;
  vi A(N); rep(i, N) cin >> A[i];
  int init = 0;
  auto conv_idx = [&](int x) { return (init + x) % N; };
  rep(qi, Q) {
    int t, x, y;
    cin >> t >> x >> y;
    --x; --y;
    if (t == 1) swap(A[conv_idx(x)], A[conv_idx(y)]);
    if (t == 2) init = (init - 1 + N) % N;
    if (t == 3) cout << A[conv_idx(x)] << endl;
  }
}
```

<br>

### 方法２：dequeを使う]

```C++
#include <bits/stdc++.h>
using namespace std;
#define rep(i, n) for (int i = 1; i <= (n); i++)

int main() {
  int N, Q; cin >> N >> Q;
  deque<int> dq;
  rep(i, N) {
    int x; cin >> x;
    dq.push_back(x);
  }
  
  rep(i, Q) {
    int t, x, y;
    cin >> t >> x >> y;
    --x; --y;
    if (t == 1) swap(dq[x], dq[y]);
    if (t == 2) {
      dq.push_front(dq.back());
      dq.pop_back();
    }
    if (t == 3) cout << dq[x] << endl;
  }
}
```
【Angular】Async pipes should not be negated.の解消法

Angular好きなんだけど、シンプルに飽きてきた泉（[@izumin_0401](https://twitter.com/izumin_0401)）です。

# ブログ記事はこちら

https://traveler0401.com/angular-async-pipes-should-not-be-negated/

# 「Async pipes should not be negated.」とは
「Async pipes should not be negated.」は、asyncパイプで判定する際に「構文変やから修正した方がええで」というエラー（というかワーニング）です。

eslintを導入していると以下のようなワーニングが出ます。
```
error  Async pipes should not be negated. Use (observable | async) === (false | null | undefined) to check its value instead  @angular-eslint/template/no-negated-async
```

# 「Async pipes should not be negated.」の原因
僕はダッシュボードを作っている際に遭遇しました。

```html
<mat-sidenav-container class="sidenav-container">
  <mat-sidenav #drawer class="sidenav" fixedInViewport="true"
      [attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
      [mode]="(isHandset$ | async) ? 'over' : 'side'"
      [opened]="!(isHandset$ | async)">
    <mat-toolbar>Menu</mat-toolbar>
    <mat-nav-list>
      <a mat-list-item [routerLink]="['/']">Top</a>
    </mat-nav-list>
  </mat-sidenav>
  <mat-sidenav-content>
    <mat-toolbar color="primary">
      <button
        type="button"
        aria-label="Toggle sidenav"
        mat-icon-button
        (click)="drawer.toggle()"
        *ngIf="isHandset$ | async">
        <mat-icon aria-label="Side nav toggle icon">menu</mat-icon>
      </button>
      <span>schematics-sample</span>
    </mat-toolbar>
    <ng-content></ng-content>
  </mat-sidenav-content>
</mat-sidenav-container>
```
上記のようなコードがあるとして、

```html
[opened]="!(isHandset$ | async)">
```
でワーニングになります。

# 「Async pipes should not be negated.」の解消方法
```html
[opened]="(isHandset$ | async) !== true"
```
上記みたいに書き換えればOK。

# まとめ
eslintは便利なんだけど、たまに「？？」ってなる...

Angularはそもそも情報が少ないですからね...

# 最後に
暇つぶしに[Twitter](https://twitter.com/izumin_0401)と[ブログ](https://traveler0401.com/)もやってるので見てね。
[mapbox]ExpressionでObjectArrayを参照する

mapboxの[Expressions](https://docs.mapbox.com/style-spec/reference/expressions/)で、
```
'paint': {
    'fill-color': '#ff0',
    'fill-outline-color': '#ffF',
    'fill-opacity': 0.5,
}
```
の```'fill-color'```の部分を、直接```'#ffF'```を指定するのではなく、

```
const fillObjs3 = [{"code":"1","fillCol":"#f00"},{"code":"2","fillCol":"#0f0"},{"code":"3","fillCol":"#00f"}];
```

の```fillObjs3[1].fillCol```に置き換えたいとします。

```
    'fill-color': ["to-color",["get","fillCol",["object",["at",1,["literal",fillObjs3]]]]],
```

こうなります。

```["to-color",["get","fillCol",["object",["at",1,["literal",fillObjs3]]]]],```
を内側から分解していくと、


<b>[ "literal",fillObjs3 ]</b> ： `fillObjs3`はObjectArrayです
<b>[ "at",1, </b><I>fillObjs3</I> <b>]</b> : `fillObjs3[1]`を取り出します(={"code":"2","fillCol":"#0f0"})
<b>[ "object", </b><I>fillObjs3[1]</I><b> ]</b> : `fillObjs3[1]`はオブジェクトです
<b>["get","fillCol",</b><I>fillObjs3[1]</I>]</b> : `fillObjs3[1].fillCol`を取り出します(="#0f0")
<b>["to-color",</b><I>fillObjs3[1].fillCol</I><b> ]</b> : `fillObjs3[1].fillCol`(#0f0)は色です

となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59911/7ebdb1df-ef68-a66b-ec8e-551cb647167c.png)

さらに、
```
if( {src[idx].}properties["work"].fillId!=0)
```
の場合は
```
["!=",0,["get","fillId",["object",["get","work"]]]]
```
geojson外のデータmesh1kmWorkを参照する場合、
```
if(mesh1kmWork[idx].fillId!=0)
```
は
```
["!=",0,["get","fillId",["object",["at",["get","idx"],["literal",mesh1kmWork]]]]]
```
のようになります。
Elecom USB Wi-Fiアダプタ(WDC-433DU2HBK)をUbuntuで利用する方法

# はじめに
　[Elecom WDC-433DU2HBK](https://www.elecom.co.jp/products/WDC-433DU2HBK.html)をUbuntuで利用する方法を紹介。下記記事とほぼ同じだが、Arm(Jetson)での方法を記載。

 https://qiita.com/diescake/items/2370dc67a0ed40b089ba

# 動作確認環境
* Ubuntu 22.04 x64
* NVIDIA Jetson AGX Orin
    * Ubuntu 20.04 arm64 (JetPack 5.1.2)

# インストール方法

## 1. ドライバソースのダウンロード

 任意のディレクトリに移動して、[rtl8812au](https://github.com/gnab/rtl8812au/)をクローンして、ダウンロードディレクトリに移動。

例：```/usr/local/src```の場合（以下、```/usr/local/src```にダウンロードした想定で手順を記載）
```bash
cd /usr/local/src
sudo git clone https://github.com/gnab/rtl8812au
cd rtl8812au
```

## 2. MakeFileの編集

### X64(x86_64)の場合
　makeファイルの編集は不要。

### Arm64(aarch64)の場合
　makeファイルをvimなどで編集が必要。

```bash
sudo vi Makefile
```

```makefile:元のファイル
...
CONFIG_PLATFORM_I386_PC = y
CONFIG_PLATFORM_ANDROID_X86 = n
CONFIG_PLATFORM_JB_X86 = n
CONFIG_PLATFORM_ARM_S3C2K4 = n
CONFIG_PLATFORM_ARM_PXA2XX = n
CONFIG_PLATFORM_ARM_S3C6K4 = n
CONFIG_PLATFORM_ARM_RPI = n
CONFIG_PLATFORM_ARM_RPI_64 = n
...
```
x86の設定(```CONFIG_PLATFORM_I386_PC```)を無効(n)にして、
Arm64の設定(```CONFIG_PLATFORM_ARM_RPI_64```)を有効(y)にする。

```makefile:編集後のファイル
...
CONFIG_PLATFORM_I386_PC = n
CONFIG_PLATFORM_ANDROID_X86 = n
CONFIG_PLATFORM_JB_X86 = n
CONFIG_PLATFORM_ARM_S3C2K4 = n
CONFIG_PLATFORM_ARM_PXA2XX = n
CONFIG_PLATFORM_ARM_S3C6K4 = n
CONFIG_PLATFORM_ARM_RPI = n
CONFIG_PLATFORM_ARM_RPI_64 = y
...
```

## 3. Makeとインストール

```bash
sudo make
sudo make install
```

## 4. 動作確認

```bash
sudo insmod 8812au.ko
```

エラーが表示されずに、Wifiが認識されればOK。

# 参考

https://qiita.com/diescake/items/2370dc67a0ed40b089ba

https://github.com/gnab/rtl8812au/
（Code Pen実装）ぐにゃぐにゃ動く背景

お疲れ様です。

早速CodePen実装のため、Qiita様の該当記事を見に行き、やってみました。
昨日投稿した内容をCodePenを使って、どういう結果になったかを表示してみます。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="LYKLebb" data-pen-title="Untitled" data-user="mtzkmy" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/mtzkmy/pen/LYKLebb">
  Untitled</a> by mtzkmy (<a href="https://codepen.io/mtzkmy">@mtzkmy</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
というわけでいかがでしょうか？
投稿前ですのでプレビューでしか状況が把握できておりませんが…

昨日のコードとは違うところがありますが、動きや雰囲気が伝わったらうれしいです:relaxed:
自己紹介

# 自己紹介
初めまして息子と言います。
工業高校に入学し、主にX（旧Twitter）にて
ゲームやPC関連のことを中心につぶやいたりしていましたが、
いつのまにか30歳になったみたいです。。。

私の仕事は自社製品のテストをしており、
（ハードもソフトもしていますが直近はソフト寄り）
産業用通信やSCADAなんかも触ってきました。
本職のシステム屋さんにプログラム技術は敵いませんが、
各社PLCとIOを繋いでテストしています。

# qiitaを始めたきっかけ
きっかけは
自社製品にてCODESYSを使用することが多いのですが、
XでCODESYSに関することが流れてきたのをきっかけに
私もメモ代わりにqiitaに記述しようと思いました。
個人でCODESYSの製品を持っているわけではないので
とりあえずRaspberry Piを購入しました。
Raspberry PiとWindowsでできることを
メインに記録していこうと思います。
（他の産業用通信などは何かの機会にセットを
用意できれば記録したいと思っています。）


# 参考までに使用したことのある通信・ソフトmemo
###### 通信
Modbus/RTU,Modbus/TCP
BACnet MS/TP,BACnet/IP
LONWORKS
ProfiBUS
CC-Link,IE,IE TSN,IE Field Basic
DeviceNet,EtherNet/IP
EtherCAT
MECHATROLINK-II/III
HLS
OPC UA

###### ソフト
Yet Another BACnet Explorer
BACnet Client Tool
LonMaker
IzoT CT
TIA Portal
MELSOFT iQ Works
Sysmac Studio
CX-One
TwinCAT2,3
MPE720
WideField3
Pytorchで画像を使う~縦横チャンネルってどうするんだっけ～

# はじめに
pytorchでちゃんと画像を使おうとしたときに、チャンネル数と縦横ってどうやって返還すんだっけ？という初歩の初歩のところで躓いてしまったので残します。

## 状況
OpenCVで画像を読み込んで、下記のコードで変換しようとしていました。

```python
transform_train = A.Compose([
    A.Resize(image_size, image_size),
    A.Normalize()
])
```

このコードではエラーが出ます。
[24, 3, 3, 3]にしたいのですが、なんかうまくなっていません。

```python
RuntimeError: Given groups=1, weight of size [24, 3, 3, 3], expected input[2, 24, 25, 5] to have 3 channels, but got 256 channels instead
```

## 解決策
なんてことはないですが、ToTensorV2を入れるだけで解決しました。
処理がどうなっているか詳しくは調べていませんが、それだけです。

```python
transform_train = A.Compose([
    A.Resize(image_size, image_size),
    A.Normalize(),
    ToTensorV2()
])
```

## 終わりに
そもそもpytorchを使っているのにtensor型への変換をするコードを書いていなかったのでそこからも気づけたのにかなり時間をかけてしまいました。
どこかで **「15分探して解決しなかったら記事をかけ」** と見た記憶があるので、非常に恥ずかしいミスですが記事にします。
会社のメールサーバーで clickparams のような有害と判断された parameter が除外される際に、エンコード問題があった・・ :(

# 背景

会社で、ある特定の URL だけ、メールで送ると壊れるんですけど、という相談があったので調査した記録

# 結論

- "clickparams=" のような情報取得を想定した url parameter は、社内メールサーバーのどこかで中継した際に、除外される
- この際、以下の再エンコードで、sharepoint が期待しているエンコードではなくなる
   1. url decode
   1. 有害判断の query を除外
   1. url encode

結果として、sharepoint でアクセスできない URL になる、と

具体的にどんなものが悪影響を受けるか、というと

- <code>Shared Documents</code>

こいつが・・以下になることで問題が生じる。大文字小文字なんかも変わってるけど

- sharepoint の期待
    - Shared%20Documents
- mail で 再エンコードによる問題
    - Shared+Documents

エンコードがどっちがいいのかは、以下でも議論されてる

https://stackoverflow.com/questions/1634271/url-encoding-the-space-character-or-20

# 現状の回避策

- パスの取得方法を別のものにする
- url parameter(Query) で問題になるものは、あらかじめ外して貼り付けるようにする

うーん・・ほかに良い方法が思いつかなかった :sweat: 

# Sharepoint でリンクアドレスを取る方法

代表的なのは以下の三種類

今回問題となるのは、URL parameter に 相対パスが含まれる 1. のパターン

1. アドレスから直接
   > https://{domain}.sharepoint.com/sites/{siteName}/Shared%20Documents/Forms/AllItems.aspx?id=%2Fsites%2FTestForTeams%2FShared%20Documents%2FFlowTest&viewid=7c268b5c%2Dc958%2D412c%2D91f8%2D4b365959236a
1. リンクのコピー
   > https://{domain}.sharepoint.com/:f:/s/{siteName}/Es5VWW9Ys2tMghVLXJqtiJMByylavfOoYTPH1JAOK3vyQA?e=y4yXCL
1. 詳細ウィンドウから、パスのコピー![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/d3e1c047-9d2b-9c5f-1cab-956cd122ac8c.png)

   > https://{domain}.sharepoint.com/sites/{siteName}/Shared%20Documents/FlowTest

# RFC 3986, 1866

https://www.rfc-editor.org/rfc/rfc3986

https://www.rfc-editor.org/rfc/rfc1866


# あとがき

自宅で調べようとしたら再現しないので、たぶん会社での情報漏洩対策なんだろう、というのが現状の結論
Java gold 例題 enum ストリームAPI

```java

enum Alphabet {
	A, B, C, D;
}

public class EnumTest {
	public static void main(String[] args) {

        String result = Arrays.stream(Alphabet.values())
							  .map(Enum::toString)
							  .collect(Collectors.joining(" - "));
         
		System.out.println(result);
    }
 }

```

上記のコードの出力結果は？
1. コンパイルエラー
1. 実行時エラー
1. Alphabet
1. A - B - C - D
1. ABCD
1. 何も表示されない

解答
↓

↓

↓

↓

↓

↓

↓

↓

↓

↓

4:　A - B - C - D
`Alphabet.values()`は列挙した値をすべて配列で返す。
Arraysの`stream(String[] array)`は`Stream<String>`型のStreamを返す。
mapでそれぞれの要素に対して、Enumの`toString()`をメソッド参照で呼び出し、適用する
(「`e -> e.toString()`」でも可)。

`toString()`は定数の名前をString型で返す。オーバーライド可能。
Enumの`name()`も同じく定数の名前をString型で返すが、オーバライドできない(final)。

`Collectors.joining(CharSequence delimiter)`で
入力要素を検出順に指定された区切り文字" - "で区切りながら連結するCollectorを返す。
その結果が変数resultに代入される。

補足:

Arrays.stream(String[] array) の使用例
```java
String[] names = {"arres", "barres", "cess"};

List<String> namedList = Arrays.stream(names)
                               .map(n -> n + " - ")
                               .collect(Collectors.toList());

namedList.forEach(System.out::println);
  ```

  出力
```java
arres - barres - cess - 
```
このように中間操作のmapを使うと、cess - のように最後の要素にまで" - "が連結される。

String型の配列に区切り文字を入れる場合、Stringクラスのjoinメソッドが利用できる。

```java
String[] names = {"arres", "barres", "cess" };

System.out.println(String.join(" - ", names));
```

出力
```java
arres - barres - cess
```


https://qiita.com/T-ngtj/private/df254aacd05be698199a
【Dango / Render.com / PostgreSQL】知識ゼロからデプロイ成功まで

# はじめに
以前 Django を学習していたころ Web サービスを作っていた。そのときのものを [Render.com](https://render.com/) にデプロイする。テーマは「音楽をより深く楽しむサービス」である。

# Render.com とは
Render.com はウェブアプリのデプロイを簡単にするクラウドプラットフォームである。開発者はインフラ管理の手間を省き迅速にサービスを展開できる。GitHub リポジトリからの自動デプロイができ、**無料枠がある**。

# デプロイした URL
> - [音楽をより深く楽しむためのサービス - MusicMemo -](https://django-render-dqde.onrender.com/) / [GitHubリポジトリ](https://github.com/TanakaLatias/new_music_memo)
> - 物語をより深く楽しむためのサービス - NovelReview - / [GitHubリポジトリ](https://github.com/TanakaLatias/new_novel_review)
※Render.com の無料枠では DB が１個しか持てないので NovelReview はデプロイできなかった

# デプロイで参考にさせていただいた Youtube 教材
> - [DjangoをRenderにデプロイする方法](https://www.youtube.com/watch?v=wwYMdRrqHwc)

こちらの動画一本だけで知識ゼロからデプロイできました(人'▽｀)

# デプロイで少し詰まったところ
#### １．環境変数を設定しないと動かない
GitHub のリポジトリを選択すると自動でデプロイが始まるが、環境変数を設定していないので失敗する。また環境変数の設定画面を開くと SECRET_KEY がデフォルトで設定されているが、Djangoのものに変える。
↓ 自分は django-allauth などの設定もしているので少し多い。
![Screenshot (242).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3047950/7d7b4a0e-fcdc-9705-98a1-29ede70a0026.png)

#### ２．requirements.txt の設定
あらかじめバージョンを指定しておくとインストールエラーが出た。なので指定せずインストールしてから pip freeze でバージョンを出した。
```python:requirements.txt
# Django==5.0.6
Django==4.2.14
django-environ==0.11.2
pillow==10.2.0
whitenoise==6.7.0
django-allauth==0.63.3

# for Render.com
asgiref==3.8.1
dj-database-url==2.2.0
dj-static==0.0.6
django-model-utils==4.5.1
gunicorn==22.0.0
packaging==24.1
psycopg2-binary==2.9.9
python-decouple==3.8
python-dotenv==1.0.1
sqlparse==0.5.1
static3==0.7.0
typing_extensions==4.12.2
tzdata==2024.1
```

#### ３．settings.py の DEBUG 設定
原因究明しようか迷ったが一旦飛ばした部分。print で確認すると正しく else: の方に行っているのになぜか上手くいかない。
```python:settings.py（デプロイ失敗）
if DEBUG:
    STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
else:
    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
    STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
    STATICFILES_DIRS = []
```
```python:settings.py（デプロイ成功）
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
STATICFILES_DIRS = []
```

# おわりに
少し詰まる部分もあったが、簡単にデプロイできた。Render.com は有料版もとても良いらしいので、機会があればまた使いたい。
AWS LambdaでSupabaseを使用したらNo module named 'pydantic_core._pydantic_core'が出た時の対処

## 問題
AWS LambdaでPythonからSupabaseを使用しようとしたら，
```エラー
No module named 'pydantic_core._pydantic_core
```
と表示された．


## 原因
Supabaseで使用されているfastapiのバージョンが高すぎると動作しない様子．


## 解決策
fastapiのバージョンを下げてインストールした後に，Supabaseモジュールをインストールする．
```
pip install -t ./python fastapi==0.99 supabase
```
ここではfastapiのバージョンを0.99に指定している．
「トランザクションログ」はundo log, redo log, binlogのどちらですか？

MySQLの`トランザクションログ`という用語は、トランザクションの記録や管理に関連する複数のログを指しています。
トランザクションの実行プロセスについては以下の記事をご参考ください。

https://qiita.com/dennis_wang/items/52d025ce176cf1a6082d

MySQLにおいて、主に以下の3つのログがあります。

- Undo Log
トランザクションによる変更を元に戻す(<font color="red">rollback</font>)ためのログ。主に読み取り/書き込みトランザクションに関連し、失敗したトランザクションのロールバックに使用されます

- Redo Log
トランザクションの<font color="red">一貫性を実現</font>するためのログ。データベースのクラッシュリカバリに使用され、トランザクションの順序と一貫性を維持する 

- Binary Log (Binlog)
トランザクションの変更履歴を記録し、トランザクションの<font color="red">永続化を実現</font>するためのログ。主にレプリケーションやポイントインタイムリカバリ（PITR）に使用される。すべてのデータ変更イベントを記録し、これによりスレーブサーバーへの変更の伝播が可能になる

「トランザクションログ」という言葉が具体的にどのログを指しているかは文脈によりますが、一般的な文脈では<font color="red">`Redo Log`や`Binlog`を指すことが多い</font>です。
`Undo Log`は主にトランザクションのロールバック用であるため、通常「トランザクションログ」として直接言及されることは少ないです。
脆弱性点検ツール

# WEB脆弱性ツール

# Nikto
ニクト。CUIツール。速度重視。perlスクリプト。webサーバを外側から点検する。
![nikto.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224341/2e537287-8ca0-3f91-317c-bd716e27e4bd.png)

（Niktoと呼ばれる傭兵。多分、関係ない。）

# Wapiti
ワピティ。CUIツール。精度重視。pythonスクリプト。webサーバを外側から点検する。
![Wapiti.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224341/f7221dc9-e09a-bbf8-4570-e90eaae66919.png)

（Wapitaと呼ばれる鹿。多分、関係ない。）

# Arachni
アラニ。GUIツールでIFはWeb。OSS。Rubyスクリプト。webサーバを外側から点検する。
![Arachni.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224341/d29a85bb-c702-e808-4c05-8187ee80a862.png)
（Arachneと呼ばれる蜘蛛女。webと蜘蛛をかけているのかな？）

# プロキシブラウザ
（ブラウザとwebサーバの間に入って、データを確認したり修正できるもの。）

# Burp Suite
バープスイート。GUIメイン。IFはJAVA。商用と無償版。JAVAランタイム環境が必要。（JAVAランタイムが同梱されているので、インストールが楽。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224341/8e5307f4-703e-4931-0b60-5a0c5bb2bf48.png)
（burpはゲップという意味。全然意味ないらしい。）

# OWASP ZAP
オーワスプザップ。ザップと呼ばれることも。GUIメイン。IFはJAVA。OSS。JAVAランタイム環境が必要。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224341/10ebb7db-9073-3802-a5c9-25d770fe3f0d.png)
（見てのとおり、Burpと同じようなもののようだ。）
![owasp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224341/59a67ce7-a0aa-c59b-d647-4222460d9d14.png)
（プロジェクト名がOWASP。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224341/1fbddf60-21e9-7534-fa04-36b367605e63.png)
（WASPと呼ばれる肉食の蜂。）

# イントラの内部からも外部からも点検できるもの。

# Nessus
ネッサス。GUIメイン。IFはWeb。商用。C/C++。
![nessus.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224341/cccc8b40-5f9b-dd6a-5c9c-959294e77448.png)
（Nessusと呼ばれる半人半馬で強力。強力ということかな？）

# OpenVAS
オープンヴァス。GUIメイン。IFはWeb。OSS。Nessusから派生。C言語。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224341/fa3a47b1-8abb-c3d4-ab3e-3efb44b31fd2.png)
（ネッシーとかNessusaurusと呼ばれる恐竜で、OpenVASのロゴ。Nessusの派生だから。）
# nmap
エヌマップ。CUIツール。OSS。
![nmap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224341/875d2934-f3cb-9072-bd40-d035de05eeed.png)
初めてのZeroGPUスペース - StableDiffusionUpscaleImage

https://huggingface.co/spaces/Akjava/StableDiffusionUpscaleImage

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54688/70859ee5-8f2c-46be-376b-a342b7efcceb.png)

128x128の画像が、シャープになります。ハイライトおかしくなるけど
なぜか、ZeroGPUは、128x128サイズでも、２分以上かかってエラーになることがあります。
小さいサイズ推奨ですが、小さいと、今度はまともに拡大されません。

(promptはbeautiful girl固定です)

　デモサイトなので、気に入ったデモがあれば、落としてローカルで動かすためのものですが、
 ローカルでまともに動くデモのほうが少ないでは？というぐらい環境合わせるのが難しい。
 
## 完成まで
コード書いてる環境で、Gradioがまともに動かない。しかたないので、Upして、エラー直してエラーという過酷な環境で開発したため(なぜかDev Modeはコミットが終わらない)もの凄く時間かかりました。

あと謎のエラーが出て苦労しました。検索で見つかったけど
```
RuntimeError: Numpy is not available
```
https://huggingface.co/spaces/zero-gpu-explorers/README/discussions/90

requirements.txt
```
numpy==1.26.4
```
PowerAutomateDesktop: OCR機能:Windows OCR で日本語テキストを見つける場合は、半角空白を挟め

# 背景

Curl アプリを自動化しようとした。
で、当初 OCR を使って、テキスト認識で入力自動化しようとしたが、上手く動かなかったので、
諦めて 画像認識で対処した。

が・・ふと「**半角空白が日本語文字の間にある**」ことを思い出したので備忘録

# 結論

Windows OCR で 日本語認識する場合、文字と文字の間に半角空白が挿入される。
よって、**「検索するテキスト」** には、半角空白を挟んでいかないと見つからない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/c770faed-3b17-9304-7b4f-133887b2adab.png)

テキスト抽出して再利用する場合は、Trim() すればいいので気付きやすいが、以下二つの場合は、テキスト検索なので気付かないことが多い・・ :sweat: 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/40a378f2-2afd-e169-7ceb-651ecf637d65.png)


# 実際の認識

以下を認識させると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/cd365029-d454-3546-f609-1ce9b0be01f0.png)

こんな感じで、一文字ずつ、**半角空白** を挟んでくる・・

> ー 織 ー shlmakuma (default) 方 イ ル 編 集 デ バ ッ グ ッ - ル 表 示 ヘ ル プ     
① こ の フ ロ - で power Fx ( プ レ ビ ュ - ) を 用 で き ま す 。 讎 細 情 毆


# テキスト認識が駄目な場合の対処

画像取得をして、以下で認識させれば、検出座標からの操作って意味ではほぼ同じ。
ただ、相対座標操作が結構面倒なので、図でイメージしないと操作しにくい :sweat: 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/120072/a48926d4-aa14-71ef-bb21-d7c0765153c1.png)


# あとがき

これらのおかげで、基本どんなアプリでも自動化可能なのは助かる。

会社のアプリはいつも糞UXで困る :angry:
初心者がGitHubで記事を管理できるのか


# はじめに
プログラミングを学ぶにあたって、記録用にQiitaのアカウントを作成した。
Markdown記法もよくわかっていないが、GitHubから管理する機能があり、恐らくそれが便利だからあるのだろうということで、使うことにした。
実際上手くできているのかも兼ね、かつここまで苦戦したので、後に続くやってみたい初心者の人の足掛かり、何らかの事情で連携が上手くいかなくなった時の自分の備忘録になるように残しておきたい。

# やったこと
まずはQiitaの右上の**投稿する**から**GitHubで記事を管理**を選択すると、以下の記事に誘導される。

https://qiita.com/Qiita/items/32c79014509987541130

GitHubのリポジトリを作ること・アクセストークンを発行し、GitHubのリポジトリ内のシークレットにアクセストークンを入力するところまではできた。
余談だが、二度アクセストークンを使わなければならないのに、私はコピーを別のもので上書きしてしまって不明となり、やり直したので、一時的にメモ帳機能などに残しておくと良いと思う。ただ、機密性が高い情報でもあるので、扱いには注意が必要だ。

Qiita CLIのセットアップなども、概ね手順通り進めることができた。
限定公開記事にして、一度確認作業をしよう。
**ここでエラーが起きた。**
GitHubには上がるが、Qiitaには反映されない。
原因を探そうと、[説明](https://qiita.com/Qiita/items/32c79014509987541130)を再度熟読すると、どうやらSecletの設定は、アクセストークンを入れるだけではないらしい。よく考えてみればURLが記入されているわけでもなく、そりゃそうかと思った。

## シークレットの設定（ワークフロー）

ここが本当に鬼門だった。
[説明](https://qiita.com/Qiita/items/32c79014509987541130)のSecretに関する説明の中にるリンクから、[GitHub Actions でのシークレットの使用](https://docs.github.com/ja/actions/security-guides/using-secrets-in-github-actions)に飛んで、改めて確認してみると、Secretの設定の一つとしてワークフローを設定してあげないといけないらしいが、 **ワークフローってどうやって作るの？** 読み漁ってもよくわからなかった。

元のページに戻り、もう一度読んでみると、シークレット設定の次の項目に
>セットアップをすると、Qiitaの記事をGitHubリポジトリで管理するためのGitHub Actionsのワークフローファイルが生成されます。

という記述を見つけた。Qiitaの記事を管理する予定のディレクトリを見てみると、

```
.github
├workflows
・   publish
・
・
```
という形で、それらしいものが書かれたpublishというYMLソースファイルという物ができていた。
GitHubに移動し、先ほどのリポジトリで、トークンを入力したのと同じ所にnew repository secretとあったので、そこのsecretにpublishの中身をコピペしてみた。


そして改めて記事をGitHubにプッシュした。
……さて、この記事が見られるようになっているということは、成功したということだ。

# 終わりに
初めてプログラミングをするにあたって、色々な説明や用語が初心者にとっては難しく、説明を読んでその説明を更に検索し、検索結果を更に検索するなんてことはよく起きると思う。
QiitaとGitHubについても同様だった。そもそもGitHubやGitの扱いも、言われたとおりにやってみたはずなのに、なんかエラーがでたなんてことが起きる。
プログラミングもその周辺についても、めげずに学んでいきたい。

この記事について、間違えていたり語弊がある部分があれば、ご指摘ください。

:::note
追記
ポエムタグをつけていましたが、他のポエムタグ記事を読んだところ、これはポエムじゃなさそうなので、タグを削除しました。
:::
ABC365Eを解いた


筆者はレート700弱の茶色コーダ

本日は以下の問題を解いていく

https://atcoder.jp/contests/abc365/tasks/abc365_e

# 実装コード

累積和と同じような感じで累積XORがあるらしい

XORは各bitで独立しているので、bitに分けて考える。

それぞれのbitが加算される回数は、各累積XORのbitで0の数と1の数の積で計算できる。

```python
import sys
def rI(): return int(sys.stdin.readline().rstrip())
def rLI(): return list(map(int,sys.stdin.readline().rstrip().split()))
def rS(): return sys.stdin.readline().rstrip()
def rLS(): return list(sys.stdin.readline().rstrip().split())
def err(*args): print(*args, file=sys.stderr)

W = 32

def main():
    N = rI()
    A = rLI()
    M = N+1
    S = [0] * (M)
    for i in range(N):
        S[i+1] = S[i] ^ A[i]
    
    X = [0]*W
    Y = [0]*W
    for i, s in enumerate(S):
        b = format(s, f'0{W}b')
        bin = [int(bit) for bit in b]
        for j,bj in enumerate(reversed(bin)):
            if bj==1:
                X[j] += 1
            else:
                Y[j] += 1

    ans = 0
    for j,xy in enumerate(zip(X,Y)):
        x, y = xy
        ans += (x * y) << j
    print(ans-sum(A))
    
if __name__ == '__main__':
    main()

```


# まとめ
ABC365Eを解いた。累積XORを実装した。

# 感想
累積和の考えをXORにも用いるという発想はなかった。
今後の参考にしていきたい。
【Chakra UI】１つのファイル内で複数のモーダルを開く場合のuseDisclosureの定義方法について

# はじめに
Chakura UIにて、1つのファイル内で複数のモーダルを開く場合の実装方法で若干迷ったので展開します。

# 問題
モーダルを複数開く場合、useDisclosureのhooksからisOpen, onOpen, onCloseプロパティをそれぞれ取得する必要があるが、1つのファイル内で複数のモーダルを開く場合のやり方

```jsx:useDisclosure（例）
// 編集画面用
const { isOpen, onOpen, onClose } = useDisclosure()
// 削除画面用
const { isOpen, onOpen, onClose } = useDisclosure()
```
↑のように、複数のuseDisclosureを宣言すると定義が重複する。

# 解決方法
一旦別名をつける。
```jsx:useDisclosure（例）
// 編集画面用
const editModal = useDisclosure();
// 削除画面用
const deleteModal = useDisclosure();
```

```jsx:使い方:（例）
// 編集画面
<EditModal
 isOpen={editModal.isOpen}
 onClose={editModal.onClose}/>
    
// 削除画面
<DeleteModal
 isOpen={deleteModal.isOpen}
 onClose={deleteModal.onClose}/>
```

# おわりに
簡単に解決するけど割と思いつかないものですね...

# 参考

https://v2.chakra-ui.com/docs/components/modal/usage

## JISOUのメンバー募集中🔥
プログラミングコーチングJISOUではメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？
気になる方はぜひHPからライン登録お願いします！👇

https://projisou.jp/
Python `functools.partial`を使って関数をカリー化する

## はじめに

関数型プログラミングの世界では、「カリー化」という概念が重要な役割を果たしています。カリー化とは、複数の引数を取る関数を、単一の引数を取る関数のチェーンに変換する技術です。Pythonでは、`functools.partial`を使用してこの概念を実装できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/9074651f-e7c1-f366-c43e-11e790abfacc.png)

## カリー化とは？

カリー化は、数学者ハスケル・カリーにちなんで名付けられました。この技術を使うと、複数の引数を取る関数を、一連の単一引数関数に変換できます。

例えば、2つの引数を取る関数 `f(x, y)` があるとします。これをカリー化すると、`f(x)(y)` という形になります。つまり、最初の引数 `x` を与えると、新しい関数が返され、その関数に `y` を与えると最終的な結果が得られます。

## `functools.partial`の紹介

Pythonの`functools`モジュールには、`partial`という便利な関数があります。これを使用すると、既存の関数の一部の引数を固定した新しい関数を作成できます。

## `functools.partial`を使ったカリー化の実装

それでは、`functools.partial`を使って関数をカリー化する方法を見てみましょう。

```python
from functools import partial

def add(x, y):
    return x + y

# add関数をカリー化
def curry_add(x):
    return partial(add, x)

# 使用例
add_5 = curry_add(5)
result = add_5(3)
print(result)  # 出力: 8

# さらに例を追加
add_10 = curry_add(10)
print(add_10(7))  # 出力: 17
print(curry_add(2)(3))  # 出力: 5
```

この例では、2つの引数を取る`add`関数をカリー化しています。`curry_add`関数は`x`を引数に取り、`partial(add, x)`を返します。これにより、`x`が固定された新しい関数が生成されます。

## より複雑な例

次に、3つの引数を取る関数をカリー化してみましょう。

```python
from functools import partial

def multiply(x, y, z):
    return x * y * z

def curry_multiply(x):
    def curry_y(y):
        return partial(multiply, x, y)
    return curry_y

# 使用例
multiply_by_2 = curry_multiply(2)
multiply_by_2_and_3 = multiply_by_2(3)
result = multiply_by_2_and_3(4)
print(result)  # 出力: 24

# さらに例を追加
print(curry_multiply(2)(3)(4))  # 出力: 24
print(curry_multiply(5)(2)(3))  # 出力: 30
```

この例では、3つの引数を取る`multiply`関数をカリー化しています。`curry_multiply`関数は2段階のカリー化を行い、各段階で1つずつ引数を固定していきます。

## カリー化の利点

1. **部分適用**: 引数の一部を固定した新しい関数を簡単に作成できます。
2. **柔軟性**: 複雑な関数を、より単純で再利用可能な関数に分解できます。
3. **関数合成**: カリー化された関数は、他の関数と組み合わせやすくなります。

## まとめ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2648069/4555a9d8-d726-b58c-f9da-945599dce005.png)

`functools.partial`を使用したカリー化は、Pythonで関数型プログラミングの概念を実装する強力な方法です。これにより、コードの再利用性と柔軟性が向上し、より表現力豊かなプログラミングが可能になります。

カリー化は、特に複雑な関数を扱う際や、関数型プログラミングのパラダイムを活用したい場合に非常に有用です。Pythonの`functools.partial`を使いこなすことで、エレガントで効率的なコードを書くことができるでしょう。

## 参考情報

1. [Python公式ドキュメント - functools.partial](https://docs.python.org/3/library/functools.html#functools.partial)
   - `functools.partial`の公式ドキュメントです。使用方法や詳細な説明が記載されています。

2. [PEP 309 -- Partial Function Application](https://www.python.org/dev/peps/pep-0309/)
   - Pythonで部分適用を実現するための提案書です。`functools.partial`の背景や設計思想について理解を深めることができます。

3. [The Python Standard Library - Functional Programming Modules](https://docs.python.org/3/library/functional.html)
   - Pythonの関数型プログラミングモジュールに関する公式ドキュメントです。`functools`以外の関連モジュールについても学ぶことができます。
Dockerを活用したLAMP環境のセットアップガイド

## 今からDockerをはじめたいあなたへ

Dockerを使用してLAMP（Linux, Apache, MySQL, PHP）スタックを構築することは、開発と本番環境の一貫性を保つための効果的な方法です。本記事では、Dockerを使用してLAMP環境をセットアップする手順を詳しく説明します。

### 必要なツールのインストール

まず、DockerとGitをインストールします。

```bash
sudo apt update
sudo apt install docker.io docker-compose git -y
```

### リポジトリのクローン

次に、LAMP環境を構築するためのリポジトリをクローンします。

```bash
git clone https://github.com/sprintcube/docker-compose-lamp.git
cd docker-compose-lamp/
cp sample.env .env
```

### `.env`ファイルの設定

`.env`ファイルを開き、必要な設定を行います。ここでは、PHPのバージョンやMySQLのバージョンなどを指定できます。

```env
# Example .env file
PHP_VERSION=8.2
MYSQL_VERSION=5.7
```

### Dockerコンテナの起動

以下のコマンドを実行して、Dockerコンテナを起動します。

```bash
docker-compose up -d
```

これで、LAMPスタックが起動し、`http://localhost`でアクセスできるようになります。

### 仮想ホストの設定

新しい仮想ホストを追加するには、`config/vhosts/default.conf`ファイルを編集します。

```apache
<VirtualHost *:80>
    ServerName example.local
    DocumentRoot /var/www/html/example
    <Directory /var/www/html/example>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
```

### SSLの設定

SSLを有効にするには、`config/ssl`ディレクトリにSSL証明書を配置し、`config/vhosts/default.conf`ファイルでポート443の設定を有効にします。

```apache
<VirtualHost *:443>
    ServerName example.local
    DocumentRoot /var/www/html/example
    SSLEngine on
    SSLCertificateFile /etc/ssl/certs/cert.pem
    SSLCertificateKeyFile /etc/ssl/private/cert-key.pem
</VirtualHost>
```

### 動作確認

`/var/www/html/`ディレクトリにPHPファイルを作成して動作確認を行います。

```bash
sudo nano /var/www/html/info.php
```

以下の内容を記述します：

```php
<?php
phpinfo();
?>
```

ブラウザで`http://localhost/info.php`にアクセスし、PHP情報ページが表示されることを確認します。

### データベースの管理

phpMyAdminを使用してデータベースを管理できます。`http://localhost:8080`にアクセスし、以下のデフォルトのクレデンシャルを使用します。

- ユーザー名: root
- パスワード: tiger

### まとめ

Dockerを使用してLAMP環境をセットアップすることで、開発から本番環境まで一貫したワークフローを実現できます。環境の違いによるトラブルを減らし、効率的なアプリケーション開発・運用が可能になります。