OmniAICreator/Qiita-1.07M huggingface dataset:
Parse ServerとNCMBのACLの相違点

[ニフクラ mobile backendは3月末で終了します](https://mbaas.nifcloud.com/info/2023/12/post-171.html)。

ニフクラ mobile backendからの移行先として、お勧めしているのが[Parse Server](https://parseplatform.org/)です。設計思想が近く、変更するコード量が少なく済むのではないかと思います。

アプリのデータをセキュアに、安全に扱うために欠かせないのがACL（アクセスコントロール）です。ACLもParse ServerとNCMBは似ていますが、幾つの違いがあります。本記事では、その相違点を紹介します。

## ACLは全部で3段階

NCMBと同じく、Parse ServerのACLは3つの情報で管理します。

| 項目   | 意味                             |
| ------ | -------------------------------- |
| Public | 未ログインユーザーを含め、誰でも |
| User   | 認証済みユーザー                 |
| Role   | グループ |

そして、各項目に読み取り・書き込み権限が指定できます。読み取りは `read` 、書き込みは `write` です。

| 権限  | 可能な操作 |
| ----- | ---------- |
| read  | 読み込み   |
| write | 編集・削除 |

## 各項目の指定方法

Publicは `*` 、 UserはユーザーのobjectId、Roleは `role:(ロール名)` として、JSONで指定します。以下の場合、データの読み込みは誰でも可能で、ユーザーIDが `ABCDEF` と `012345` のユーザーと `admin` ロールに属するユーザーは編集可能という意味になります。

```json
{
	"*": {
		"read": true
	}
	"ABCDEF": {
		"write": true
	},
	"012345": {
		"write": true
	},
	"role:admin": {
		"write": true
	}
}
```

## クラスレベルの権限

NCMBとParse Serverの両方にクラスレベルの権限が付与できます。これはNCMBとParse Serverで多少異なります。

### NCMB

NCMBのクラス権限は以下のようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/8a6b42fa-5e18-7194-69ca-2587ba688307.png)

| 項目   | 意味                             |
| ------ | -------------------------------- |
| Public | 未ログインユーザーを含め、誰でも |
| User   | 認証済みユーザー                 |
| Role   | グループ |

それぞれに対して指定できる権限は以下の通りです。

- 読み込み
- 検索
- 更新
- 作成
- フィールド追加

### Parse Server

Parse Serverのクラス権限は以下のようになっています。

| 項目          | 意味                             |
| ------------- | -------------------------------- |
| Public        | 未ログインユーザーを含め、誰でも |
| Authenticated | ログインユーザー                 |
| User          | 特定のユーザー                   |
| Role          | グループ                         |

それぞれに対して指定できる権限は以下の通りです。

- 簡易
	- 読み込み
	- 編集
	- フィールド追加
- 詳細
	- 読み込み
		- GET
		- Find
		- Count
	- 書き込み
		- Create
		- Update
		- Delete
	- フィールド追加

## フィールドのプロテクト

これはParse Server独自の機能です。

- 全員に公開するフィールド
- 特定のユーザーに公開するフィールド
- 特定のロールに公開するフィールド

フィールドのプロテクトは、 **自分だけが見られるといった指定はできない** ので注意してください。

## フィールド名が違う

NCMBはaclですが、Parse ServerはACLなので注意してください（大文字、小文字の違い）。

## まとめ

Parse Serverでは、NCMBよりも細かくACLを設定できるようです。この点においてはNCMBの機能は網羅しているようなので、移行する際に機能を削らずに済みそうです。

また、Parse Serverでは返却するフィールドを絞り込むこともでき、データ受信量を削減できます。自分でサーバーを立てる必要があるという点は懸念点ですが、移行先として検討してください。

[Parse Platform](https://parseplatform.org/)
Parse Server Swift SDKの使い方（ファイルの取得方法）

[ニフクラ mobile backendは3月末で終了します](https://mbaas.nifcloud.com/info/2023/12/post-171.html)。

ニフクラ mobile backendからの移行先として、お勧めしているのが[Parse Server](https://parseplatform.org/)です。設計思想が近く、変更するコード量が少なく済むのではないかと思います。

Parse ServerではiOS向けにSDKを提供していますが、Swift SDKについてはあまりドキュメントが充実していません。そこで、各機能の使い方を解説します。今回はファイルストア相当として使える、ファイルフィールドの取得方法を紹介します。

## Swift SDKとObjective-C SDKのどちらを使うべきか

Parse ServerにはSwift SDKと[Objective-C SDK](https://docs.parseplatform.org/ios/guide/)が用意されています。機能的にいうと、Objective-C SDKの方が多いようです。しかし、公式メッセージとしてはSwift SDKを使っていくのを奨励しています。

## Parse Server Swift SDKのインストール

Swift SDKのインストールは、CocoaPodsやCarthageなどが使えます。しかし、一番簡単なのはXcodeのPackage Dependenciesを使う方法でしょう。

XcodeのFileメニューより、Add Package Dependenciesを選択して、出てきたダイアログで以下のURLを指定します。

```
https://github.com/parse-community/Parse-Swift.git
```

そして、利用するファイルでSDKをインポートします。

```swift
import ParseSwift
```

## 初期化

SwiftUIの例です。初期化は `(アプリ名)App.swift` にて行います。そして、初期化は `ParseSwift.initialize` にて行います。指定するアプリケーションID、クライアントキー、サーバーURLはそれぞれParse Serverを立ち上げる際に指定したものを使います。

マスターキーも指定できるようですが、アプリ側では使わない方が良いかと思います。

```swift
import SwiftUI
import ParseSwift

@main
struct ParseDemoApp: App {
    init() {
        ParseSwift.initialize(applicationId: "YOUR_APP_ID", clientKey: "YOUR_CLIENT_KEY", serverURL: URL(string: "https://example.com/parse")!)
    }
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
```

## Parse Serverにはファイルストアはありません

Parse ServerにはNCMBでいうファイルストア相当の機能はありません。ファイルはデータストアの1フィールドに紐付けて保存します。また、取得できるのはURLのみで、実データの取得はURLからダウンロードする形になります。

今回は例として、以下のような構造体を定義しています。

```swift
import ParseSwift

struct Item: ParseObject {
    // 以下は必須です
    var objectId: String?
    var createdAt: Date?
    var updatedAt: Date?
    var ACL: ParseACL?
    var originalData: Data?

    // 以下は自分で定義したフィールドです
    var name: String?
    var file: ParseFile?
}
```

そして、以下のようにデータを取得できます。

```swift
let query = Item.query()
let items = try? query.find()
if ((items) != nil) {
		if items![0].file != nil {
			print(items![0].file?.url ?? "No file")
		}
}
```

ここで取得できる `url` は以下のようになります。

```
https://example.com/parse/files/appId/your_file.jpeg
```

後はこのURLを使って画面に表示するか、実データをダウンロードして処理する形になります。

## まとめ

Parse ServerとNCMBでは、バイナリファイルの扱いがまったく異なります。ファイルに対するACLという概念はありませんので、URLが分かってしまうと誰でもアクセスできます。NCMBで言えば、標準でファイルストアのHTTPS公開が有効になった状態と言えます。

移行する場合には、 `files` のようなクラスを作成し、そこにファイルを紐付ける形になるでしょう。もちろん、Parse Serverらしくデータストアの1フィールドとして保存も可能です。

ファイルストアについては違いがあるとはいえ、データの管理方法などはParse ServerとNCMBで似ています。他のmBaaSと比べると、修正量はそこまで多くないと思われます。載せ替え先として検討に挙げてください。

[parse\-community/Parse\-Swift: The Swift SDK for Parse Platform \(iOS, macOS, watchOS, tvOS, Linux, Android, Windows\)](https://github.com/parse-community/Parse-Swift)
CloudFunctions + BigQueryでpandasを使った一括インサートの備忘録

## 概要
- python
- csvまたはtsvファイルのデータをBigQueryのテーブルにインポートする
    - pandas DataFrameにcsvのデータをいれて、そのままBigQueryに流し込みたい


## 起きた事
dataframeに入れたcsvデータをto_bgqを使い、一括でBigQueryに流し込む
```
df = pandas.read_csv(file_path, encoding='UTF8')

df.to_gbq(
    "project.test_dataset.table",
    table_schema=[スキーマ定義配列],
    if_exists='append'
)
```
結果、以下の様なエラー

```
Reason: 400 Error while reading data, error message: Failed to import values for column 'name': Unexpected end of stream"
```

- **9400件あたりを超えるか超えないか**が、エラー発生の境目になっているっぽいことがわかる
- DataFrameへのreadは問題ない(全レコード取り込めている)
- CloudFunctionsメモリも問題ない
- to_gbqでこける
→ **to_gbqでのinsert処理はストリーミングらしいので、[割り当て上限](https://cloud.google.com/bigquery/quotas?hl=ja#streaming_inserts)に引っかかっている**


## やったこと
DataFrameを特定行ごとに分割して処理する
```
# 一度のインサートが約9400以上で失敗するので、データフレームを分割して処理
df = pandas.read_csv(file_path, encoding='UTF8')

split_count = 8000
insert_count = 0
splited_df = [df[i:i+split_count] for i in range(0, len(df), split_count)]

for part in splited_df:
    logger.info(f"split insert:{insert_count}〜{insert_count + split_count}")
    part.to_gbq(
        "project.test_dataset.table",
        table_schema=[スキーマ定義配列],
        if_exists='append'
    )
    insert_count += split_count
```

これで解決。
Android ストレージ内の暗号化済みSQLiteをDBeaverで接続する

私がAndroidStudioでAndroidアプリの開発を行っているとき、SQLiteを使っていてデータの内容を確認できないのかなと思い、[こちら](https://gist.github.com/thecodingcod/a3987ddde8892d1a6bda8bd94febb7d0)（←リンク）を参考にしてDBeaver（DB管理ツール）にデータの内容を表示することができたので備忘録としてまとめておきます。

* SQLiteとは
ざっくり説明するとSQLiteはAndroidのSDKが標準サポートしているデータベースです。詳しくは以下参照。

https://gihyo.jp/dev/serial/01/androidapp/0008

## DBのパスワードを取得できるか
後々に使うので一旦飛ばしてもいいですが、DBのパスワードを取得できないと後でつまずくので先に取得できるかを確認します。
開発環境によって違うとは思いますが、私はDatabaseオブジェクトを取得する部分をデバッグモードで止めてパスワードを確認しました。

## SqlCipherのドライバのライブラリをダウンロード
以下から``sqlite-jdbc-3.44.2.0.jar``をダウンロード。（数字は最新版）

https://github.com/Willena/sqlite-jdbc-crypt/releases

* SqlCipherとは
SqlCipherはSQLiteデータベースを拡張し、同じインターフェイスで暗号化されたローカルデータストレージを提供するオープンソースのライブラリです。

## DBeaverでカスタムドライバの作成
DBeaverのメニューバーからデータベース⇒ドライバマネージャー⇒新規をクリック。
![001..jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/a0530301-f947-449e-400d-3c21d23e6c48.jpeg)
![002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/1dbb1f43-31f8-26b6-68ce-775db649f669.jpeg)

### 設定タブ
* ドライバ名 = ``SqlCipherV3``
* ドライバタイプ = ``Generic``
* クラス名 = ``org.sqlite.JDBC``
* URLテンプレート = ``jdbc:sqlite:{file}``
* 他は設定不要
![003.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/f67fb706-45e8-64ea-540a-04ccba20e1dd.jpeg)

### ライブラリタブ
* ファイルを追加からダウンロードしておいた``sqlite-jdbc-3.44.2.0.jar``を選択
![005.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/ca71fe8e-c445-b20e-dd4f-a535a4b83646.jpeg)

### 接続プロパティタブ
右クリックから新しいプロパティを追加
* cipher = ``sqlcipher``
* fast_kdf_iter	= ``2``
* hmac_algorithm = ``0``
* hmac_pgno = ``1``
* hmac_salt_mask = ``0x3a``
* hmac_user = ``1``
* kdf_iter = ``64000``
* legacy = ``3``
* legacy_page_size = ``1024``
* key　は事前に取得しておいたパスワードを設定するがここでは空欄
![006.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/eab4aa48-1e1d-f04b-5d11-81abc23450b8.jpeg)

ここまで設定したらOK

## Android端末からDBファイルを取得する
AndroidStudioでツールバーのWindow⇒Device Manager⇒デバイス右のオプションからOpen in Device Explorer
開発環境によって違うとは思いますが、私は``/data/data/(プロジェクト名)/databases/``にあったのでそれをPCにダウンロード。
![007.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/19e70f50-8554-53d7-91b2-a5b05fdf7a51.jpeg)

## いざ接続
新しい接続⇒SqlCipherV3を選択して次へ
![008.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/e6f8d150-5a10-a422-d234-43ed30a9695d.jpeg)

### 一般タブ
パスに取得しておいたDBファイルを選択
![009.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/f52dd10d-d088-2061-4477-4560f3363bbd.jpeg)

### ドライバのプロパティタブ
keyプロパティに最初に確認しておいたパスワードを設定⇒テスト接続して問題なければ終了
![010.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/bace0a67-d72d-6855-3dc5-f83134cf4eea.jpeg)

## まとめ
苦戦しましたがデータベース表示することができました。
データ内容を確認したいときにどうぞ試してみてください。
![011.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2480832/589fcc01-8243-0834-d3b6-20f6af2e0df0.jpeg)
NutanixのWEBコンソールがsafariで開けない時の対処法

# はじめに

この記事は、社内でNutanix CE構築中遭遇した、
「NutanixのPrism（WEBコンソール）をsafariで開けない時の対処法を簡単にまとめたものです。

# そもそもの問題

Nutanix CEでインストールされるPrismの証明書には、SAN(Subject Alternative Name)が設定されていないようでした。

そのため、新しいバージョンのSafariやChomeで開くことができません。

. [iOS 13 および macOS 10.15 における信頼済み証明書の要件](https://support.apple.com/ja-jp/103769)

. [Chrome Root Program Policy](https://g.co/chrome/root-policy)

そこで、Prism用にSAN付きの自己証明書を作成しPrismに設定する

https://www.nutanix.dev/2023/09/26/generating-a-self-signed-san-certificate-to-use-red-hat-openshift-in-nutanix-marketplace/

# 手順

## 1. crt及びKey file作成

```bash
export PC_IP=(PrismのIPアドレス)
openssl req -x509 -nodes -days 3650 \
    -newkey rsa:2048 -keyout Prism.key -out Prism.crt \
    -subj "/C=US/ST=CA/L=San Jose/O=Nutanix Inc./OU=Manageability/CN=*.nutanix.local" \
    -addext "subjectAltName=IP:${PC_IP}"
```

## 2. crt及びkey fileのインポート

作成された、`Prism.crt` 及び `Prism.key`を以下の手順にてPrismにインポートする

https://portal.nutanix.com/page/documents/details?targetId=Nutanix-Security-Guide-v6_7:wc-security-ssl-certificate-wc-t.html

https://blog.ntnx.jp/entry/2023/03/05/210107

~~単に鍵が古かったって気がしないでもない~~
アイヌ語の「虎」に関して

2023年11月[14日](https://srad.jp/~yasuoka/journal/665146/)・[17日](https://srad.jp/~yasuoka/journal/665205/)の日記にも書いたが、アイヌ語の「虎」に関して、私(安岡孝一)が知る最も古い用例は、Михаил Михайлович Добротворский『[Аинско-русскій словарь](https://www.google.co.jp/books/edition/%D0%90%D0%B8%D0%BD%D1%81%D0%BA%D0%BE_%D1%80%D1%83%D1%81%D1%81%D0%BA%D1%96%D0%B9_%D1%81%D0%BB%D0%BE%D0%B2%D0%B0%D1%80/_r4wAQAAMAAJ)』(Казань: Университецкая типография, 1875年)の語彙番号1519に出てくる

> Ивайсаруспѐ. С. тигръ (въ древности были на Сахалинѣ).

である。ドブロトゥヴォールスキーの記述を信じるなら、昔、樺太(サハリン)には「虎」がいて「イワイサルㇱペ」と呼ばれていたことになる。一方で、Bronisław Piłsudski『Materials for the Study of the Ainu Language and Folklore』(Cracow: Imperical Academy of Sciences, 1912年)のNr.27 [Dictated (December 1903) by Nita aged 28 of village of Aj.]では「Tóiki emuś-ani ivaj-saruśpe antáwḱe」を「with (my) earthen sword, I struck the six-tailed beast」と訳しており、ピウスツキは「イワイサルㇱペ」を「虎」ではなく「six-tailed beast」(六尾獣)と直訳している。

その次に私が知る限りでは、Николай Александрович Невскийが『Turera rerera petun tori setana kuru kuru』を翻訳した[ノート](https://tclopac.tenri-u.ac.jp/opac/opac_link/bibid/1000478989)(天理図書館088//イ2//A38&lt;B.700&gt;)があって、そこでは「toj arasarush wen arasarush」を「いやな虎　にくらしい虎」と訳している。つまり、ネフスキーの訳を信じるなら「アラサルㇱ」が「虎」ということになる。この『Turera rerera petun tori setana kuru kuru』は、1923年頃に鍋澤ユキから聞き取ったものらしいが、同時期にこれを筆録した金田一京助は「arasarush」を「熊」と訳しており(cf. 北海道立図書館『金田一京助採録ユーカラ・ノート[3]』請求番号M/418フィルムコマ158～163)、「アラサルㇱ」を「虎」ではなく「熊」だとみなしている。これを受けて、知里真志保も『[アイヌ語獣名集](http://hdl.handle.net/2115/33244)』(北海道大學文學部紀要, Vol.7 (1959年3月), pp.150-121) §12 (41)で

> ar&aacute;sarus クマのばけもの ｟ホベツ, ホロベツ｠

と記しており、「アラサルㇱ」を「虎」ではなく「クマのばけもの」とみなしている。「アラサルㇱ」は他の物語にも登場することがあるのだが、それらは「クマのばけもの」でないと困るらしい。

とすると、アイヌ語における「虎」の訳語としては、「イワイサルㇱペ」の方がまだマシ、ということになると思う。ただし、藤村久和は(たぶん)この意見に反対で、「イワイサルㇱペ」を「六尾をもつ(というオオカミの)奴」と訳している(cf. B・ピウスツキ『樺太アイヌの言語と民話についての研究資料&lt;26&gt;病弱な者でも有能な憑き神によって開運する由来話』(創造の世界, 第77号 (1991年2月), pp.138-145))。もちろん「虎」＝「イワイサルㇱペ」は私個人の感覚に過ぎないし、もっといい訳語(用例付き)があるなら、どんどん教えてほしい。
【git】【コマンド】gitの基本操作コマンドをまとめる

## Git Config: ログイン情報の設定
- メールアドレスを設定: `git config --global user.email "you@email.com"`
- ユーザー名を設定: `git config --global user.name "your name"`

## ブランチの確認
- 現在のブランチを確認: `git branch`
- ローカルとリモートの全ブランチを確認: `git branch -a`

## Commit後のステータス確認
- 作業状態を確認: `git status`
- 最新のコミットを取り消す: `git reset --hard HEAD^`

## ブランチ操作
- ブランチ削除: `git branch -d <branch-name>`
- 新規ブランチ作成: `git checkout -b <new-branch>`
- ブランチの変更を強制プッシュ: `git push -f --set-upstream origin <branch-name>`
- ログの確認: `git log --oneline`

## マージ
- 関連しない履歴を持つブランチをマージ: `git merge --allow-unrelated-histories <branch-name>`

## ステージング操作
- 特定のファイルをステージングに追加: `git add <file-name>`
- すべての変更をステージングに追加: `git add -A`

## 変更の確認
- ステージング前後の差分を確認: `git diff`

## ステージングから削除
- ファイルをステージングから削除: `git rm <file-name>`

## ステータス確認
- 作業状態を確認: `git status`

## コミット
- 変更をコミット: `git commit`

## Vimエディタの操作
- Vimエディタを終了: `:q`

## Vimエディタでのコミット
- Vimエディタでコミットメッセージを書いて終了: `:wq`
Azure Data Factory から Databricks Jobs computing を作成した際の挙動確認

## 概要

Azure Data Factory から Databricks Jobs computing を作成した際に、どのようなクラスターが作成されるかに関する確認結果を共有します。Unity Catalog 有効の有無によりアクセスモードが変更されるようです。下記画像は Unity Catalog が有効なワークスペースでの実行結果ですが、有効でない場合にはアクセスモードがカスタムとなりました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/e4396b7c-7722-f349-4b3a-422ed583c87c.png)

## 確認方法

### 1. Azure Data Factory から Databricks Notebook アクティビティでノートブックを実行

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/1af108f3-3ed7-3036-b970-305b719f1cbb.png)

### 2. Databricks にて作成されたクラスターを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/e5fa8b5a-0c49-678f-59df-5eb280183179.png)

## 確認結果

### 1. Unity Catalog が有効なワークスペースの確認結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/9fb2f0bd-77f9-01bf-580b-5ca3e18d27da.png)

### 2. Unity Catalog が有効でないワークスペースの確認結果

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/cb817fff-9bf4-7278-c1a1-9c7e030aa6e2.png)
CloudTrailのtrailを、管理イベントを「書き込み」だけで作成するCFnテンプレート

### はじめに

CloudTrailの証跡をデフォルトで作成すると、管理イベントのAPIアクティビティは読み取りと書き込みの両方が記録されます。AWSリソースの変更操作だけ記録したい場合「書き込み」だけに設定するケースのために、CFnテンプレートを試してみました。

### デフォルト設定
AWS公式にはEventSelectorを指定しない場合、デフォルトで管理イベントの「読み取り」と「書き込み」が設定されるとあります。

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-cloudtrail-trail-eventselector.html


実際に特になにも設定せずにtrailを作成すると下記の設定になっています。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/36b9069e-fbd2-9b47-6689-11dbfcba9729.png)

### EventSelector
管理イベントを「書き込み」のみにする設定について、明確な記述は確認できませんでしたが、どうやらEventoSelectorでDataResourceを指定せずに、ReadWriteTypeを「WriteOnly」と指定することで、設定できるそうな感じでしたので、試してみました。


### CFnテンプレート
``` yaml:trailWriteOnly_sample.yaml
AWSTemplateFormatVersion: "2010-09-09"

Resources:
  MyTrail:
    Type: AWS::CloudTrail::Trail
    Properties:
      S3BucketName: <your bucket name>
      IsLogging: true
      IsMultiRegionTrail: true
      EnableLogFileValidation: true
      IncludeGlobalServiceEvents: true
      TrailName: my-trail
      EventSelectors:
        - ReadWriteType: WriteOnly
          IncludeManagementEvents: true
          DataResources: []
```

### デプロイ後
S3バケットの作成と、バケットポリシーの設定は省略

CloudFormationで容易したCFnテンプレートでデプロイ後に、作成されたtrailを確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/ee9c6ba2-c6e2-6559-0535-892a04d75e83.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/707b22a3-b768-7fe4-d1fa-fb559c208533.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636209/163fa50d-2c0d-1376-f2bc-4c65fca1e5bf.png)

無事、管理イベントのAPIアクティビティが「書き込み」のみに設定されていることを確認できました。
git initしようとすると「On branch main nothing to commit, working tree clean」が出る

## TypeScriptの環境構築後にコードをgithubに載せたい

①git init
②git add .
③git commit -m "Initial commit"
→ここで以下の警告が出て実行できない

```
On branch main
nothing to commit, working tree clean
```
つまり、Gitのリポジトリにはまだコミットされていない変更が存在しない。

・・・

？

そもそもディレクトリ内でプロジェクトを作った際は、gitはどう連携されているのか？(言葉にするのが難しい…)

## 解決方法

git remote add originから後は、githubでリポジトリを作った際にSSHのところにある。
```
git remote add origin git@github.com:{アカウント名}/{リポジトリの名前}.git
git push -u origin main
```
連携できました！！！

## コミットメッセージは？

```
Initialize project using Create React App
```
このようなコミットメッセージでした。正直なところ、連携できれば目的は達成なので細かいところまで触れませんが、メッセージを変更する方法があるのか気になります。

## まとめ

今回はローカルで作った環境をgithubに連携する流れを備忘録的にまとめました。
不定期ですが、忘れたくないことをアウトプットしていきたいと思います。
FirebaseでNext.jsをホストしたら、キャッシュが更新されなかったよ

# やりたいこと
私は、Tech Course Rankというランキングサイトを開発・運営しております。
ランキングのデータを、日次のバッチ処理でDBに更新しています。データが１日１回のみ更新されるので、ページ自体も１日１回だけDBからデータフェッチして、静的HTMLを生成しておけば、DBアクセスによる課金も減らせるし、ユーザーにも最新の情報を届けると思い、実装しました。ただ、静的HTMLの日次更新がうまくいかず、それについて共有させていただきます。

(本来は、DBのデータ更新のタイミングで、On-Demandでキャッシュの更新がしたいが、それもうまくいっていないので、後回し)

https://www.techcourserank.com/

前置きとして、私はFirebaseを否定したり、批判をしたいわけではなく、個人開発でFirebase Hostingを使ってみて、純粋に困ってしまったので、共有しようと思っております。むしろ、助けを求めています。

# テックスタック

項目名    |採用した技術
-------|----------
フレームワーク     |Next.js
ホスティングサービス   |Firebase Hosting
データベース    |Firestore

上記のやりたいことを実現するために、Next.jsでサポートされている、ISR(Incremental Static Regeneration)機能を利用しようと思いました。ざっくり説明すると、ビルド時に静的HTMLを作成しておきます。その上で、設定した秒数が経過するたびに、静的HTMLを更新する仕組みです。

https://nextjs.org/docs/pages/building-your-application/data-fetching/incremental-static-regeneration

問題は、Next.jsアプリをFirebase Hostingでホストすると、ISRが動作しない、ということです。ISR動きましたよ！みたいな記事も見ましたが、私2024年1月の時点では、本サイトでは期待した動作になっておりません。

# 検証方法
まずは、キャッシュ更新をする期間を**設定しない**状態で、デプロイします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188008/17ec5e70-3535-e115-24cf-c8b2961aefd5.png)

Cache Control Headerを確認すると、こうなります。
max-age=3600、と入ってますね。デフォルトの設定なんですかね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188008/f0d073ab-a482-0f34-967f-13f852532431.png)


次に、キャッシュ更新をする期間(revalidate)を**設定します**。試しに、60 * 5 = 300sec に設定しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188008/39ce7d17-dcbe-38bc-49dc-16cc3bd3c03e.png)

再び、Cache Control Headerを確認します。
今度は、s-maxageが300秒と、stale-while-revalidateというディレクティブが表示されました。revalidateの設定が効いてそうですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188008/d8b0ba3d-ec9e-9d3e-e2b4-69f801f7fe6b.png)

### 問題
ただし、Firestore側のデータを更新した後、設定した5分以上経っても、ページ上の表示が更新されることはありませんでした。具体的には、Firestore側に保存しているデータ(ランキングデータの更新日)を2024/1/23などに変更しても、ページ側で更新されません。デプロイすると、ようやく適応されます。

Firestoreでは最終更新日 = 2024/1/23
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188008/8d74fe8b-2350-2d88-ab67-93ffcb4f7cc0.png)

ページ側の表示では最終更新日 = 2024/1/24 のまま
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188008/63f8d5d3-d8dc-7f3f-818a-b2de432b259d.png)


### 考察
考えられるのは、ISRの設定は動いてるが、Firebase側のCDNのキャッシュが残って、更新されていないとか。ただ、s-maxageが設定されているので、CDN側もキャッシュクリアされるはずなんだけど・・・。
いずれにしても、Firebase HostingにNext.jsをデプロイして、素直にrevalidateだけ設定しても、動作できませんでした。

### おわりに...
改めて、私は、Firebaseを否定したり、批判をしたいわけではありません。Firebaseは、料金面も使いやすさもよく、愛用しています。
ただ、Firebase HostingでNext.jsのISRが動いたというブログは見つけましたが、さらっと「動きました」としか書いてないのでよく分からず。逆に、ISRはサポートしていない、というドキュメントも見つけられませんでした。私の検索能力の問題かもですが・・・。

色々試してうまくいかなかったので、近々Vercelに移行してみようと思います。VercelのPricingが、Firebaseより分かりにくく、若干不安ですが、これも個人開発の醍醐味ですね。やってみます。

長々とご清聴いただき、ありがとうございました。技術っぽいブログは初めて書くので、緊張しています。お手柔らかに、お願い致します。

本ブログで扱ったサイト(Tech Course Rank)について、こんなブログも書いていますので、ご興味があれば是非。

https://qiita.com/AJCode/items/6659b8d21619ea4a8f5c
品質管理事故ダメ絶対

### はじめに ###

　私は、小売業の畜産部門に携わっています。

　主な仕事内容は、商品の加工、製造、**値付け**、店だし作業が一連の流れです。

　今回注目する点は**値付け**です。

 ※**値付け**・・・作業場内で加工した商品に**値段を付ける**作業

**アウトライン**

 **緊急事態・・・自社の問題提起**
 **現状・・・現在のやり方**
 **問題点・・・現在の運用方法の見直し**
 **解決策・・・画像による判断**
 **今後の展開・・・もう一歩先へ**
 
 

 

 

 ### 緊急事態 ###
 ###### 自社の問題提起
 

　当社では、月に2.3件のペースで**値付けミス**が発生しています。自分の店も例外ではありません。
　**値付けミス**は重大なアレルゲン事故につながり、最悪の場合、**お客さまの命にかかわる**事案です。

 

**※値付けミス**・・・表示ラベルと内容物（商品）が違うこと。

**※アレルゲン事故**・・・**値付けミス**により、アレルゲン表示が欠落した商品を
　　　　　　　　　　　販売してしまうこと。


### 現状 ###
###### 現在のやり方

①値付け品名カードを使用
②値付け時に商品とラベルがあっているか指差し呼称で相互確認
③店だし時に値付け品名カード商品が合っていることを確認したうえで店だし

### 問題点 ###
###### 現在の運用方法の見直し

①値付け機械は重さは判別できるが、**商品の判別はできない**
＝違った品番を入力すればそのまま通ってしまう
**※多くの品番が連番になっている**
②商品の整合性を相互確認という**アナログ**な方法で行うこと
③間違いは起きないだろうという**過信**

今回は①②を切り口に解決策を提案します。

### 解決策 ###
###### 画像による判断

　現状、画像での認識で判別を行えないため、TeachableMachineを使って認識してみようと思います。
 
 　使用する商品

上　アメリカンドッグ　下　ハンバーグ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655358/bbe2086a-ab22-60aa-b095-b8000d19cd48.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655358/82e55eb6-4746-3f14-fd6d-6bd229e741a1.png)



