OmniAICreator/Qiita-1.07M huggingface dataset:
どの作業にどれだけ時間をかけたか計測したい

# 作業に実際にかかった時間ってどうやって確認していますか？

**このタスクは 1 人日(8 時間)で終わるだろう。実際にはどれくらいかかったか？**
もしくは
**複数のタスクを抱えていて、1 日あたりタスク A には 2 時間、タスク B には 4 時間の時間を割り当てたい。**

こういう場合ってみなさんどうやって時間を計測していますか？

僕は開発用 PC が Windows なので、標準で入っているクロックアプリでいい感じに時間計測出来ないかと思ったんですが、

- ストップウォッチは同時に複数走らせることが出来ないので、
  タスク A  --> タスク B --> タスク A
  という順番で作業したときに、いちいちストップウォッチをリセットしなければいけなく、タスク A にかかった時間の合計を計算するのが面倒くさい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154029/9c4a9af6-874d-b2d6-1d69-8d97cce72eb4.png)

- カウントダウンなら複数走らせることができるけど、最初に決めた時間からの延長ができないので、
  8 時間の見積もりだったけど実際には 9 時間かかってしまった。という場合にうまく対応できない。
  あとアラーム音や通知が鬱陶しい(設定で消せるかも？)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154029/a87d4657-9779-b4bd-4c48-f93b0b357ca0.png)

# 1 つのアプリで複数のストップウォッチを走らせたい、可能ならラベルとかもつけたい

軽く探してみたのですが、いい感じのデスクトップアプリや web アプリは見当たりませんでした。
また、会員登録とかも出来ればしたくないです。ササッと気軽に使いたいです。

# ないので作ってみた

ないなら作れば良いのです。こういうときエンジニアって便利だなって思いますね。
サーバ契約するのもアレだったので、github.io で公開しました。

https://madayo.github.io/html-js-parts/stopwatch.html

ただの html と Vue.js です。
入力されたデータはサーバには一切保存していないので良ければ皆さん気軽に使ってみてください。
**※ソースはかなり改善の余地ありそうですが、機能としては十分なので良しとしています。**
![127.0.0.1_5502_stopwatch.html.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154029/65cf3c28-033a-d181-c405-904523ba0ccd.png)

機能としては以下のとおりです。

- なんの時間を計測しているのか分かるようなラベルを自由に設定できる
- Start で時間計測開始、再開
- Stop で時間計測一時停止
- Reset で計測した時間を削除
- Reset all でラベル、計測した時間をすべて削除する
  - いきなり全削除ではなく、短時間にもう一度クリックしない限りは削除されないので、一応誤クリックは防げる
- 10 秒おきにブラウザの Local Storage にデータを保存するので、間違って画面を閉じてしまった場合でも復元できる

# 使ってみてどうだったか

作業時間を計測することが全然苦じゃなくなりました。
いちいちブラウザのタブを切り替えないと Start, Stop ボタンが押せないのがちょっと手間でしたが、すぐに慣れて気にならなくなりました。
大きめのモニターを使っている場合は、フリーソフトとかと組み合わせて、ブラウザを常に前面に表示するのも良さそうです。
FTPでアップロードも削除もできない件を解決した

# 基本環境
CentOS Linux release 7.9.2009
vsftpd: version 3.0.2

# 不具合

FTPクライアントでアクセスできるが、アップロードも削除もできない
表示　◯
ダウンロード　◯
アップロード　×
削除　×

# vsftpd設定を疑う

```
vim /etc/vsftpd/vsftpd.conf
```

変化なし

# ファイルパーミッションを疑う

```
$ ls -l /var/www/html/

example.com apache apache
```
FTPでは別ユーザー名でログインしていた

# グループ設定を確認

```
$ cat /etc/group

apache:x:48:
```

apacheグループに自分のユーザー名を登録

```
$ usermod -aG apache ＜ユーザー名＞
```

確認する

```
$ cat /etc/group

apache:x:48:＜ユーザー名＞
```

# FTPテスト
アップロード　◯
削除　◯