　一見すると、**間違えるわけないだろう**と思われる方が大半だと思います。

 実際は、**忙しさ**、**慢心さ**からしばしば起こることが現状です。

 そこで・・・TeachableMachineを使って判断してもらいました。

 ①サンプル画像をウェブカメラで様々な角度から撮影
 ②比較対象の画像も同様に撮影
 ③トレーディングを行い、プレビューで実際に稼働させる

 ![スクリーンショット (16).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655358/0eb8989f-ca16-33ea-e48f-abf39a273834.png)
 ![スクリーンショット (17).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655358/43585f71-61c2-2fbd-003d-d2397638b306.png)

 結果として、**画像による判別が可能**になりました！
 

 そして、NodeREDと連携させてみました
 
 まずは、全体のフロー画像はこちら↓
 ![スクリーンショット (18).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655358/6d31a432-4998-0e3e-2071-e10da5061af6.png)

 ①ノードに**camera**機能を追加
 ②**teachablemachine**にプレビューのリンクを張り付け
 ③**template**機能に構文を入力
 ④**debug**機能で出力

 実際に稼働させた結果がこちら
 ![スクリーンショット (19).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655358/cf1ea9b8-39bc-f624-7ad0-2a74c5864f7f.png)
![スクリーンショット (20).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3655358/57214417-5345-b333-f1c2-b1829ec668b5.png)

### 結果 ###

　画像による商品ごとの判別ができるようになりました。

 
  

 

 ### 今後の課題 ###
 ###### もう一歩先へ
 　現状は、画像のみの判断となったが、今後は品番と画像を連携させていきたいです。

　品番と画像が連携していないと誤った認識をされてしまうからです。
　
　また、サンプル画像を増やすとともに、類似商品をどの程度見分けることができるようになるのか確かめていきく必要があります。
`gh variables set -f`でGitHub Actions Variablesを一括設定すると500エラーになる時がある

# こんな感じのエラー

一括設定しようとするとたまにこんなエラーが出ます。

```
> gh variable set -f .env
✓ Created Actions variable VAR1 for matoruru/samplerepo
✓ Created Actions variable VAR2 for matoruru/samplerepo
✓ Created Actions variable VAR3 for matoruru/samplerepo
1 error occurred:
	* failed to set variable "VAR4": HTTP 500: Server Error (https://api.github.com/repos/matoruru/samplerepo/actions/variables)
```

# 解決方法

単にもう一回実行すればOK。

```
> gh variable set -f .env
✓ Created Actions variable VAR1 for matoruru/samplerepo
✓ Created Actions variable VAR2 for matoruru/samplerepo
✓ Created Actions variable VAR3 for matoruru/samplerepo
✓ Created Actions variable VAR4 for matoruru/samplerepo
```
Snipping Toolの通知はオフにできるって話