***
# 参考記事
https://qiita.com/orangain/items/056db6ffc16d765a8187
https://eng-entrance.com/linux-group
[C#] AES暗号化

QR決済に認証キーを保管する時に、予め暗号化することでセキュア面を強化する


        //初期化ベクトル
        private const string AES_IV = "rW$yZDcM?9L@pf6G";
        //暗号化鍵
        private const string AES_Key = "9%L4HP&eKWFYBi@4O2$yTvG!b]Kz9H&R";

        /// <summary>
        /// 対称鍵暗号を使って文字列を暗号化する
        /// </summary>
        /// <param name="text">暗号化する文字列</param>
        /// <returns>暗号化された文字列</returns>
        public static string Encrypt(string text)
        {
            // AES暗号化サービスプロバイダ
            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();

            // ブロックサイズ（何文字単位で処理するか）
            aes.BlockSize = 128;
            // 鍵の長さ
            aes.KeySize = 256;
            // 暗号利用モード 
            aes.Mode = CipherMode.CBC;
            // パディング
            aes.Padding = PaddingMode.PKCS7;

            aes.IV = Encoding.UTF8.GetBytes(AES_IV);
            aes.Key = Encoding.UTF8.GetBytes(AES_Key);

            // 文字列をバイト型配列に変換
            byte[] src = Encoding.Unicode.GetBytes(text);

            // 暗号化する
            using (ICryptoTransform encrypt = aes.CreateEncryptor())
            {
                byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
                // Base64形式（64種類の英数字で表現）で返す
                return (Convert.ToBase64String(dest));
            }
        }

        /// <summary>
        /// 対称鍵暗号を使って暗号文を復号する
        /// </summary>
        /// <param name="cipher">暗号化された文字列</param>
        /// <returns>復号された文字列</returns>
        public static string Decrypt(string cipher)
        {
            string plain = string.Empty;

            try
            {
                if (!string.IsNullOrEmpty(cipher))
                {
                    // AES暗号化サービスプロバイダ
                    AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
                    aes.BlockSize = 128;
                    aes.KeySize = 256;
                    aes.Mode = CipherMode.CBC;
                    aes.Padding = PaddingMode.PKCS7;
                    aes.IV = Encoding.UTF8.GetBytes(AES_IV);
                    aes.Key = Encoding.UTF8.GetBytes(AES_Key);

                    // Base64形式の文字列からバイト型配列に変換
                    byte[] src = Convert.FromBase64String(cipher);

                    // 複号化する
                    using (ICryptoTransform decrypt = aes.CreateDecryptor())
                    {
                        byte[] dest = decrypt.TransformFinalBlock(src, 0, src.Length);
                        plain = Encoding.Unicode.GetString(dest);
                    }
                }
            }
            catch (Exception)
            {
                //暗号化していない文字列が登録している場合に発生。空文字を設定する
                plain = "";
            }
            return plain;
        }

ｓ
Raspberry Pi におけるネットワークの設定とnumpyとsounddeviceを利用するまでの設定

# はじめに
Raspberry Piの環境がぶっ壊れたので新しく環境を構築しました。
# IPアドレスの設定
`/etc/dhcpcd.conf`というファイルに書き込むことで設定できます。
1. `sudo nano /etc/dhcpcd.conf`で内容を編集
1. `sudo reboot yes`で再起動
### 例
```
interface eth0
static ip_address=<Static IP>
static routers=<Gateway IP>
static domain_name_servers=<DNS IP>
static domain_search=<DNS IP>
noipv6
```

オプションの意味は`dhcp-options`と基本的には同じだそうなので、下記リンクを参考にしてください。例えば`domain_name_servers`について知りたい場合は`domain-name-servers`の項を見てみると情報を得ることができます。

https://linuxjm.osdn.jp/html/dhcp/man5/dhcp-options.5.html

# ルーティングの設定
`/lib/dhcpcd/dhcpcd-hooks/40-route`というファイルに書き込むことで設定できます。
1. `sudo touch /lib/dhcpcd/dhcpcd-hooks/40-route`でファイルを作成
1. `sudo nano /lib/dhcpcd/dhcpcd-hooks/40-route`で内容を編集
1. `sudo reboot yes`で再起動
### 例
`eth0`や`wlan0`の場合のネットワークの設定についてです。
```
ip route add <destination network> dev eth0 via <gateway> metric 100
ip route add <destination network> dev wlan0 via <gateway> metric 100
ip route add <destination network> dev usb0 via <gateway> metric 100
```

細かい文法規則はRedHatのドキュメントを参照してください。

https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/networking_guide/sec-configuring_static_routes_with_ip_commands

https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/networking_guide/sec-Configuring_Static_Routes_in_ifcfg_files#bh-Static_Routes_Using_the_IP_Command_Arguments_Format

# エラーの対処
### SSLError
SSLError具体的には下記のようなエラーが発生した場合の対処について。
```
Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by
'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate is not yet valid (_ssl.c:1056)'))':
  Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by
'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate is not yet valid (_ssl.c:1056)'))':
  Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by
'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate is not yet valid (_ssl.c:1056)'))':
  Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by
'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate is not yet valid (_ssl.c:1056)'))':
  Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by
'SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate is not yet valid (_ssl.c:1056)'))':
```
アドレス毎に信頼するホストを追加すればよいが、**日時がズレている場合が多い**ため正しい日時を設定すればうまくゆくことが多い。
```
python3 -m pip <package name> --trusted-host piwheels.org --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org
```
### No package 'libffi' found
```
sudo apt install libffi-dev
```
Vue3 + TypeScriptでObnizを使う

# はじめに

遅ればせながら `Obniz` で遊んでみようと思ったんですが、僕がよく使う `Vue3` 環境では動作せず…
調べるとQiitaにも記事はあったのですが、Obnizのバージョンが違うからなのかVueのバージョンが違うからなのか、記事を参考にしてもうまく動作しませんでした。

https://qiita.com/wicket/items/569afb7c10b55a9fccd2

記事を参考に色々と挑戦してみたのですが、`npm` から取得したObnizSDKのコードの中に`Buffer`を使用している箇所があり、これがどうにも解決できず…
「もしかして`npm`からインストールした`obniz`は`node.js`でしか動かないのでは？(動かないようになったのでは？)」と思い、別のアプローチを試したところうまく行ったので、紹介したいと思います。

# やったこと

結局、CDNで公開されているバージョンを使用する事にしました。TypeScript用の型を設定する方法はこちらを参考にしています。

https://qiita.com/jerrywdlee/items/cfd06a64edffd8703a1e

## obniz をインストール

型情報を取得する必要があるので、結局`obniz`をインストールする必要はあります。ただし、型情報だけを使うので `webpack` の設定でここでインストールした`obnize`は除外する事になります。

```
npm i -S obniz
```

## CDNからobinzを読み込む

`public/index.html`にscriptを追加します。

```index.html
<script src="https://unpkg.com/obniz/obniz.js"></script>
```

## 型を定義

`src`直下に`obniz.d.ts`を追加します。ファイル名前は何でも良いと思います。

```obniz.d.ts
import _Obniz from "obniz";

declare global {
  const Obniz: _Obniz;
}

```

## vue.config.jsを更新

`npm` でインストールした `obniz` を使用しないように、 `webpack` の設定に `externals` を追加します。

```vue.config.js
const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
  transpileDependencies: true,
  configureWebpack: {
    externals: {
      obniz: "Obniz",
    },
  },
});

```

# 使用方法

設定が終われば、後は普通に `obnize` を使用できると思います。

```vue
<script setup lang="ts">
import Obniz from "obniz";

const obniz = new Obniz();
obniz.on("connect", async () => {
  if (obniz.display) obniz.display.print("hello!");
});
</script>
```

# おわりに

Obnizに限らず、「CDNで公開されているパッケージを使いたいけど、TypeScriptの型情報も使いたい」という場合には使えそうな方法だなと思います。
同じ事で悩んでいる方の時間を1msecでも短くできたなら幸いです。
Accessのサニタイズでよく使う特殊文字のChr()一覧（改行コード、タブ、NULL終端文字列など）

# 一覧

| 特殊文字 | VBAの関数 |
|---|---|
| ラインフィード（LF） | Chr(10) |
| キャリッジリターン（CR） | Chr(13) |
| CR+LF | Chr(13) & Chr(10) |
| 水平タブ | Chr(9) |
| 垂直タブ | Chr(11) |
| バックスペース文字 | Chr(8) |
| NULL終端文字列（NULL文字） | Chr(0) |
| 半角ダブルクオテーション | Chr(34) |
| 全角ダブルクオテーション | Chr(33128) |
| ノーブレークスペース | ChrW(160) |

# よく使うサニタイズSQLスニペット

```sql
-- AccessはNULLをReplace()に通すと例外になるので、まずNULLは空文字に。
UPDATE [テーブル名] SET [カラム名] = Nz([カラム名], '');

-- 改行除去。
-- CR+LFは下記2つを流せば消えるので、わざわざCR+LFをReplace()する必要は無い。
UPDATE [テーブル名] SET [カラム名] = Replace([カラム名], Chr(10), '');
UPDATE [テーブル名] SET [カラム名] = Replace([カラム名], Chr(13), '');

-- タブ除去。
UPDATE [テーブル名] SET [カラム名] = Replace([カラム名], Chr(9), '');
UPDATE [テーブル名] SET [カラム名] = Replace([カラム名], Chr(11), '');

-- バックスペース文字除去。
UPDATE [テーブル名] SET [カラム名] = Replace([カラム名], Chr(8), '');

-- NULL終端文字列除去。
UPDATE [テーブル名] SET [カラム名] = Replace([カラム名], Chr(0), '');

-- 半角ダブルクオテーションを全角ダブルクオテーションに。
UPDATE [テーブル名] SET [カラム名] = Replace([カラム名], Chr(34), Chr(33128));

-- ノーブレークスペースを通常の半角スペースに。
UPDATE [テーブル名] SET [カラム名] = Replace([カラム名], ChrW(160), ' ');
```

# バージョン

Windows 10 Pro 21H2 OSビルド 19044.1889
Microsoft Access for Microsoft 365 MSO (バージョン 2207 ビルド 16.0.15427.20166) 32 ビット
python3 数学ⅰ 展開と因数分解

高校で使う数学の復習がてらpythonの勉強もしていきます。今回は数学ⅰの数と式から展開と因数分解をやります。

まずsympyというライブラリをpipコマンドで入れます
```
pip install sympy
```
式の展開からやっていきます。
sympy.Symbol()で文字を定義し、sympy.expand()で式を展開します。
```python
import sympy

x = sympy.Symbol('x')#文字ｘを定義
y = sympy.Symbol('y')

ex = (x + y)*(2*x + 2*y)#式を定義
f = sympy.expand(ex)#式の展開
print(f)
```
2*x**2 + 4*x*y + 2*y**2と出力されます
つぎは因数分解
sympy.factor()で因数分解できます
```python
import sympy

x = sympy.Symbol('x')
y = sympy.Symbol('y')

ex = 2*x**2 + 5*x*y + 3*y**2
f = sympy.factor(ex)
print(f)
```
(x + y)*(2*x + 3*y)と出力されます
基本情報（ジョブ管理）

データ収集、給与計算、明細票印刷、残業チェックなど仕事は色々とあります。コンピューターが処理する仕事の単位を**ジョブ**といいます。

## ジョブ管理の流れ
ジョブ管理は、カーネルが持つ機能の一つです。この機能で利用者との間を橋渡しするのが**マスタースケジューラ**という管理プログラム。利用者はこの管理プログラムに対して、ジョブの実行を依頼します。

マスタスケジューラは、ジョブの実行を**ジョブスケジューラ**に依頼します。依頼を受け取ったジョブスケジューラは、次の流れでジョブを実行していきます。

|   |説明  |
|---|---|
|リーダ  |依頼されたジョブを入力して、ジョブ待ち行列に登録します。  |
|イニシエータ  |優先度の高いジョブを持ってきて、ジョブステップに分解します。  |
|ターミネータ　 | 実行を終えたジョブに割り当てられたハードウェア資源を解放して、ジョブの結果を、出力待ち行列に登録します。
|ライタ |優先度の高いものから順に、ジョブ結果を出力します。|

OSから見た処理単位の、ジョブ、プロセス、スレッドと頭が混乱してきたのでまとめてみます。

ジョブ ⊃　ジョブステップ　⊃　プロセス（タスク）　⊃　スレッド

ユーザが指定したプログラムはジョブとして扱われ、ジョブはいくつかのジョブステップに分解されます。
そのそれぞれは並行処理可能なタスクに分解され、非同期的に実行する流れ

が上記の説明です。

## スプーリング
CPUと入出力装置では、処理速度に大きな差があります。

入出力データを高速な磁気ディスクへと蓄えるようにして、CPUが入出力装置を待たなくて済むようになります。例えば音楽データを磁気ディスクに書き出したら、CPUは次の処理に移ってしまいます。
そうすることで、無駄な待ち時間を削減できます。

低速な装置とのデータのやりとりを、高速な磁気ディスクを介して行うことで処理効率を高める方法を**スプーリング**と呼びます。

スプーリングを利用すると、CPUの待ち時間を削減することができるので、単位時間あたりに処理できる仕事量を増やすことができます。単位時間あたりに処理できる仕事量を**スループット**と呼びます。

## まとめ
どんな流れなのか混乱するところもありますが、まとめながらやっていきます。
net::ERR_CONNECTION_REFUSED cannot read properties of null (reading 'end')の解決方法

# エラーの経緯
当時はタスク管理のwebアプリを開発中でした。
その日の作業を終えてパソコンを閉じ、翌日に再びパソコンを開いて更新をかけると、タスクの一覧が取得できないエラーが発生していました。
動作確認をしてからパソコンを閉じていたので、コードの誤りなどはありません。

# エラー文
net::ERR_CONNECTION_REFUSED
cannot read properties of null (reading 'end')

# 原因
このエラーの原因は、PCのIPアドレスが前日と自動で変わっていたためでした。

# 解決法
.envファイルのIPアドレスの設定をPCのIPアドレスに合わせて変更します。
また、MySQL Workbench等のクライアントアプリケーションを使用している場合も、IPアドレスの設定を変える必要があります。

# ＊IPアドレスの確認法
・Windowsの場合
1.「Windows」キーを押しながら「X」キーを押し、表示された一覧から「コマンドプロンプト」をクリックします。 
2.「ipconfig」と入力して［Enter］キーを押します。 
3.表示されたIPv4アドレスまたはIPアドレス（IP Address）を確認]