Windows+Shift+sのショートカットキーからSnipping Toolで静的イメージの切り取りを行う際に、取得したスクリーンショットをクリップボードにコピーするたびに右下に通知がでてきて自分の使い方ではちょっと邪魔に感じていました。
（Snipping Toolを開いて追加で編集等は普段しないので）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/8a45ebf7-4f23-dcd5-5006-0c2558f0cb59.png)

普段は別のツールをつかっているので、Snipping Toolを利用するのは稀なため、この通知を我慢して利用していましたが。

そもそもSnipping Toolの通知をオフにしておけば通知がでないことにいまさらながら気が付いたのでメモ

## 環境

Windows 11 22 H2

## Snipping Toolの通知をオフに

システム　＞　通知　＞　Snippint Toolから赤枠の通知をオフにすると通知がでなくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/2d75ae57-b43a-7d50-21ee-eaaa1469415f.png)

## 総評

Snippint Toolの設定を開いて、通知に関するものがないと残念に思っていましたが。
気づけば当たり前ですがOS設定側でSnipping Toolの通知をオフにすれば通知でないですね。

通知がOS側で抑制できるといった当たり前の事になかなか思い至らなかったのでメモ
Docker学習2日目

Dockerの学習を始めて2日目。
まだ全体の流れがイメージできていない、メモのような内容です。
間違っているところがあるかもしれませんし、
読みづらい記事になっているかと思います。。。
理解が深まり次第、その都度、更新・訂正します！
(間違いのご指摘や、アドバイスいただければとても嬉しいです！)


# 《目的》
1. Dockerの理解
1. docker-composeの理解
1. コンテナオーケストレーションツール（AWS ECS）の理解



## 1-3 マルチステージビルド

ステージとは大きな処理をキリのいいところで区切ったもの
例えば、下記のように分ける。
* テストステージではアプリをコンパイルし動作に異常がないチェック
* リリースステージでは動作チェック済みのアプリを本番ように作成する
このようなビルドの仕方を**マルチステージビルド**という。



## 1-4 ビルドしてみる
まずは下記の3行だけのDockerfileから`Dockerimage1`というDuckerimageを作成します。


#### 使用コマンドの説明
```dockerfile
FROM alpine:3.13.5 AS test
# test ベースイメージはアルパインLinux 3.13.5を使う,`AS`で今記述しているのはテストステージだよと定義
LABEL application=todobackend 
# applicationというキーに対してtodobackendという値を設定、タグをつけてる。
RUN apk add --no-cache bash git
# RUN以降のコマンドを実行してください。apkはアルパインLinuxのパッケージ管理コマンド、addで追加。
# --no-cacheはパッケージを保存しないで=Dockerimageのサイズ削減のためにキャッシュは必要ない。
# bashはトラブルシューティングのために使う。gitはリリースステージ作成のためにインストールする
```


Dockerfileを読み込んで(ビルドして)Dockerimageを作成していく。
まず、`cd Dockerfile`でDockerfile作成ディレクトリと同じ場所に移動する。
![スクリーンショット 2024-01-24 10.31.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/102f3293-0f6c-b96a-d4bf-1b1d8f7362d7.png)

ビルドログの解説。
ここで理解すべき大切なことはDockerFileが1行1行指示通り読み込まれているか確認すること。
**※初回読み込み時のビルドログをスクショし忘れてしまった為、初回と同じ設定を読み込ませているところはキャッシュしてくれています。Dockerが早い理由の1つはここ。**

1. **[internal] load build definition from Dockerfile**
ーDockerfileのビルド定義を読み込んでいます。
1. **[internal] load .dockerignore.dockerignore **
ーファイルを読み込んでいます。
1. **[internal] load metadata for docker.io/library/alpine:3.13.5**
ーDocker HubからAlpine Linuxの3.13.5イメージのメタデータを読み込んでいます。
1. **[1/2] FROM docker.io/library/alpine:3.13.5@sha256:1d30d1ba3cb9096206**
ーAlpine Linuxの3.13.5のイメージを取得しています。
1. **CACHED [2/2] RUN apk add --no-cache bash git**
ーキャッシュを使用して、Alpine Linuxイメージに bash と git をインストールしています。(**以前に読み込んだものをキャッシュしてくれている。**)
1. **exporting to image, naming to docker.io/library/myimage1**
ー最終的なイメージを作成し、名前を myimage1 に付けています。

`docker images`で作成したDockerimegeの参照をし、`docker history 《Dockerimage名》`で履歴を見れる
![スクリーンショット 2024-01-24 11.18.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/e94373aa-7780-bcb0-ded0-290b49fa2cc3.png)
下から古い順に履歴が表示される。
1. **青枠**
ーアルパインLinuxをベースイメジで使用していますよの表示
1. **みどり枠**
ーLABELコマンドを実行して、タグつけしたよの表示
1. **赤枠**
ーRUNから始まるコマンドを実行して、Dockeriamgeを作成したよの表示。この行為のIDは`fdd51c4b69da`で作成したDockerimages1のIDと一致するはず。
1. **オレンジ枠**
ーRUNコマンド実行して15.2MB、アルパインLinuxをベースにして5.6MB、トータルでこのDockerimage1は20.8MBある

大切なのはDckerimageはレイヤーが重なってできている事。それらレイヤーを全てまとめてDckerimageになっていることを理解する。
下記の図のことを実際に行っていた。
(図ではDocker image2になっているが1だと思ってください)


![スクリーンショット 2024-01-24 12.26.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3570384/4952aaaa-1f99-1488-c33f-02b7bc6f0407.png)
JavascriptとJinja2の組み合わせ For Flask

### 本記事の背景
Javascript入門は多くの情報と解説があるが、Jinja2と組合わせで使う場合やグラフ描画用ライブライであるchatjs+Jinja2で使う場合の紹介記事は少ない。筆者は、ある程度Javascriptの基礎文法を習得している人向けにJinja2との組み合わせで注意すべき点や引数の渡し方について焦点を合わせている。
※Jinja2については、以下の記事参照

https://qiita.com/akeyi2018/items/2557911351493aff87a8

jinja2とJavaScriptの組み合わせの事例（chat.jsのData部分にjinja2の構文が書ける）は、以下の記事でも紹介している

https://qiita.com/akeyi2018/items/ba806a89ca852157a591

### 本記事の対象者

- JavaScriptの基礎文法を理解している
- chatjsとはなにかを理解している
- Flaskの基礎を取得している
- HTML/CSSのタグを理解している
- Python基礎文法を理解している
- Webの基本的な仕組みを理解している（localhost,portとはなにかが分かる程度）
※上記で未収得の場合は下記の関連情報を先に参照し、習得、理解してください。