・macOSおよびOS X（Mac）の場合
1.アップルメニューの［システム環境設定］を開き、［ネットワーク］アイコンを選択します。
2.［Ethernet］を選択します。
3.表示されたIPアドレス（またはIPv4 アドレス）を確認します。
AWS CLIで一定期間更新されていないロググループを一掃

# はじめに
検証環境にロググループがどれくらいあるかを見たことがあるでしょうか。
僕の検証環境には310個のロググループがいました。
![310.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2109911/d11b68c3-b9f7-8199-c26c-0011e55e3005.png)

ロググループのストリームの数や保持期間にもよりますが、大体50円/月ほどかかっています。
費用的には大したことなく、「なんか気持ち悪いなぁ」という程度なので消さなくても良いかと思います。

ただ、**先日JAWS-UG CLI支部でCloudWatch Logsのハンズオンをやったこともあり、理解を深める意味でロググループを一掃するコマンドを作ってみることにしました。**

でも、使用中のロググループもあり、全部消す訳にもいかないので、一定期間更新されていないロググループのみ消すようにします。

# 手順
### 1. 削除したいリージョンでCloudShellを起動
![cloudshell.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2109911/02daab0f-285b-284e-9d71-4240b7128f61.png)
### 2. 全ロググループを抽出する
**コマンド**
```
ARRAY_LOG_GROUP_NAMES=$( \
aws logs describe-log-groups \
    --query 'logGroups[*].logGroupName' \
    --output text
) \
  && echo ${ARRAY_LOG_GROUP_NAMES}
```
**結果(例)**
実行したリージョンに存在しているロググループがスペース区切りで全て出力
```
/aws/lambda/appmesh-xxxx /aws/lambda/no1 ... test-log-group-1 test-log-group-2 test-log-group-3 ... test-log-group-4month-ago test-log-group-2month-ago
```
### 3. 削除対象とする期間を指定
- dateコマンドで3ヶ月前のタイムスタンプを取得
- AWS CLIで使える形式にするために000を追加

**コマンド**
```
THREE_MONTH_AGO_DATE=$(date -d '3 month ago' +%s)000 \
  && echo ${THREE_MONTH_AGO_DATE}
```
**結果(例)**
```
1649930797000
```
### 4. 削除対象の抽出
**コマンド**
```
for i in $(echo "${ARRAY_LOG_GROUP_NAMES%None*}");do
  x=$( \
    aws logs filter-log-events \
      --log-group-name ${i} \
      --max-items 1 \
      --start-time ${THREE_MONTH_AGO_DATE} \
      --query 'events[].logStreamName' \
      --output text \
  )
  if [ -n "$x" ]; then
    ARRAY_LOG_GROUP_NAMES=`echo ${ARRAY_LOG_GROUP_NAMES} | sed -e s@$i@@`
  fi
done \
  && echo ${ARRAY_LOG_GROUP_NAMES}
```
**結果(例)**
手順1で出力されたロググループから、指定した期間内で更新されたロググループが取り除かれて出力
```
/aws/lambda/appmesh-xxxx /aws/lambda/no1 ... test-log-group-1 test-log-group-2 test-log-group-3 ... test-log-group-4month-ago
```
ロググループが少ないリージョンで試してみたところ、3ヶ月間で更新されロググループが削除対象から消えていることを確認
（見やすさのために改行していますが、実際はスペース区切りの1行で出力）
![hikaku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2109911/9c662cbd-2854-2499-bff2-2bc7e48df916.png)
### 5. ロググループの削除
**コマンド**
```
for delete in $(echo "${ARRAY_LOG_GROUP_NAMES%None*}");do
    aws logs delete-log-group \
      --log-group-name ${delete} 
done
```
**結果(例)**
```
出力なし
```
# 実行後
ロググループの数が63まで減った！
![63.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2109911/97238672-732d-c60b-0e45-e3f6c910bdc6.png)


# 今後の展望
- AWS CLIスクリプト → Lambda(python) + EventBridgeに書き換え、
自動化できそう
    - ただ増えても100円/月いくか怪しいので、作成する必要があるか微妙... 
- 全リージョンのロググループを一括で削除できるようにする
Flutter画像の表示方法と各形式の変換

Flutterにおいて、画像の表示方法は下記の通りです。 

1. Image.asset
1. Image.network
1. Image.file
1. Image.memory

# Image.asset
アプリ内(アセット)の画像を表示する方法です。pathはアセットのlocalパスです。
1. プロジェクト直下にimagesフォルダを作成して、表示したい画像をその中に入れます。
1. pubspec.yamlを編集する
1. Image.assetで画像を表示する
```yaml
flutter:
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
    - images/asset.jpg

```

# Image.network
ネットから画像を表示する方法です。pathは画像のネットワークURLです。
```dart
return SizedBox(
      height: 60,
      width: 60,
      child: Image.network(
        "https://XXXXXXXXXX",
        fit: BoxFit.cover,
        errorBuilder: (BuildContext context, o, s) {
          return Image.asset(
            "asset/images/noPhoto.png",
            fit: BoxFit.cover,
          );
        },
      ),
    );
```
# Image.file
ローカルファイル形式で画像を表示する方法です。
image_pickerで取得したファイルはこの方法で表示できます。
```dart
// image picker
XFile? image = await ImagePicker().pickImage(source:ImageSource.gallery);

final pickedImage;
if(image == null){
  return;
}
pickedImage = File(image.path);
```
image_picker:
https://pub.dev/packages/image_picker
# Image.memory
Uint8List形式で画像を表示する方法です。Base64コードの画像データはこの方法で表示できます。
変換方法については後述します。

# 変換について
- URLからUint8Listへ変換する
```dart
Uint8List imageBytes =
          (await NetworkAssetBundle(Uri.parse(path)).load(path)).buffer.asUint8List();
```
- アセットファイルをUinit8Listへ変換
```dart
    // アセットからデータをロードする
    final byteData = await rootBundle.load(assetPath);
    Uint8List imageBytes =
        byteData.buffer.asUint8List(byteData.offsetInBytes, byteData.lengthInBytes);
```
- アセットファイルをImageへ変換
```dart
Future<ui.Image>? loadImageFromAssets(String path) async {
  ByteData data = await rootBundle.load(path);
  return decodeImageFromList(data.buffer.asUint8List());
}
```
- fileからBase64へ変換
```dart
final file;
List<int> imageBytes = await file.readAsBytes();
// base64 code
String base64Image = base64Encode(imageBytes);
```
- Base64からUint8List
```dart
Uint8List unit8 = base64Decode(base64);
```
Spring BootでSelenideを使う

# 環境
- macOS Monterey 12.4
- Google Chrome 103.0.5060.134
- JDK 17.0.1 (Eclipse Temurin)
- Spring Boot 2.7.1
- Selenide 6.6.6

# Selenideとは
Seleniumを使いやすくしたラッパーライブラリだそうです。

公式サイト→ https://selenide.org/

# 依存性の追加
pom.xmlに、spring-boot-starter-testと共にselenideを追加します。

```xml:pom.xml
    <dependencies>
        ...
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.codeborne</groupId>
            <artifactId>selenide</artifactId>
            <version>6.6.6</version>
            <scope>test</scope>
        </dependency>
        ...
    </dependencies>
```

> 以前のSpring Bootでは、Spring Boot自体がSeleniumに依存していたため、Seleniumのバージョンを指定し直す必要があったようです（[参考記事](https://qiita.com/OSKD/items/27881470ea4f93790f32)）。しかし、少なくともSpring Boot 2.7ではSeleniumに依存していないため、そのような必要はありません。

# Selenideの設定
src/test/resourcesにselenide.propertiesを作成して、次のように記述します。

```properties:selenide.properties
selenide.browser=chrome
selenide.headless=true
```

> どのような設定項目があるかは、 `com.codeborne.selenide.Configuration` クラスのJavadocをご確認ください。

# テストコードの作成
Selenideの詳細なAPIは[公式ドキュメント](https://selenide.org/documentation.html)を参照してください、

```java:MemoIntegrationTest.java
package com.example.springmemo.integration;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;

import static com.codeborne.selenide.Selenide.*;
import static com.codeborne.selenide.Condition.*;
import static com.codeborne.selenide.CollectionCondition.*;
import static org.junit.jupiter.api.Assertions.*;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MemoIntegrationTest {

    @LocalServerPort
    int port;

    private String url(String path) {
        return "http://localhost:%d%s".formatted(port, path);
    }

    @Test
    @DisplayName("トップページにアクセスしたらメモが3件表示される")
    void test01() {
        open(url("/"));
        String selector = ".tr-memo";
        assertAll(
                () -> $$(selector).shouldBe(size(3)),
                () -> $(selector).shouldBe(matchText("1 あああ \\d{4}年\\d{1,2}月\\d{1,2}日 \\d{1,2}時\\d{1,2}分\\d{1,2}秒"))
        );
    }
}
```
サクラエディタで改行コード3種（CR+LF、キャリッジリターン、ラインフィード）の画面表示確認と入力変更

# ここ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/17cc87d9-6868-2014-6052-a8a8e63bb240.png)

**改行はどのコードで入力するか** の設定もできる。

| 種類 | 画面上の表示 |
|---|---|
| CR+LF | ↵ |
| キャリッジリターン（CR） | ← |
| ラインフィード（LF） | ↓ |

# 参考サイトさん

https://auroralights.jp/entry/2021/04/03/233553

# バージョン
Windows 10 Pro 21H2 OSビルド 19044.1889
サクラエディタ   v2.4.1.2849 32bit (tag v2.4.1)
go で private なメソッドは他パッケージでは実装できない

# これは何？

go で

```go:go
type Foo interface {
	private()
}
```

みたいなインターフェイスがあって、この `private` を他パッケージで実装可能かどうか調べた。

# C++ の場合

C++ の場合。
アクセス制御とオーバーライド可否は直行している。

```c++:c+++
struct base
{
private:
  virtual void foo() = 0;
};

struct derived : public base
{
private:
  virtual void foo() override {}
};
```

private なメンバ関数をオーバーライドできる。

たまにそういうコードを見る。まれに便利。

# Go の場合

public なインターフェイスに private なメソッドを用意して。

```go:foo/foo.go
package foo

type Foo interface {
	private()
}
```

それを別のパッケージで実装すると。

```go:bar/bar.go
package bar

import 	foo "略/foo"

type bar struct {}

func (b *bar) private() {}

func NewBar() foo.Foo {
	return &bar{} //   *bar does not implement foo.Foo (missing private method)
}
```

エラー。
この「`private`」を「`NotPrivate`」に変えるとエラーが出なくなる。

そういうものらしい。
初心者　MySQL8に挑戦する 10

# 要はSQL文？

　見た目ちゃんと書けてるけど動かないうまくいかないMySQL、たぶん問題は「呼び方」なんだろう。今までちゃんと（？）エラー文が出たのは、公式のときだけだった。じつはサンプルによって、呼び出し方が微妙に違うんだよね。試しに昨日のコードで呼び出しの部分を公式っぽく書いたら、ほら、出た、例のヤツ。早速コピって検索掛けたら、うわ、私の記事（挑戦する5)がトップに出た。

　え、このエラーに苦しんでるのは私だけなのか？

　もちろんそんなことはない。貼り付けた長いエラー文に完全一致したのが私の記事。部分一致なら他にも質問があり、回答があるが、基本それは「配列」に関するものだ。

　配列かあ。まあ、確かに配列だよな。

　これはどうか。

https://qiita.com/ILTsubugai/items/4459e429b0b5a8b3b337

　昨日と同じか。simulatorはno event表示で、consoleにもプリントされない。エラー文なし。

　有効なSQL文を書けるようならないと・・・。

　no event, see you laterという表示は　snapshotが空だったら、というif文の反応なのだから、snapshotは取れたという判断でいいんだろうか。なら、なぜ空なんだろう。

## テーブル作るところからやり直すか･･･？

https://qiita.com/dongri727/items/4c5071f8dfb8b479d7b4


https://qiita.com/dongri727/items/ca17cab051ca0e2a1cce
Windows環境でDocker＋WordPressを爆速化

Dockerが遅すぎます……

WordPress+docker-composeで開発環境を構築していますが、ページを更新するだけで30秒くらいかかります。
初めは私のPCが弱すぎるだけかと思いましたが、検索してみると同じような方が結構出てきました。

## なぜDockerが遅くなるのか

例えば、以下のようにdocker-composeファイルのvolumesで同期処理を設定しているとします。
とあるブログ記事を参考に私が真似した設定です。

```rm:docker-compose.yml
version: '3'

services:
  wordpress:
    image: wordpress:latest
    ports:
      - "3000:80"
    depends_on:
      - mysql
    env_file: .env
    volumes:
        - ./public:/var/www/html/

  mysql:
    image: mysql:5.7
    env_file: .env
    ports:
      - "3306:3306"
```

これだと、更新するたびwordpressフォルダpublic以下全体とdocker側の`/var/www/html`が毎回同期処理を行ってしまいます。

これがDockerを遅くしてしまう原因です。

wordpressフォルダ全体だとかなりのファイル数になりますので、これを最小限に減らせばいいということですね。

## volumesの設定で同期を管理する

そこで変更したのがこちら。

```rm:docker-compose.yml
version: '3'

services:
  wordpress:
    image: wordpress:latest
    ports:
      - "3000:80"
    depends_on:
      - mysql
    env_file: .env
    volumes:
        - wp_data:/var/www/html/
        - ./public/wp-content/themes/mytheme:/var/www/html/wp-content/themes/mytheme/

  mysql:
    image: mysql:5.7
    env_file: .env
    ports:
      - "3306:3306"

volumes:
  wp_data:
```

テーマフォルダのみ同期することを前提にした設定です。
mythemeをお使いのテーマフォルダ名に変更してください。

プラグイン開発などする場合は適宜パスを変更すればOKですね。

一番下で`wp_data`という`volumes`を作り、`/var/www/html/`を突っ込んでおきます。
テーマフォルダ内しか同期しませんので、それより上の階層の変更は見なくなりますので注意です。

私の場合、これだけで即時ページ更新されるくらいに早くなりました。

## .wslconfigでWSL2使用メモリ容量の調整

さらに早くしたい場合には`.wslconfig`の中身を以下のように書き換えると良いそうです。

`.wslconfig`はエクスプローラーのアドレスバーに`%UserProfile%`と入力して表示されたフォルダ内に作ります。

```rm:.wslconfig
# Settings apply across all Linux distros running on WSL 2
[wsl2]

# Limits VM memory to use no more than 4 GB, this can be set as whole numbers using GB or MB
memory=4GB 

# Sets amount of swap storage space to 8GB, default is 25% of available RAM
swap=8GB
```

[Advanced settings configuration in WSL](https://docs.microsoft.com/en-us/windows/wsl/wsl-config#configure-global-options-with-wslconfig)

何をしているかというと、WSL2を使用するためのメモリ容量を増やしているわけですね。
初期値は2Gに設定されているようなので、ご利用のPCメモリ容量と相談しながら、4G、8Gと上げてみてください。
swapはメモリがパンパンになったときにストレージと置き換えるために確保する容量です。
Field Service Lightningの勉強メモ②

記載中
再勉強したのでまとめ。(過去の記事と重複あり)

## FieldServiceとは？
フィールドサービスとは、顧客からの問い合わせを受け、点検・修理・工事・配送など、現場に赴いて作業を行う業務のことです。

## Field Service Lightning概要
※以降、Field Service Lightningは、FSLと略します。
#### 登場人物
・顧客　→　問い合わせる人（例：エアコン購入したい）
・サポートセンター（例：一次受付、作業指示）
・ディスパッチャー（例：要員割り当てする人）
・サービス要員（例：ドライバーなど、実際に指示を受けて作業する人）
#### 利用の流れ
【機能紹介】Salesforce Field Service の『現場で使える』機能のご紹介より抜粋
https://www.youtube.com/watch?v=oUg7BQInhdQ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450298/43230176-9e9b-2653-bc30-868cb30712e0.png)
## ライセンス
https://www.salesforce.com/jp/products/field-service/pricing/
■従業員（社内）
　①MobileEmployee
　　　⇒フィールドサービス技術者など該当
　②Dispatcher
　　　⇒ディスパッチャー・割り当てる人が該当
　③FieldServiceLightningPlus
　　　⇒①＋②＋SalesCloud／ServiceCloud

■委託先（社外）
　・Contractor
　　⇒外部ユーザーのスケジューリング、作業指示やケース、取引先、取引先責任者の管理

＜おまけ＞ServiceCloudのライセンス表（FSLとの比較）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450298/7eeb2c87-c98f-8af7-5f11-983fd6e776cb.png)


## FSLのモバイルで出来ること
・色味やロゴ変更、地理情報などの精度設定
・プッシュ通知
・デイビュー（その日の作業を見る）
・フロー
・ナレッジ
・Chatter
・オフライン
・バーコードスキャナー
・サービスレポート（作業指示から出力可能）

## ディスパッチャーが出来ること
・スケジュール管理／最適化
・ガントチャートで稼働をTrack
・ロケーションに応じて分割
・Chatter

## データモデル
ざっくり以下の通り。


参考：FSLデータモデル
https://developer.salesforce.com/docs/atlas.ja-jp.field_service_dev.meta/field_service_dev/fsl_dev_soap_maintenance.htm
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450298/f2461b61-7395-d94d-8506-f71740cd63bc.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450298/70128b2a-09e6-5bb6-a16a-3290120dde4a.png)
Power Platform / Dynamics 365 の新しい環境を作成し、ユーザーを追加する方法