#### 関連情報
- [JavaScript超入門](https://qiita.com/rukukun/items/39bd94377b4b2021e684)（var/let/const解説付)
- [とほほJavascript入門](https://www.tohoho-web.com/js/index.htm)（必要に応じて逆引き）
- [とほほchatjs入門](https://www.tohoho-web.com/ex/chartjs.html)
- [chatjs公式](https://www.chartjs.org/docs/latest/)(chatjsはバージョンによって若干変更されているので、必要に応じて公式ページ参照）
- [chatjs日本語](https://misc.0o0o.org/chartjs-doc-ja/)
- [Flask構築手順](https://qiita.com/akeyi2018/items/0005cebf81c7909db84d)

### HTML内にJavaScriptを記述する場合

- 引き渡しでうまくいかない場合→「tojson」でデータ変換を行う
```html
{{ user_list|tojson }}
```
- 辞書型は「tojson」を付けないで、そのまま引き渡す
```html
datasets: [{
              label: {{ data_label|tojson }},
              data: {{ member_gender.male }},
              stack: 'stack-1',
              }
```

#### HTMLではなく、別途にJavaScriptを記述する場合
- 引き渡しデータをHTML側で用意しておく（グローバル変数）
```html
<script>
    const data = {{user_in | tojson}};
    const user_name = '{{ user_name }}';
</script>
```
- jsの参照設定
```html
<script src="{{url_for('static', filename='post.js')}}"></script>
```
- 呼び出し部分の実装
```html
<button type="button" class="btn btn-primary" onclick="post_data()">データPOST</button>
```

- JavaScript本体(post.js)の実装
```javascript
function post_data() {
  let checkbox = document.getElementById("checked_");
  if (checkbox.checked){
    let res = JSON.stringify(
      data,
      );
    $.ajax(
      {
        type: 'POST',
        url: '/set_user_data',
        data: res,
        contentType: 'application/json',
        success: function(){
          window.location.href = '/payment/' + user_name;
        }
      });
  }else{
    alert('同意にチェックを入れて下さい。')
  }
}
```
上記の例は、
①バックエンドからuser_nameをHTMLへ引き渡す。
②HTML内でグローバル変数の「user_name」を定義する
③post.jsへ引き渡す。
④post.jsでユーザ登録処理を行う
⑤登録処理結果が成功した場合、支払いのページを表示し、urlに「user_name」を引き渡す

バックエンド側で、決済ページに「user_name」を引数で受け取る
```py
@app.route('/payment/<name>')
def payment(name):
    cls = User()
    user = cls.get_user(name)
    user_email = user['email']
    user_kana = user['kana_name']
    return render_template('payment.html', 
        checkout_public_key=stripe_keys['publishable_key'],
        user_kana = user_kana,
        user_email = user_email)
```
[flutter]ダイアログの外側をタップしても閉じないようにする

こんにちは。
今回は、ダイアログの外側をタップしても閉じないようにする方法を紹介します。

# 方法
![スクリーンショット 2023-08-13 20.18.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524120/ce57fee2-1687-ea40-8538-b4c8b9db94e0.png)

まず、showDialog メソッド内で AlertDialog を呼び出します。

次に、barrierDismissible に false を設定することでダイアログの外側をタップしても閉じないようにします。

# 使用例
```dart

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("ダイアログ"),
      ),
      body: Center(
          child: ElevatedButton(
              onPressed: () {
                showDialog(
                  context: context,
                  //barrierDismissibleをfalseにすると、戻るボタン以外をクリックしても反応しません
                  barrierDismissible: false,
                  builder: (_) {
                    return AlertDialog(
                      title: const Text("アラートダイアログ"),
                      content: const Text("戻るボタンしか反応しません"),
                      actions: [
                        ElevatedButton(
                            onPressed: () => Navigator.pop(context),
                            child: const Text("戻る"))
                      ],
                    );
                  },
                );
              },
              child: const Text("Dialogを開く"))),
    );
  }
}

```

# 実行例
![aleartDialog.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524120/450c285b-08f0-6933-78b5-7bdf456f1acb.gif)


# 最後に
ここまで読んでいただき、ありがとうございました！
いいねしてくれたら、スキップして喜びます:heartpulse:
オブジェクトによって [常にレイアウトに表示] と指定されている標準項目


https://help.salesforce.com/s/articleView?id=000386827&type=1

[常にレイアウトに表示] 項目はページレイアウト上にあるべきであり、ページレイアウトからは削除することができない項目です。[常にレイアウトに表示] を使用する標準オブジェクトとその関連項目のリストを確認してください。


+ [Hide "Always on Layout" fields?](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007XIDncSAH)

:::note
go to ‘Field level security ‘ from that field in object and uncheck from that profile as shown below.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/cefb5a6c-7c2f-7d07-a921-746ee150aaa3.png)
:::
【ChatGPT有＋DALL-E3】いらないものをいらない、は意味がない。


　どうも、カーブミラーです。

　今回は、DALL-E3で描画された内容に、いらないものが出てきてしまった際の対処方法です。“いらない”は通じません。

:::note info
本記事は、ChatGPT（有料版）
＋DALL-E3で行なっております。

あしからず。
:::

　本題に行く前に、
　　ちょっとニュース。

　まずは、
　　OpenAIからのお知らせ？
　　　届いていないので、なんともw

　発信元は、こちら
[ChatGPT Plus と Team の統合が可能に](https://x.com/ctgptlb/status/1749942329270780411?t=K3RKcITtIYogJtHA6icV9w&s=03)

　記事を引用します。
>OpenAIがChatGPT PlusおよびTeamワークスペースを統合する新機能を発表しました。これによりサブスクリプションを統一することが可能に。詳細👇🏼

　メインメニューを
　　確認しましたけど、
　　　それらしいものは、
　　　　ありませんでした。

　Teamだけでは？

　いや、Plusと書いてるけど、
　　Enterpriseと間違えていないかな？
***
　次も
　　ChatGPTです。

　これもX-Twitterからです。
　　しかも、ChatGPTのアカウントw
　　　これはユーザーからの質問に
　　　　ChatGPTのチームが
　　　　　答えたもののようです。

[Archiveについて](https://x.com/ChatGPTapp/status/1749927523423834294?t=c3gWGhPb7FGUTge1qeea8A&s=03)
内容：
you can now bulk archive all of your chats with the ‘archive all chats' option you’ll see in settings. no matter if you’ve had 1 chat or 700k chats.
DeepL翻訳：
「設定の「すべてのチャットをアーカイブ」オプションで、すべてのチャットを一括アーカイブできるようになりました。 
チャットが1回でも700k回でも大丈夫です。」

　以前に、
　　設定に、
　　　Archiveが追加された旨を
　　　　お知らせしました。
[【ChatGPT無&有】に新機能。その名も“Archive”。ほかにもあり。](https://qiita.com/kabumira/items/8623325f0aedceff6cda)

　その追加情報ですね。

　上記記事を
　　書いた時点では
　　　Archiveの仕様が
　　　　イマイチわかりませんでした。

　どうやら
　　Archiveの目的が
　　　わかった気がします。

　Gmailと同じです。
　　不要なセッション履歴を
　　　Archiveして、
　　　　履歴上から消すためです。

　履歴から
　　必要なセッションに
　　　すぐアクセスできるように
　　　　なるわけです。

　プロンプトのテストに
　　使ったようなセッションは
　　　そこで終わり、
　　　　有用なセッションに
　　　　　アクセスするのに
　　　　　　邪魔です。

　そこで
　　そうしたセッションを
　　　Archiveして、
　　　　履歴上をきれいにする、
　　　　　それが目的だったんです。

　こう考えると、
　　Archiveの機能が
　　　すんなりと理解できました。

　問題は、
　　Archiveしたセッションは
　　　Exportできるのか？と
　　　　いうことです。

　これは
　　質問を投げてみました。
　　　返事は期待していませんけど
　　　　(=^･^=)
***
　さて、
　　本題です。

　DALL-E3で描画していると
　　不要なものが、
　　　描画されることが
　　　　ありますよね。

　“いらない”と
　　指示しても
　　　消してくれません。

　プロンプトを出力されると
　　“いらない”と書かれています。

　なのに、
　　描画される。

　これは
　　DALL-E3が
　　　文章生成AIではないことが
　　　　起因しているのではないかと
　　　　　私は考えています。

　DALL-E3は、
　　ChatGPTが
　　　与えてきたプロンプトを
　　　　解釈して
　　　　　描画するわけです。

　が、
　　書かれているのだから
　　　必要だと勝手に
　　　　解釈してしまう。

　そのため、
　　“いらない”とプロンプトに
　　　書かれても
　　　　“必要だ”と
　　　　　描画するわけです。

　つまり、
　　いらないならば、
　　　“いらない”と書くのではなく
　　　　プロンプトから
　　　　　削除するように
　　　　　　指示する必要が
　　　　　　　あるのです。

　例えば、
　　人だけの画像に
　　　猫が入ってしまった、と
　　　　仮定しましょう。

　“猫はいません”と書いても
　　猫は消えません。

　“猫はいません”＝“猫はいる”と
　　解釈されます。
　　　執拗に、描画されますw

　こういうときは、
　　「プロンプトから“猫”の部分を削除してください」と
　　　入れてみましょう。
　　　　消える可能性は高いです。

　あるいは、
　　プロンプトを出力させて
　　　いらない部分を
　　　　削除したプロンプトを
　　　　　与える手もあります。
　　　　　　こちらの方が
　　　　　　　確実かもしれません。
***
　これが
　　みなさんの
　　　作品制作の
　　　　糧になれば、
　　　　　幸いです。

　今回は、ここまで。
[備忘]Viva Connections の Feed タブで業界のニュースを表示する

# 目的
Viva Connections のフィード欄で業界のニュースを表示したい。

* デフォルト
何もニュースが表示されていない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464309/5f788ca2-97dc-abb2-cc05-50e458fbd43f.png)

* 変更後
指定した業界のニュースが表示される。
![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464309/63400cc0-d5d8-198b-415e-944419f6be43.png)

# 方法
[Microsoft フィードの概要](https://learn.microsoft.com/ja-jp/microsoft-365/ms-feed/m365-feed?view=o365-worldwide#disabling-the-feed-in-microsoft-edge) の Microsoft Edge でのフィードの無効化を参考にした。
1\. Microsoft 365 管理センターをひらく
2\. 設定 > 組織設定 > サービスタブ > ニュース をひらく

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464309/7d1552a5-9233-efb1-cd7c-b83a9bf68d24.png)

3\.全般タブで項目を変更する。今回は以下のように変更した。
* 業界：Information Technology
* Microsoft フィードで業界ニュース コンテンツをオンにする：✓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464309/63d71b86-6b6e-6eb1-34a3-e43ed93a1062.png)

# 結果
指定した業界のニュースが表示されるようになった。
![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464309/63400cc0-d5d8-198b-415e-944419f6be43.png)
[メモ] Miro をWacom タブレットから使う際の設定

個人的に以下がベストだった。

## Tablet

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/c3a06324-3bdd-fb1c-cf9c-fe2a6dc0985a.png)

## Pen

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/efe91c42-e8a8-0f67-8032-f98f77c33e16.png)

## Mapping

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/534442/3fbc8d01-5a80-8a37-1a16-82e0fd047f12.png)
Next.jsでGoogleフォントを設定する

# はじめに

Next.jsのプロジェクトで`Google fonts`を設定するメモ

# １．初期のコード

Next.js14のプロジェクトを立ち上げると`layout.tsx`が以下のようになっている。現在は`Inter`というフォントが使用されているみたい。

```tsx:layout.tsx before
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  );
}

```

２．変更する場所

今回は`Noto_Sans_JP`というフォントに変更する。おそらく以下のコードのようにすれば`Noto_Sans_JP`が適用できるはず。`lang`は一応`ja`にしている。

```diff_tsx:layout.tsx after
import type { Metadata } from "next";
+ import { Noto_Sans_JP } from "next/font/google";
import "./globals.css";

+ const notoSansJP = Noto_Sans_JP({ subsets: ["latin"], weight: ["400"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
+     <html lang="ja">
+       <body className={notoSansJP.className}>{children}</body>
    </html>
  );
}

```

# おわりに

一応コピペ用に下にコード貼っときます。

```diff_tsx:コピペ用
import type { Metadata } from "next";
import { Noto_Sans_JP } from "next/font/google";
import "./globals.css";

const notoSansJP = Noto_Sans_JP({ subsets: ["latin"], weight: ["400"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
      <body className={notoSansJP.className}>{children}</body>
    </html>
  );
}
```
【GO】コンパイラによるレシーバの暗黙的変換ってなに？

# はじめに
ありし日の自分は以下のコードが正常に動作することに疑問を感じてました。
```golang
type Dog struct {
	Name string
}

func (d *Dog) Speak() {
	fmt.Printf("Woof, I am %s\n", d.Name)
}

func main() {
	dog1 := &Dog{Name: "hoge"}
	dog1.Speak() // => Woof, I am hoge
	dog2 := Dog{Name: "fuga"}
	dog2.Speak() // => Woof, I am fuga
}
```
「`*Dog`というポインタ型に対して`Speak()`は定義されているけど、`Dog`という値型には`Speak()`定義されてないのだから、`dog2.Speak()`はpanicするはずでは？」と脳内にはてなマークを浮かべてました。
疑問を先輩にぶつけてみたところ、腑に落ちる説明を頂いたので備忘録としてまとめておきます。

# 結論
「コンパイラによる暗黙的変換が行われているから」です。
ポインタ型に対して定義してあるメソッドを値型の変数から呼び出そうすると、コンパイラが自動でポインタ型のメソッド呼び出しに変換してくれます。
コンパイラ君さては頭いいです。
```golang
func main() {
	dog2 := Dog{Name: "fuga"}
	dog2.Speak() // => (&dog2).Speak()にコンパイラが変換してくれる
}
```
また、この変換は値型に対して定義してあるメソッドをポインタ型の変数から呼び出そうとした場合も行われます。
```golang
func (d Dog) Speak() {
	fmt.Printf("Woof, I am %s\n", d.Name)
}

func main() {
	dog1 := &Dog{Name: "hoge"}
	dog1.Speak() // =>  (*dog1).Speak()にコンパイラが変換してくれる
}
```
# 終わりに
コンパイラ君頭ヨシヨシですね。
GO言語を学び始めてまだ1ヶ月も経っていないので、簡単なものになるかもしれませんがこれからも疑問点を記事にしていきたいと思っています。これ以降の記事も読んでもらえたら嬉しいです。
Macのアプリケーション管理を一元化する：Homebrew、mas、Brewfileの活用ガイド

※本記事は個人的に調べるためにChatGPTと対話した結果を記事にまとめたものです

Macユーザーの皆さん、アプリケーションの管理に頭を悩ませていませんか？今日は、Homebrew、mas、Brewfileを使って、Mac上でのアプリケーション管理を簡単に一元化する方法を紹介します。

## 1. Homebrewの基本

まずはHomebrewの導入から。Homebrewは、Macのためのパッケージ管理システムで、コマンドラインから簡単にソフトウェアをインストール、アップデート、管理できます。

### インストール方法
ターミナルを開いて以下のコマンドを実行します。

```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

## 2. masコマンドの活用

次に、Mac App Storeのアプリケーションをコマンドラインで管理するための`mas`コマンドを導入します。

### インストール方法
既にHomebrewがインストールされている場合は、以下のコマンドで`mas`をインストールできます。

```bash
brew install mas
```

## 3. Brewfileを使った一元管理

Brewfileは、インストールしたいHomebrewパッケージやmas経由のApp Storeアプリを一箇所にまとめるファイルです。

### Brewfileの作成
好きなテキストエディタを使って、以下のようにBrewfileを作成します。

```ruby
# Homebrew パッケージ
brew "wget"
brew "python"

# Cask アプリケーション
cask "google-chrome"
cask "slack"

# Mac App Store アプリケーション
mas "Xcode", id: 497799835
```

このファイルをホームディレクトリなどに保存します。

## 4. GitHubでのBrewfile管理

BrewfileをGitHubにバックアップして、いつでもアクセス可能にします。

### GitHubに新しいリポジトリを作成
- GitHubで「New repository」をクリック。
- リポジトリ名を入力（例: `my-mac-setup`）。
- 公開（Public）または非公開（Private）を選択。
- 「Create repository」をクリック。

### Brewfileをリポジトリに追加
ターミナルで以下のコマンドを実行します。

```bash
git clone [クローンURL]
cd [リポジトリ名]
# Brewfileをリポジトリディレクトリにコピー
git add Brewfile
git commit -m "Add Brewfile"
git push origin main
```

## まとめ

Homebrew、mas、Brewfileを使うことで、Mac上のアプリケーション管理が劇的に簡単になります。これらのツールをうまく活用して、効率的なデジタルライフを楽しみましょう！
Nuxt3でsassをインストールし、使おうとするとエラーがでてしまう原因。

一応解決したので記事にします。

開発環境
Windows11
node 20.5.0

どうやら依存関係が影響しているみたいです。
npx nuxi init プロジェクト名でnpmを選択し、プロジェクトを立ち上げます。

>その後、一度コマンドで以下を実行。

```
#powershellの場合
Remove-Item -Recurse -Force node_modules
Remove-Item package-lock.json

#macの場合
rm -rf node_modules
rm package-lock.json
```
>その後npm install
```
npm install node-sass
npm install sass-loader
npm install -D sass
```
必要なものをインストールし完了。

npm run devでローカルサーバーを立ち上げおそらく動作可能になります。
Route53の複数値回答ルーティングの通信の流れ

AWS Route53の複数値回答ルーティングの通信の流れを理解するのに時間がかかったので備忘録。

## Route53 複数値回答ルーティングの通信の流れ

#### 前提
Route53の複数値回答ルーティングを使って`example.com`に対して以下3つのIPアドレスを登録しておく。
10.0.0.1
10.0.0.2
10.0.0.3

#### 処理の流れ
1. ブラウザでexample.comにアクセス
2. Route53が登録されている3つのIPそれぞれに対してヘルスチェック
3. ヘルスチェックでOKだったIPをブラウザに返す（3つともOKだったら3つとも返す）
4. ブラウザ側で受け取った3つのIPアドレスのうちどれにアクセスするか決めてサーバーにアクセス（ここの選択基準はブラウザによってことなる）
備忘_SpringBootでS3のファイル操作（awssdk version2.2136）

# pom.xml
```
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.21.36</version>
</dependency>
LombokとかDB接続周りとか
```

# Service
### 接続設定
環境変数にAWSのアクセスキーとシークレットキーを設定すれば実行時に読み込む。
また、AWS上で動かす場合は実行するロールのものを参照するような記述があった気がする（AWS公式見て）
```
@Service
public class HogeImpl implements Hoge {
    
    @Value("${aws.region}")
    private Region region;
    
    @Value("${aws.s3.bucket}")
    private String bucket;

    // 署名付きURL発行を行うために必要
    private S3Presigner presigner = S3Presigner.builder().region(region).build();

    //　S3の操作をするために必要
    private S3Client s3Client =  S3Client.builder().region(region).build();

    
```
### 署名付きURLの発行（保存）
```
public URL genPreUrl(){
    // putするオブジェクトの情報を組み立て
    PutObjectRequest objReq = PutObjectRequest.builder()
        .bucket(bucket)
        .key("保存先となるS3Objectキー")
        .overrideConfiguration("追加で設定したいコンフィグ")//なくてもいい
        .contetType("text/plain")//なくてもいい。指定するなら必要
        .build();

    // preSignUrlの情報を組み立て
    PutObjectPresignRequest presignReq = PutObjectPresignRequest.builder()
        .signatureDuration(Duration.ofMinutes(30))//有効時間とか
        .putObjectRequest(objReq)
        .build();

    // 発行
    PresignedPutObjectRequest preSignedReq = presigner.presignPutObject(presignReq);
    URL url = preSignedReq.url();

    return url;
}
```

### S3にあるzipファイルの解凍と格納
```
public void unzipUpload(){
    // S3からオブジェクトの取得
    ResponseInputStream<GetObjectResponse> zipObj = s3Client
        .getObject(GetObjectRequest.builder()
            .bucket(bucket)
            .key("取得したいObjectキー")
            .build());

    // zipのstream作成
    ZipInputStream zipInputStream = new ZipInputStream(zipObj);
    ZipEntry zipEntry;

    // zip内の全ファイルに対してループ
    try{
        while((zipEntry = zipInputStream.getNextEntry()) != null){
            ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            // 実際のバイト数を入れる変数
            int bytesRead;
            // エントリーの内容を読み込みバイト配列に書き込む
            while ((bytesRead = zipInputStream.read(buffer)) != -1){
                byteOutStream.write(buffer, 0, bytesRead);
            }
            byteOutStream.close();

            //ここから格納
            ByteArrayInputStream byteInStream = new ByteArrayInputStream(byteOutStream.toByteArray());

            // upload
            s3Client.putObject(PutObjectRequest.builder()
                .bucket(bucket)
                .key("保存するObjectキー名")
                .build()
                ,
                RequestBody.fromInputStream(byteInStream, byteInStream.available())
            );
        }
        // zipファイルの削除
        s3Client.deleteObject(DeletePbjectRequest.builder().bucket(bucket).key("削除対象").build();
    }catch ~~~~~
    ```
```
Typeフィールドのエラー


# Create a Self-Registration Page

+ [Login Page Type](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007X6ORQ)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/275ee22e-c5ac-01ce-bf7b-f7ceb1355b22.png)

:::note
You can click on the magnifying glass icon without typing anything and then select value.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/0ef26cd8-0450-bfe6-9a1e-5f260ad60eca.png)



何も入力せずに虫眼鏡アイコンをクリックして値を選択できます。
:::

+ [Experience Site self registration throwing error with person account](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007YwRdU)


:::note
Oddly enough, i remove the account on the login and registration page in admin and it worked. Might have been some really long caching that caused it to fail previously.


奇妙なことに、管理者のログインと登録ページでアカウントを削除しましたが、うまくいきました。以前に失敗した原因は、非常に長いキャッシュだった可能性があります。
:::
GASでインラインスレッドへの返信はMessageReplyOptionが必須だった

# やりたかった事

GASでGoogleChatに投稿、そのスレッドにインラインスレッドへの返信の形で追加情報をPOSTしたかった。
しかし、いくらやっても新しいスレッドへの投稿になってしまい悩んでいました。


# 解決方法

インラインスレッドへの返信はMessageReplyOptionを指定しないとデフォルトで無効になっていました。

https://developers.google.com/chat/api/reference/rest/v1/spaces.messages/create?hl=ja#MessageReplyOption

webhook URLに下記の様にオプションを加える必要がありました。
```
&messageReplyOption=REPLY_MESSAGE_OR_FAIL
```

# サンプルGAS

下記、サンプルのロジックです。参考になれば幸いです。

```js
function postGoogleChat(url, message) {

  var msg_json = JSON.stringify(message);
  var options = {
    "method" : "POST",
    "contentType" : "application/json; charset=utf-8",
    "payload" : msg_json
  }
  return UrlFetchApp.fetch(url , options); 

}

function main() {
  // スペースのWebhook URL
  var url = "https://chat.googleapis.com/v1/spaces/xxxxxxxxxx";

  // https://developers.google.com/chat/api/reference/rest/v1/spaces.messages/create?hl=ja#MessageReplyOption
  // 指定しないとdefaultで新しいThreadが生成される
  // インラインスレッドへの返信の場合は指定が必須
  var res_swith = "&messageReplyOption=REPLY_MESSAGE_OR_FAIL";

  //Googlechat投稿用のMessageを作成
  var msg = {
    "text" : new Date(),
  }
  var response = postGoogleChat(url , msg);

  //Threadを取得する
  var response_json = JSON.parse(response.getContentText());
  var thread_name = response_json["thread"]["name"];

  //インラインスレッドへの返信のMessageを作成
  var thread_msg = {
    "text" : "Hello!",
    "thread": {
      "name" : thread_name
    },
  }

  //res_swithを付与し、Thread返信を有効化する
  var thread_response = postGoogleChat(url + res_swith, thread_msg);
  Logger.log(thread_response);
}
```
nullの代わりにdefaultを使うべき？

## 参考
この記事は、以下の動画を参考にしています。詳しくは、動画をご覧ください。

https://youtu.be/iEGaj1CNrl8?si=sjg3zhp3b7mgK8zB

## 値を明示する方がベター
- `default`は、`null`とは限らない
- `default`は、それだけ見てもデータ型がわからない
- コレクションなら、`null`よりも空のコレクション`Enumerable.Empty<T>()`がベター
【解説付き】複数回のランダムアクセス（ランクD】　Ruby

# 初めに
paizaのレベルアップ問題集を解いていたのですが、これだけ問題の意味がわからなかった、、
Cランクの問題とか普通に解けたのに、、
[このブログ](https://blog-it.hatenablog.com/entry/programming/c/random/access/plural)を読んでようやく意味が分かりました。
わかってしまえば全然難しいものではありません。
ただ問題文が少し抽象的且つRubyの解説が載っていなかったので、同じように躓いた初学者のためにRubyでの解説を残しておきます。

[複数回のランダムアクセス (paizaランク D 相当)](https://paiza.jp/works/mondai/data_structure/data_structure__array_step2/edit?language_uid=ruby&t=2d5e2d03a113588c517f8c90ac523ee2)



# 解説

問題の目的を簡単に要約すると、


**２列目で与えられた数値列から４列目で与えられた数値列の各数値番目の要素を取り出してくださいね**ってことです。

変に要素数（１列目と３列目）とか与えられているせいで混乱しますが、これは考慮しなくても良いです。

例えば入力例１の２列目では
```
    10 20 30 40 50
```
が与えられているので、
ここから４列目で与えられた2 4 1つまり２番目(20)と４番目(40)と１番目(10)の要素を取り出してねってことになります。

複数要素の取り扱いは配列を使えばいいだけなので簡単だと思いますが、ここで１つ注意点として、**配列は0番目から始まる**ということです。
その点を考慮していただければ後は回答できるかと思います。

**回答の手順**
①　各標準入力を受け取る
②　２列目、４列目を配列にする
③　配列の各要素を数値に変換する
④　４列目のそれぞれの要素を取り出し、−１する（配列のインデックスに変換する）
⑤　配列化した２列目から④のインデックス番目の値を取り出し出力する


# 解答例

一応私が書いたコードを解答例として乗っけておきます。
paizaでは100点でしたが、初学者が書いたものですので、参考程度にご覧ください。
```
input_line1 = gets.chomp.to_i
input_line2 = gets.chomp.split(" ").map{|n| n.to_i}
input_line3 = gets.chomp.to_i
input_line4 = gets.chomp.split(" ").map{|n| n.to_i}
input_line4.each do |n|
    puts input_line2[n-1]
end
```

※何か間違いがあったらご指摘いただけると幸いです。