こんにちは。日本マイクロソフトの向井です。
こちらの記事では、Power Platform の新しい環境を作成し、環境にユーザーを追加し、セキュリティーロールを割り当てる方法をご紹介します。
**Dynamics 365 CE を使用するための環境もこちらの手順で追加することができます。**


# 0. 前提条件
環境に追加するユーザーに必要なライセンスが付与されていることを前提とします。
ライセンスの付与は[Microsoft 365 管理センター](
https://admin.microsoft.com/Adminportal/Home?source=applauncher#/homepage)からおこなうことができます。

# 1. 環境の作成
[Power Apps のホームページ](https://make.powerapps.com/)にアクセスします。
右上の歯車マークから、管理センターを選択し、[Power Platform 管理センター](https://admin.powerplatform.microsoft.com/home)にアクセスします。


左のメニューから環境を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/bfc9da09-e409-eb76-538e-0a52da51e127.png)


左上の新規をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/b297c820-bfa4-d5b7-3f4b-9ea2a86c85be.png)


環境の名前、地域、種類を設定します。
この環境でDataverse を使用する場合は、「この環境にデータベースを作成しますか？」をはいにします。
次へをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/77199c15-85a6-ac8d-03b2-b04484de2c03.png)


言語、通貨を選択します。
Dynamics 365 を有効にする場合は、「はい」を選択し、追加するアプリにチェックを付けます。
今回はSales Enterpriseにチェックを付けました。
保存をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/069e8104-c14d-2fef-6a0e-3aabe77093d9.png)



しばらく待つと、新しい環境が作成されます。
状態がReadyになれば、新しい環境を使用することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/1f3ed77c-2b08-8abf-4171-41bf78f3f418.png)




# 2. 環境にユーザーを追加し、セキュリティーロールを付与する
ユーザーを追加したい環境を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/db19194c-0c0b-96ec-22ff-44a76bd953e0.png)


ユーザーのすべて表示を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/cf2eb329-d1c9-a6f1-8d5e-976e0dd09eb1.png)


左上のユーザーの追加をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/fdc07d53-aa49-4ace-b190-b6febb0af3f1.png)


右に表示されたボックスに、ユーザーのメールアドレスを入力すると、ユーザーの候補が表示されるので選択します。
追加を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/c6755aa0-e97c-5f45-7ec7-d4c56d23085f.png)


ユーザーに割り当てたいセキュリティーロールにチェックを入れます。
今回は、この環境内の管理の権限を与えたいので、システム管理者の権限を付与しています。
保存をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/aa5dacdf-f2aa-1e62-6231-178b9374db19.png)


これでユーザーを環境に追加し、セキュリティーロールを付与することができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1004488/ab259a70-5b0e-f648-ce68-29f87e80094f.png)
discord.jsのバージョンを13から14に移行する

一昨日バージョンを下げるのを試してみてくださいという記事を書きましたが運営してるbotのコードをv14用に変えたのでついでに記事を書きます。
# Intentsがv13とは違う
今までは`FLAGS.GUILD_MESSAGES`のようなIntentsでしたがv14からは`GatewayIntentBits.MessageContent`のようなIntentsになります。
なので一昨日の記事のようなエラーが出るのです。
APIの変更により2022/8/31よりメッセージ内容読み取りのIntentsが必要になります。

https://discord.com/developers/applications

Discord Developersよりbotを選択
Bot→MESSAGE CONTENT INTENTをONにする
![IMG_20220721_144524.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734671/124fbcbd-82ff-a7cd-9e98-10948b63ae1e.jpeg)
![IMG_20220721_144556.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734671/646b32ea-8f57-4a73-cf09-b56ece793135.jpeg)

もちろんコード側のIntentsの変更も必要になります。
```js:index.js
const { Client, GatewayIntentBits, Partials } = require("discord.js")
const client = new Client({
  'intents': [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent
  ],
  'partials': [Partials.Channel]
});
```
自分はこのようにしました。
記事見ていただきありがとうございました
お名前.comSDサーバーにPhpMyAdminインストールで難渋した件をこう解決した

# 2021年のトラウマ
2021年にお名前.com SDサーバーのヘルプページ通りインストールを試みるも、何度チャレンジしても失敗

このサイトに救われた！
「お名前.comでphpMyAdminが入れられなくてお困りの方へ。バージョンを確認してみよう」
https://wp-firststep.com/onamae-com_phpmyadmin/

phpmyadomin4.6.6でインストールが成功
その備忘録に沿って2022年に別のサイトにインストールしようとすると失敗…

# 2022年トラウマ再び…

phpmyadomin4.6.6でインストール失敗

お名前.com SDサーバーのヘルプページを見ると、なんと適応バージョンが変わっている
phpmyadomin5.1.1
https://help.onamae.com/answer/9117

# 5.1.1インストール

ZIPダウンロード
https://www.phpmyadmin.net/files/5.1.1/

解凍したファイル群をアップロード
https://example.com

フォルダのパーミッション設定を755
phpファイルのパーミション設定を700

```
$ cd /root/phpMyAdmin
$ find . -name '*.php' -exec chmod 700 {} \;
```

# セットアップ画面表示

`https://example.com/phpMyAdmin/setup`

言語変更　→ 日本語
「新しいサーバー」クリック

[基本設定]
「SSLを使用する」に　✔︎　→ 「適用」

[作成されたサーバを編集]
[基本設定]
「このサーバの詳細な名前」　→ MySQLホスト名
「サーバのホスト名」　→ MySQLホスト名
[認証]
「config認証用のユーザ」　→ 空欄
[サーバ設定]
「リスト化したデータベースだけを表示する」　→ MySQLデータベース名
→ 「適用」

[設定ファイル]
デフォルト言語　→ 日本語
「ダウンロード」

# configファイルをアップロード

`https://example.com/phpMyAdmin/`にアップロード

# ログイン画面

`https://example.com/phpMyAdmin/`
にログイン画面が表示

ユーザ名　→ MySQLユーザ名
パスワード　→ MySQLパスワード

***
# 参考記事
https://qiita.com/saya_c/items/b31e9c6eadee4448c251
UniRx 　subjectの使い方

イベント駆動の動かし方を教わったので忘れないようにメモ

```C#:sample.cs
    Subject<Unit> subject = new Subject<Unit>();

    buttons[index]
    .OnClickAsObservable()
    .Subscribe(_ => subject.OnNext(Unit.Default))
    .AddTo(this);
```
```
Subject<Unit> subject = new Subject<Unit>();
```
subjectを定義するときに使用されている\<Unit>は型であり、クリックされたかどうか判定するだけのときに用いられる。
押しているときにスピードが上がるなどの機能をつける際には\<int>に変更したり複雑な動作が増える。

その後の動作は、
buttons[index]がクリックされたことを検知＆IObservable型に変換(OnClickAsObservable())されたら、Subscribe（購読者）がsubject（川）にOnNextでUnit.Defaultというそうめん（イベント）を流す、そしてAddTo(this)でこのインスタンスが消滅した際にSubscribeをやめる

#### 補足１
```
(_ => subject.OnNext(Unit.Default))
```
この書き方については
ラムダ式を用いた表現をしており

https://qiita.com/rawr/items/11790e9ea08a29d028a4

https://qiita.com/YSRKEN/items/5a36fb8071104a989fb8#q-%E3%83%A9%E3%83%A0%E3%83%80%E5%BC%8F%E3%81%AE--hoge%E3%81%A8_--hoge%E3%81%A3%E3%81%A6%E4%BD%95%E3%81%8C%E9%81%95%E3%81%86%E3%81%AE
の(() => ~)との違いを参照すると良い

#### 補足２
今回の例ではprivateでsubjectを定義したが、他のファイルで参照したい場合は
```
public IObservable<Unit> subject => onClickSubject;
private Subject<Unit> subject = new Subject<Unit>();
```
こうすることによってonCklickSubjectとして他ファイルで変更はできなく、参照のみできるようになる。
NextのImageでvia.placeholder.comの仮画像を表示させる

# 参考
https://github.com/vercel/next.js/issues/36977

# なぜこの記事を書いたか
https://via.placeholder.com の画像を `next/image` で読ませても表示されず、解決に苦戦したため

# やること
```next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    domains: ['via.placeholder.com'],
  },
}

module.exports = nextConfig
```

```imgDiv.tsx
import type { NextPage } from 'next'
import Image from 'next/image'

const ImgDiv: NextPage() = () => {
  return (
    <Image
      src="https://via.placeholder.com/100x100.png"
      width="100px"
      height="100px"
      alt="alt"
    />
  )
}

export default ImgDiv

```

via.placeholder.com でデフォルトで表示されるものがgifになっているため、末尾にpngをつける必要があるとのこと。
docker/ecr/lambda(python3.9)を使って、chromeとseleniumでスクレイピングする

serverless-chromeを使った記事は割とあるが、chromeのバージョンやらpythonのバージョンやらをいい感じにできない。
ecrを使うとlambdaにデプロイできるサイズ上限が一気にあがるので、普通に自前でchromeビルドして全部dockerimageに突っ込んじゃえばよくね？というもの。

https://github.com/adieuadieu/serverless-chrome

ローカルでdockerを使います。
python3.9のlambdaで動かします。

# ファイル
```
.
├── test.py
└── Dockerfile
```

# コード

この例ではchrome103とlambda(python3.9)

```Dockerfile:Dockerfile
# chromeのセットアップ用
FROM public.ecr.aws/lambda/python:3.9 as build
RUN yum install -y unzip && \
    curl -Lo "/tmp/chromedriver.zip" "https://chromedriver.storage.googleapis.com/103.0.5060.53/chromedriver_linux64.zip" && \
    curl -Lo "/tmp/chrome-linux.zip" "https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F1002910%2Fchrome-linux.zip?alt=media" && \
    unzip /tmp/chromedriver.zip -d /opt/ && \
    unzip /tmp/chrome-linux.zip -d /opt/

# 完成物
FROM public.ecr.aws/lambda/python:3.9
RUN yum install atk cups-libs gtk3 libXcomposite alsa-lib \
    libXcursor libXdamage libXext libXi libXrandr libXScrnSaver \
    libXtst pango at-spi2-atk libXt xorg-x11-server-Xvfb \
    xorg-x11-xauth dbus-glib dbus-glib-devel -y
RUN pip install selenium
COPY --from=build /opt/chrome-linux /opt/chrome
COPY --from=build /opt/chromedriver /opt/
COPY test.py /var/task
CMD [ "test.handler" ]
```

```python:test.py
import json
from selenium import webdriver

def handler(event, context):
    options = webdriver.ChromeOptions()
    options.binary_location = '/opt/chrome/chrome'
    options.add_argument('--headless')
    options.add_argument('--no-sandbox')
    options.add_argument("--disable-gpu")
    options.add_argument("--single-process")
    options.add_argument("--disable-dev-shm-usage")
    options.add_argument("--disable-dev-tools")
    driver = webdriver.Chrome(options=options, executable_path="/opt/chromedriver")
    driver.get("https://example.com")

    print(driver.title)

    return {
        'statusCode': 200,
        'body': json.dumps("done.")
    }
```

print削除したりreturnを適当に変えたりしてください。
オプションとかはseleniumのドキュメント等で別途自分が必要そうなものを調べてみてください。

# ビルド
```zsh
% docker build -t {イメージ名} .
```

## m1macの場合
```zsh
% docker build --platform linux/amd64 -t {イメージ名} .
```

# ecrにプッシュ

get-login-password のコマンドはマネジメントコンソールでecrのリポジトリを作成したあと見れます。

```zsh
% aws ecr get-login-password --region {リージョン} --profile {使うプロファイル} | docker login --username AWS --password-stdin {awsアカウントid}.dkr.ecr.{リージョン}.amazonaws.com
% docker tag {イメージ名}:{タグ} {awsアカウントid}.dkr.ecr.{リージョン}.amazonaws.com/{イメージ名}:{タグ}
% docker push {awsアカウントid}.dkr.ecr.{リージョン}.amazonaws.com/{イメージ名}:{タグ}
```

# lambda
マネジメントコンソールからecrにあげたイメージをもとにしたlambdaを適当にポチポチ作る。

# 補足
seleniumで色々やろうとすると重いからhtmlの解析自体はbeautifulsoupとかのパーサー使った方がパフォーマンスが云々、みたいなのもあるっぽいのでそこは必要に応じて。


# 参考

https://zenn.dev/445/articles/29aff4c8ce2e01

https://github.com/umihico/docker-selenium-lambda/

https://stackoverflow.com/questions/65429877/aws-lambda-container-running-selenium-with-headless-chrome-works-locally-but-not
親要素がoverflow: hiddenの時、どうにかして子要素をoverflow: visibleにしてはみ出したい

# どういうこと？
こんなことしたいことありませんか？
[![Image from Gyazo](https://i.gyazo.com/c9a36d2ff1f5b157c4e7fa64e0098e44.png)](https://gyazo.com/c9a36d2ff1f5b157c4e7fa64e0098e44)

# なぜできないの？
上記画像はElement-plusのel-tabとel-badgeを使っていますが、el-tabの全体に`overflow: hidden;`がかかっており、はみ出した部分が消えていました。
[![Image from Gyazo](https://i.gyazo.com/150d9d045d2ad0062e8e4afdd1176d38.png)](https://gyazo.com/150d9d045d2ad0062e8e4afdd1176d38)

# じゃあどうするの？
`overflow: hidden;`を消して以下を設定します。
```html
# うまくいかない例
<div id="parent" style="overflow: hidden">
 <div id="child">！</div>
</div>

# うまくいく例
<div id="parent" class="clearfix">
 <div id="child">！</div>
</div>

<style lang="css">
  .clearfix:before,
  .clearfix:after {
    content: ".";    
    display: block;    
    height: 0;    
    overflow: hidden; 
  }
  .clearfix:after { clear: both; }
  .clearfix { zoom: 1; } /* IE < 8 */
</style>
```

# Element-plusではどうするの？
el-tabタグは自動で展開されるので展開後のクラスを変更する必要があります。
```html
      <el-tabs type="border-card" class="clearfix">
        <el-tab-pane v-for="state in states" :key="state" :label="state">
          <template #label v-if="state == '送信失敗'">
            <el-badge
              value="！">
            <span>{{state}}</span>
            </el-badge>
          </template>
          <!-- content -->
        </el-tab-pane>
      </el-tabs>

<style lang="scss">
  // 展開されたあとのクラス名で上書きする
  .el-tabs__nav-scroll:before,
  .el-tabs__nav-scroll:after {
      content: ".";    
      display: block;    
      height: 0;    
      overflow: hidden; 
  }
  .el-tabs__nav-scroll:after { clear: both; }
  .el-tabs__nav-scroll { overflow: visible; } // 明示的にvisibleにする
  .el-tabs__nav-wrap { overflow: visible; } // 明示的にvisibleにする
</style>
```

# 参考資料
「overflow hidden child visible」で検索したら出てきました。

https://stackoverflow.com/questions/3387489/make-child-visible-outside-an-overflowhidden-parent
カラムの値ごとにeach文を回す方法【Rails】

:::note alert
本記事の内容は**N+1問題**を引き起こしています。
改善策は[こちらの記事](https://qiita.com/ysk91_engineer/items/12a8709aeb251afcfc5f)を参考にしてください。
:::

# 目的
hogesテーブルをジャンルごとに分けて出力したい。

# 目標物の確認
今回作成したいページはこのような見た目です。
![スライド1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2688241/8f231299-c076-09ef-5f62-913aaca12367.jpeg)


# MCVの準備
今回の目標物を実現するために最低限必要なファイルは以下です。

## モデル
**Hogeモデル**：メインとなるhogesテーブルのモデルです。
`id`カラム：データ作成と共に自動付与される値
`content`カラム：表示する内容
`genre_id`カラム：今回ポイントとなるカラムです。genreモデルと紐づけるためのカラムです。

**Genreモデル**：Hogeモデルの`genre_id`カラムと紐づけるためのモデルです。
`id`カラム：データ作成と共に自動付与される値。hogeモデルの`genre_id`カラムと紐づけます。
`name`カラム：genre名を設定します。

![スライド2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2688241/c5d86f35-3808-b8b6-6f99-825ef7f7086b.jpeg)

## コントローラ
```ruby:コントローラファイル
# アクション内で
@genres = Genre.all
```

## ビューファイル
```html:ビューファイル
<% @genres.each do |genre| %>
  <p><%= genre.name %></p>
  <% hoges = Hoge.where(genre_id: genre.id) %>
  <table>
  <tr>
    <td>id</td><td>content</td>
  </tr>
  <% hoges.each do |hoge| %>
    <tr>
      <td><%= hoge.id %></td>
      <td><%= hoge.content %></td>
    </tr>
  <% end %>
  </table>
<% end %>

```

# 解説
2段階で考えます。
まず、コントローラで定義した`@genres`に対してeach文を回します。
`<p><%= genre.name %></p>`の部分ではジャンル名を表示しています。
each文の中で、`hoges`という変数を定義します。
`hoges`は`where`メソッドにより、**現在each文で回っているgenreのidをgenre_idにもつ配列**として定義されます。
そしてその`hoges`に対してeach文を回します。
`hoges`に対するeach文が終了すると、`@genres`の1周目のeachが終了します。
続いて、`@genres`の2周目のeachが開始します。

これにより、`hoges`テーブルの`genre_id`ごとにeach文を回すことができました。

![スライド3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2688241/71fd7b0a-ef81-1f5e-d958-73ae614383e3.jpeg)

# まとめ
今回はカラムの値ごとにeach文を回す方法を紹介しました。
2段構造でeach構文を作るという少し複雑な方法ですが、ぜひ試してみてください。
【ServiceNow】ガイドセットアップの使い方について

# はじめに
みなさんこんにちは、三井情報株式会社です。

今回は[ガイドセットアップ](https://docs.servicenow.com/en-US/bundle/sandiego-platform-user-interface/page/administer/core-configuration/concept/guided-setup.html)の使い方についてご紹介致します。
ガイドセットアップは特にプラグインをインストールする必要はなく、各機能の基本的な構成や使い方などを知ることができます。新規ユーザーが個人インスタンスを取得した際に各機能の使い方を１から学ぶのに適しています。



# ガイドセットアップの紹介
①	ガイドセットアップの種類
ガイドセットアップは現在以下のような種類があり、タイトルにある機能の解説およびカスタマイズを行うことができます。
・ITSM ガイド付き設定
・カスタマーサービスマネジメントのガイドセットアップへようこそ
・ナレッジ管理のガイド付き設定にようこそ
・パフォーマンス分析のガイド付き設定にようこそ
・エージェントワークスペースガイドセットアップ
・スポットライトのガイドセットアップへようこそ
・AI Search ガイド付き設定
・正規化データサービス
・Conversational Interfaces ガイド付き設定
・IT Operations Management のガイド付き設定
・構成可能ワークスペースのガイド付き設定

この記事では「エージェントワークスペース」のガイドセットアップを例としてご紹介致します。

②	エージェントワークスペースとは
[エージェントワークスペース](https://docs.servicenow.com/en-US/bundle/sandiego-it-service-management/page/product/itsm-workspace/concept/itsm-agent-workspace.html)とはITSMをより効率的に管理できる機能です。
複数のインシデント、問題、変更などをより効率的なUIで確認することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599899/1b3c6b2a-5e62-5a0a-e3cf-9f49b8fffcd0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599899/84fd598c-b2c6-07e5-b901-b3a4696de919.png)
 

③	エージェントワークスペースガイドセットアップ
フィルタナビゲータからガイドセットアップを選択すると以下の「はじめに」の画面が表示されます。「続行」を押下して各テーマを選択する画面へ移ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599899/fa4dda79-57ec-b505-dd92-b19485ab63d7.png)


「続行」を押下すると各テーマを選択する画面が表示されるので、編集したい項目の「続行」ボタンを押下します。
※ステータスが「未開始」の場合は「ようこそ」、「処理中」の場合は「続行」、「完了」の場合は「編集」と表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599899/016d7651-1c96-8439-5a12-a8fc52d2cbcd.png)


エージェントワークスペースのテーマには以下があります。
・自社用カスタム設定とテーマ
・タブの設定
・リスト
・フォーム
・追加フォームの構成
・検索
・ワークスペースの通知
・高度な作業アサイン
・ワークスペースエージェントチャット
・ランディングページ
・OpenFrame
・Playbook

今回は「自社用カスタム設定とテーマ」を編集します。

・自社用カスタム設定とテーマ
会社のロゴと2色のテーマ設定を使い自社用カスタム設定とテーマをカスタマイズできます。「構成」を押下して編集します。
※既に完了している場合でも「構成」を押下して何度でも編集できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599899/7014ad2f-b61d-3bdf-e993-fab8651ad4d4.png)


今回は「ブランドカラー1」「ブランドカラー2」を変更してみます。
変更して「更新」後にワークスペースエージェントを開いて確認します。
※編集するためにはアプリケーションスコープが「Agent Workspace」になっている必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599899/a9535635-8f0a-0b8b-26c4-e0a27418e5a3.png)


色が変更されました。「ブランドカラー1」がヘッダーに、「ブランドカラー2」がメニューに反映されています。
※元に戻す場合は先程設定した「ブランドカラー」の値を削除します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599899/fff1acf9-4e4a-1060-4a8d-3867815d683b.png)



# おわりに
今回はガイドセットアップの基本的な使い方をご紹介させていただきました。
新しい機能を使い始める際はガイドセットアップから始めると理解が深まると思います。
最後まで読んでいただき、ありがとうございました。
プロファイルからレコード タイプを削除するURLハック

[一部のプロファイルにデフォルトで設定されているレコード タイプを削除したい](https://salesforce.stackexchange.com/questions/143476/i-want-to-delete-the-record-type-which-is-default-to-the-some-profiles)

Chatter Free ユーザーなどの特定のプロファイルには、プロファイルのレコード タイプ セクションの編集リンクをクリックしても削除できないレコード タイプを割り当てることができます。これにより、レコード タイプが削除されなくなります。プロファイルからレコード タイプを削除するには、URL の「ハック」を実行する必要があります。


[URLハックのリンクを参照してください。]( https://success.salesforce.com/answers?id=90630000000h2pXAAQ)


上記のリンクのコンテンツの下に追加して、回答が確実に完了するようにします。

Chatter Free ユーザーなどの特定のプロファイルには、プロファイルのレコード タイプ セクションの編集リンクをクリックしても削除できないレコード タイプを割り当てることができます。これにより、レコード タイプが削除されなくなります。プロファイルからレコード タイプを削除するには、URL の「ハック」を実行する必要があります。

まず、ユーザー インターフェイスの [拡張プロファイル ユーザー インターフェイスを有効にする] チェックボックスをオフにする必要があります。

セットアップ > カスタマイズ > ユーザー インターフェース > セットアップ セクション > 拡張プロファイル ユーザー インターフェースを有効にするの選択を解除 > 保存

次の手順に進みます。

1) このクリックパスに従ってください:

[設定] > [ユーザーの管理] > [プロファイル] > [システム管理者] > [レコード タイプ] に移動 > 問題のオブジェクトのセクションの [編集] リンクをクリックします。

この時点で、オブジェクトのレコード タイプ設定の編集ページが表示されます。

2) ブラウザのアドレス バーから URL をコピーし、メモ帳に貼り付けます。

```
https://naX.salesforce.com/setup/ui/profilerecordtypeedit.jsp?id=xxxxxxxxxxxxxxx&tid=Opportunity&pn=System+Administrator&retURL=/xxxxxxxxxxxxxxx
```

3）このクリックパスに従ってください

[設定] > [ユーザーの管理] > [プロファイル] > [Chatter Free ユーザー]

4)ブラウザのアドレスバーの URL から 15 桁の Salesforce ID をコピーします。

yyyyyyyyyyyyyy

5)ここで、Edit Record Type Settings Opportunity ページの ID (URL の「jsp?id=」の直後の最初の ID 番号) を Chatter Free User の ID に置き換えます。

This: 
```
https://naX.salesforce.com/setup/ui/profilerecordtypeedit.jsp?id=xxxxxxxxxxxxxxx&tid=Opportunity&pn=System+Administrator&retURL=/xxxxxxxxxxxxxxx
```

Should now look like this: 
```
https://na5.salesforce.com/setup/ui/profilerecordtypeedit.jsp?id=yyyyyyyyyyyyyyy&tid=Opportunity&pn=System+Administrator&retURL=/xxxxxxxxxxxxxxx
```


xxxxxxxxxxxxxxx は yyyyyyyyyyyyyy に置き換えられました

6) 変更した URL を取得して、ブラウザのアドレス バーに配置し、Enter キーを押します。

*************これにより、システム管理者の編集ページに移動したように見えますが、正しい場所にいます.**************** **

7) 値を強調表示して削除を選択すると、値を削除できるようになります。デフォルトのレコード タイプを好きなものに切り替えて、保存をクリックします。

ここで行う必要があるのは、必要な他のプロファイルに対してこれを繰り返し、レコード タイプを非アクティブ化して削除することだけです。
【ServiceNow】Virtual Agent_コンポーネント

## はじめに
みなさんこんにちは、三井情報株式会社です。

今回は、VirtualAgentで「リンク」コンポーネントを使用すると、リンク先の画像や動画のプレビューが表示されることを検証しました。


### リンクの展開（プレビュー）
VirtualAgentでリンクコンポーネントを使用するとプレビューが表示されるようになりました。ここではその設定方法を記載します。


#### 「ホスト名許可リスト」にホスト名やドメイン名を追加
①	フィルターナビゲーターで「sys_cs_hostname_allow.list」と検索し、「新規」を押下。
　※既にインスタンスやServiceNow HP、YouTubeの５件は設定されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599953/24c1b5de-cecf-b9cf-7ad2-0f12b75b6f55.png)

②	プレビューしたいリンクのホスト名かドメイン名のレコードを新規作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599953/2466657a-cda3-4de0-5bd9-8ef595159b68.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599953/a053ea03-a85c-573e-ea46-c6f9f0856020.png)


#### 仮想エージェントデザイナーに移動し、トピックを作成
①	仮想エージェント＞デザイナー を開き、トピックを選択または新規作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599953/b0d819a6-3d36-3c6f-59eb-2ee0b6f9d136.png)

②	トピックを開き、コンポーネントのボット応答から「リンク」をノードに追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599953/a01e6f95-7f63-df24-9e2e-e8fd99eadd58.png)

③	応答プロパティで各項目を設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599953/bf9cb6ed-f2b0-1a37-2ee8-0e46c7f036be.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599953/5de7a1a1-800d-7eb7-9034-b63c95eed114.png)

④	項目の設定が完了したら公開し、サービスポータルで確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599953/c2e55bbc-f224-1fbd-3f98-67e7a7b50119.png)

⑤	プレビューが表示されました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599953/c284141b-58b4-acbb-eda1-09dcbaee5252.png)
※YouTubeのような動画のリンクではチャットボットのウィンドウ内で再生・一時停止などの操作が可能です。


### 注意事項
利用する上で注意が必要な点をこちらにまとめています。
①	プレビューが表示できるのはドメイン名が「.com」のリンクのみです。
②	リンク先ページにHTMLのOGタグが設定されている必要があり、VirtualAgentでは画像・ビデオ・テキストのみ対応しています。
③	YouTube動画の埋め込み部分では、日本語やハイフンなどの特殊文字は文字化けします。
※ServiceNow UIページにOGタグをつける方法やリンクの展開（プレビュー）を無効化する方法は[公式ドキュメント](https://docs.servicenow.com/bundle/sandiego-servicenow-platform/page/administer/virtual-agent/concept/va-link-unfurling.html)をご参照ください。


## おわりに
いかがでしたでしょうか。プレビューが表示されることによりユーザーの興味や関心を惹きやすくなるので、研修の動画やナレッジなど閲覧させたいものに目を通してもらいやすくなります。また、動画についてはチャットボットのウィンドウ内で閲覧が可能なため、迅速に次の会話につなげることができます。ぜひ利用してみてください。
タスク一覧の取得など、MySQLに接続できない時のエラー解決法(IPアドレス)

# エラーの経緯
当時はタスク管理のwebアプリを開発中でした。
その日の作業を終えてパソコンを閉じ、翌日に再びパソコンを開いて更新をかけると、タスクの一覧が取得できないエラーが発生していました。
動作確認をしてからパソコンを閉じていたので、コードの誤りなどはありません。

# エラー文
net::ERR_CONNECTION_REFUSED
cannot read properties of null (reading 'end')

# 原因
このエラーの原因は、PCのIPアドレスが前日と自動で変わっていたためでした。

# 解決法
.envファイルのIPアドレスの設定をPCのIPアドレスに合わせて変更します。
また、MySQL Workbench等のクライアントアプリケーションを使用している場合も、IPアドレスの設定を変える必要があります。

# ＊IPアドレスの確認法
・Windowsの場合
1.「Windows」キーを押しながら「X」キーを押し、表示された一覧から「コマンドプロンプト」をクリックします。 
2.「ipconfig」と入力して［Enter］キーを押します。 
3.表示されたIPv4アドレスまたはIPアドレス（IP Address）を確認]

・macOSおよびOS X（Mac）の場合
1.アップルメニューの［システム環境設定］を開き、［ネットワーク］アイコンを選択します。
2.［Ethernet］を選択します。
3.表示されたIPアドレス（またはIPv4 アドレス）を確認します。
devcontainer内でhadolintを使う

あまりないとは思いますが、備忘録のため。
タイトルの通りです。
~~すでにContainerで配布されてるんだからそっち使えとかある気もしますが...~~
`docker pull hadolint/hadolint:latest-debian`とか。。。

```dockerfile:.devcontainer/Dockerfile
# ユーザ名はいい感じにしてやってください。
# devcontainer.json内のremoteUserに合わせるとよさそう
USER node
RUN export LANG=C.UTF-8 && export LC_ALL=C.UTF-8 && export LANGUAGE=C.UTF-8 \
    && apt-get update \
    && export DEBIAN_FRONTEND=noninteractive \
    && apt-get -y install --no-install-recommends  haskell-stack \
    && stack upgrade --binary-only \
	&& cd /home/node \
    && git clone https://github.com/hadolint/hadolint --depth=1 \
	&& cd hadolint \
	&& stack install \
	&& cd ../ \
	&& rm -rf hadolint
```

また、`.devcontainer/devcontainer.json`内、`extensions`に`"exiasr.hadolint"`を追加しておくといい気がします。
あと、使い終わったらhaskell-stackはもう使わない気がするのでパージしておくのもいいかもしれません。

#### その他
2022年7月21日 追記: 
- haskell-stack入れた箇所抜けてたので追加しました。
- `"exiasr.hadolint"`追加しておくといいよという内容を追記しました。
vueで表示を切り替えた項目をフェードインさせる

## 実現したいこと
要素の表示フラグを切り替えた際に、アニメーションでフェードイン/フェードアウトさせる

## vue側の記述
isVisibleは表示状態を管理するフラグ
```vuejs
    <div
        :class="{
            fadeIn: isOpen,
            fadeOut: !isOpen,
            'open':isOpen,
            'close':!isOpen,
        }"
    >
```

## css側の記述
```css
.open {
  right: -10px;
  width:200px;
}
.close {
  right: -180px;
  width:200px;
}

.fadeIn {
    animation: fadeIn 1s;
}
@keyframes fadeIn {
    from {
        transform: translateX(100px);
    }
    to {
        transform: translateX(0px);
    }
}

.fadeOut {
    animation: fadeOut 1s;
}
@keyframes fadeOut {
    from {
        transform: translateX(-100px);
    }

    to {
        transform: translateX(0px);
    }
}