OmniAICreator/Qiita-1.07M huggingface dataset:
Thunderbird115での外観カスタマイズ

個人的メモ

Thunderbird バージョン115での外観をカスタマイズする方法

# 事前設定
1. Thunderbirdを起動
設定（歯車アイコン）＞一般＞設定エディター（一番下のボタン）　を起動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/407a68b0-81ae-547e-250a-3ddab46b73f9.png)

2. 「設定名を検索」のところに、「toolkit.legacyUserProfileCustomizations.stylesheets」を入力
下にtoolkit.legacyUserProfileCustomizations.stylesheets　false　と表示されているので、
右側の切り替えボタンを押して、trueに変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/f62afd25-26ea-071a-6225-54ed496b847c.png)

3. 下記フォルダにchromeフォルダを作成する
C:\Users\xxx\AppData\Roaming\Thunderbird\Profiles\xxxxxxxx.default-release\
※xxxはユーザ名
※xxxxxxxx　の部分は任意
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/0a9c4efe-91a0-5ac7-bc5a-e29d00fe1306.png)

4. chromeフォルダ配下に、userChrome.cssファイルを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/c043fcd6-fa57-f9ba-738a-532b321ab041.png)

以降、userChrome.cssに記述したCSSがThunderbirdに反映される。


# 設定例
:::note warn
すべての設定には、!important　をつけること
:::
* フォルダツリー関連
``` CSS
/* フォルダツリー関連 */
/* フォルダツリーにて未読ありのフォルダを選択した場合の未読バッチの背景色・文字色 */
#folderTree:focus-within li.selected > .container > .unread-count {
  background-color: #ffffff !important;
  color: #0000ff !important;
}

/* フォルダツリーの未読件数を表示するバッチの背景色 */
.unread > .container > .unread-count {
  background-color: #0000ff !important;
}

/* フォルダツリーの新規メッセージありのフォルダ名の文字色 */
.new-messages > .container > .name {
  color: #69C0FF !important;
}

/* フォルダツリーの未読ありのフォルダ名の文字色 */
.unread > .container > .name {
  color: #69C0FF !important;
}
```

* メッセージ一覧関連
``` CSS
/* メール一覧関連 */
/* スレッドの"タグなし" "未読"の背景色と文字色 */
tr[data-properties~="untagged"][data-properties~="unread"] {
  background-color: #69C0FF !important;
  color: #ffffff !important;
}

/* スレッドの"タグなし" "未読"の「選択時」の背景色と文字色 */
tr[data-properties~="untagged"][data-properties~="unread"].selected {
  background-color: #1373D9 !important;
  color: #ffffff !important;
}

/* スレッドの"タグなし" "新規"の背景色と文字色 */
tr[data-properties~="untagged"][data-properties~="new"] {
  background-color: #69C0FF !important;
  color: #1373d9 !important;
}

/* スレッドの"タグなし" "新規"の「選択時」の背景色と文字色 */
tr[data-properties~="untagged"][data-properties~="new"].selected {
  background-color: #1373D9 !important;
  color: #ffffff !important;
}
```

# その他要素の見つけ方
開発ツールを使用します。
1. ハンバーガアイコン（三みたいなやつ）＞ツール＞開発ツール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/49174c5a-fcf0-71bb-1ecd-f230641b01bf.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/16cbab0a-209d-598c-a0e8-5c265a152e2a.png)

2. 「リモートデバッグの接続許可を求められています。」のダイアログが出たら「OK」を押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/a01f8c4d-c4e2-0d52-d59c-8d7da5e8f543.png)

3. 開発ツールが立ち上がるので、変更したい要素を確認しタグなどを調べる（Chromeの開発ツールと似たような操作感）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492164/c413502d-5d55-9ba9-fdd9-903a63a42e35.png)
コネクテッドシートのヘッダは日本語にできる

# コネクテッドシートとは
BigQueryのViewやテーブルの内容をそのままスプレッドシートに同期するコネクテッドシートという機能があって、ローデータを共有する際に有用なのでよく使っています。
https://cloud.google.com/bigquery/docs/connected-sheets?hl=ja

シンプルにCSVエクスポートしたデータをインポートする方式だと
- 何かしら情報を残さないとデータの出所がたどれなくなって不便
- 定期更新するにはGASや外部ツールを使って処理を書く必要がある
- データ量が多い時に不都合が生じやすい

といった不便さがありますが、コネクテッドシートだとその問題が解消します。

このように便利なコネクテッドシートですが、列名がそのままヘッダ行として使われるので必ず英語になってしまう。という問題がありました。
大体エンジニアでは無い人に共有するためにコネクテッドシートを使うので、地味に辛いポイントです。

# BigQueryが列名を日本語対応
少し前までは英語しか使えなかったですが、今年の3月から大部分のUnicode文字列が列名で指定できるようになりました(preview版ではある)。
https://cloud.google.com/bigquery/docs/schemas?hl=ja#flexible-column-names

そのため、列名を日本語にしてしまえばヘッダ行が日本語にできます。

# 日本語列の使い方

Viewで日本語列名を利用する際は下記のようにASを使って実現できます。

```sql
SELECT
  name AS `氏名`,
  nickname AS `あだ名`,
  tel AS `電話番号`,
FROM
  sample_dataset.users
```

ただし、記号は使えないものがあります。

> 柔軟な列名は、次の特殊文字をサポートしていません。
Unicode 正規表現 \u0021 で表される感嘆符（!）。
Unicode 正規表現 \u0022 で表される引用符（"）。
Unicode 正規表現 \u0024 で表されるドル記号（$）。
Unicode 正規表現 \u0028 で表される左かっこ（(）。
Unicode 正規表現 \u0029 で表される右かっこ（)）。
Unicode 正規表現 \u002A で表されるアスタリスク（*）。
Unicode 正規表現 \u002C で表されるカンマ（,）。
Unicode 正規表現 \u002E で表されるピリオド（.）。
Unicode 正規表現 \u002F で表されるスラッシュ（/）。
Unicode 正規表現 \u003B で表されるセミコロン（;）。
Unicode 正規表現 \u003F で表される疑問符（?）。
Unicode 正規表現 \u0040 で表されるアットマーク（@）。
Unicode 正規表現 \u005B で表される左角かっこ（[）。
Unicode 正規表現 \u005C で表されるバックスラッシュ（\）。
Unicode 正規表現 \u005D で表される右角かっこ（]）。
Unicode 正規表現 \u005E で表される曲折アクセント（^）。
Unicode 正規表現 \u0060 で表される抑音アクセント（`）。
Unicode 正規表現 \u007B で表される左中かっこ（{）。
Unicode 正規表現 \u007D で表される、右波かっこ（}）。
Unicode 正規表現 \u007E で表される、チルダ（~）。

かっこは補足情報として入れたくなる気持ちが湧くかもしれませんが、他の方法で表現するしかないですね。

ちなみに日本語列名はPIVOTの中でも使えました。

```sql
SELECT
  *
FROM
  sample_table
PIVOT(
  MAX(column_a) AS `列名A`,
  MAX(column_b) AS `列名B`,
  FOR column_c
  IN ("X群", "Y群")
)
```

# 最後に
使ってて気になったことは
- 中間テーブルを日本語列名にしてしまうとJOINの時に日本語を指定する必要が出てくる

という点です。


中間テーブルに使うと日本語名になっていないテーブルとのJOINする際に分かりづらくなってしまうので、最終アウトプットに使うViewやテーブルでのみ利用するのが今の所は良いと思ってます。

他にも注意点や活用方法があればぜひ教えてください。
neosでlogix その145

# 概要
neosでlogixやってみる。
練習問題、やってみた。

# 練習問題
物を掴むと、どんどん増えて、いくつでも取れるを実装せよ。

# 写真

![2023-10-10 09.49.14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/f0de8db2-269c-a508-46b7-ad0038b51fe5.jpeg)


# 方針

- GrabInstancerコンポーネントを使う。

# 手順

- devtooltipを装着
- 新規->3d->トライアングルをスポーン
- 新規->空のオブジェクトをスポーン
- インスペクターを表示
- Transform->Interaction->GrabInstancerコンポーネントを追加
- トライアングルをグラブして空のオブジェクトの下に移動
- GrabInstancerコンポーネントのtemplateにトライアングルを指定
- トライアングルのGrabbableのチェックを外す

以上。
スキーマ駆動開発について調べたことをまとめました。


## 1. スキーマ駆動開発の基本概念

スキーマ駆動開発では、スキーマ（APIの型やデータの形を定義する仕様）を先に定義することで、フロントエンドとバックエンドの開発が平行して行えるようになります。


## 2. GraphQLとスキーマ

GraphQLは、Facebookが開発したクエリ言語で、スキーマを中心としたAPIの設計を可能にします。

GraphQLのスキーマは、以下のように型を用いて定義されます。

```graphql
type User {
  id: ID!
  name: String!
  email: String!
}

type Query {
  user(id: ID!): User
}

```
User型:
UserはGraphQLのオブジェクト型です。
id, name, emailの3つのフィールドを持ちます。
ID!やString!の末尾の!はそのフィールドが非nullであることを意味します。

Query型:
QueryはGraphQLのルートクエリ型で、クエリのエントリポイントとして機能します。
この例では、userというフィールドを持ち、ID!型のidを引数として受け取ることで、特定のUserを返すことが定義されています。
このスキーマは、IDを受け取り、Userオブジェクトを返すクエリを定義しています。

#### 2.1 単一エンドポイント
従来のRESTful APIがリソースごとに複数のエンドポイントを持つのに対し、GraphQLは通常、単一のエンドポイントで全てのデータの取得・変更を行います。

単一エンドポイントの特徴：
RESTful APIとの違い: RESTful APIでは、通常、リソースごと（例えばユーザー、記事、コメントなど）に異なるエンドポイントが存在します。例えば、ユーザー情報を取得するためのエンドポイント（/users）、記事を取得するためのエンドポイント（/articles）などが独立しています。

GraphQLのエンドポイント: 一方、GraphQLでは通常1つのエンドポイント（例: /graphql）を使用します。このエンドポイントを使用して、どのようなデータを取得するか、またはどのようにデータを変更するかを、クエリまたはミューテーションの形式で指定します。

なぜ単一のエンドポイントが役立つのか：
柔軟性: クライアントは1つのエンドポイントを通じて、必要なデータを精密に指定できます。このため、不要なデータの取得を避けることができ、効率的なデータ取得が可能となります。

APIのバージョン管理: 新しいフィールドや型を追加しても、既存のクエリに影響を与えないため、APIのバージョンアップが容易です。従来のRESTful APIの場合、新しいエンドポイントを追加するか、既存のエンドポイントを変更する必要がある場合が多いです。

簡潔なエラーハンドリング: 単一のエンドポイントを通じてエラー情報を一貫して取得できます。また、部分的なエラー（一部のフィールドが失敗しても他のフィールドが成功する場合など）もきちんとハンドリングできます。

開発とドキュメントの容易さ: エンドポイントが1つだけなので、開発やドキュメントの管理も一元化され、シンプルになります。



## 3. PythonでのGraphQLの実践例
PythonでGraphQLを使用する場合、grapheneというライブラリが利用できます。以下は、先ほどのスキーマをgrapheneで実装する例です。

```python

import graphene

class User(graphene.ObjectType):
    id = graphene.ID(required=True)
    name = graphene.String(required=True)
    email = graphene.String(required=True)

class Query(graphene.ObjectType):
    user = graphene.Field(User, id=graphene.ID(required=True))

    def resolve_user(self, info, id):
        # この例ではハードコードしたデータを返しています。
        # 実際にはデータベースや他のAPIからデータを取得する。
        return User(id="1", name="John Doe", email="john.doe@example.com")

schema = graphene.Schema(query=Query)
```
このスキーマを使用すると、以下のようなクエリを投げることで、ユーザー情報を取得できます。
```graphql
{
  user(id: "1") {
    name
    email
  }
}
```

4. まとめ
スキーマ駆動開発は、APIの設計と開発のプロセスを効率化するための有効なアプローチです。GraphQLはこのアプローチを実現するための強力なツールの1つで、適切な実践とともにAPI開発の品質と速度を大きく向上させることができます。
Python 散布図から確率楕円（誤差楕円・信頼楕円），外接する長方形を作成

## 使用するモジュール
matplotlib, numpy

## コード書くとき参考にした文献
- [Plot a confidence ellipse of a two-dimensional dataset](https://matplotlib.org/stable/gallery/statistics/confidence_ellipse.html)
- [Creating a Confidence Ellipse in a scatterplot using matplotlib](https://stackoverflow.com/questions/20126061/creating-a-confidence-ellipse-in-a-scatterplot-using-matplotlib)

## 確率楕円のみ生成するコード
```python
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Ellipse

###################### 設定 ######################
n_sigma = 3 # nシグマ(標準偏差)分を覆う．3の場合は3σ確率楕円を生成．

###################### データの準備 ######################
data = np.random.randn(1000,2) # 100x2行列で，標準正規分布の乱数を生成　

###################### 確率楕円の生成 ######################
x = data[:,0]; y = data[:,1]

cov = np.cov(x, y) # 共分散
eigenvalues, eigenvectors = np.linalg.eig(cov) # 固有値と固有ベクトル

w, h = 2 * n_sigma * np.sqrt(eigenvalues) # 長軸と短軸の長さ
theta_rad = np.arctan2(*eigenvectors[:,0][::-1]) # 楕円の傾き，radian
theta = np.degrees(theta_rad) # 楕円の傾き，degree

###################### グラフの生成 ######################
ax = plt.subplot(111)
ax.set_aspect('equal')
ax.scatter(x, y)

ell = Ellipse(xy=(np.mean(x), np.mean(y)),width=w, height=h, angle=theta, color='black')
ell.set_facecolor('none')
ax.add_artist(ell)

plt.show()
```

### 結果
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531123/9d3b9fd0-b1b3-a95a-1367-fcde857ff171.png">


## 序に確率楕円の座標を取得し，外接する長方形も表示するコード
```python
import numpy as np
import matplotlib.pyplot as plt

###################### 設定 ######################
n_sigma = 3 # nシグマ(標準偏差)分を覆う．3の場合は3σ確率楕円を生成．

###################### データの準備 ######################
data = np.random.randn(1000,2) # 100x2行列で，標準正規分布の乱数を生成　

###################### 確率楕円の生成 ######################
x = data[:,0]; y = data[:,1]

cov = np.cov(x, y) # 共分散
eigenvalues, eigenvectors = np.linalg.eig(cov) # 固有値と固有ベクトル

w, h = 2 * n_sigma * np.sqrt(eigenvalues) # 長軸と短軸の長さ
theta_rad = np.arctan2(*eigenvectors[:,0][::-1]) # 楕円の傾き，radian
theta = np.degrees(theta_rad) # 楕円の傾き，degree

phi = np.linspace(0, 2*np.pi, 1000) # 極座標，角度の列
center_x = np.mean(x); center_y = np.mean(y) # 楕円の中心

rotate = np.array([[np.cos(theta_rad), -np.sin(theta_rad)],[np.sin(theta_rad), np.cos(theta_rad)]]) # 回転する行列
ellipse = np.array([w/2*np.cos(phi), h/2*np.sin(phi)]) # 回転，移動させる前の楕円
ellipse = (np.matmul(rotate, ellipse)+np.array([[center_x],[center_y]])).T # 回転，移動後の楕円
square = np.array([[w/2, -w/2, -w/2, w/2, w/2],[h/2, h/2, -h/2, -h/2, h/2]]) # 回転，移動させる前の長方形
square = (np.matmul(rotate, square)+np.array([[center_x],[center_y]])).T # 回転，移動後の長方形

###################### グラフの生成 ######################
ax = plt.subplot(111)
ax.set_aspect('equal')
ax.scatter(x, y)
ax.plot(ellipse[:,0], ellipse[:,1], '--',lw=1)
ax.plot(square[:,0], square[:,1], '--',lw=1)

plt.show()
```

### 結果
<img width="600" alt="image.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531123/23761bf8-ff26-216f-0453-2fb1eaa0c4c6.png">
Yurimellブログを支える技術

初めまして、Yurimellブログの管理人です。今回は、このブログを支える技術について紹介します。

## ホスティング環境
Yurimell は最初、Vercel にてデプロイを行っていましたが、BuyMeACoffe等の投げ銭サービルを導入するためには Vercel の Pro プランにアップグレードする必要があり、毎月 20 ドルのコストがかかるため、Google Cloud Run に移行しました。Cloud Run は、コンテナ化されたアプリケーションを簡単にデプロイできるサービスです。さらにアクセスが来ない時には自動でインスタンスを停止してくれるので、コストを抑えることができます。

## CDN
Cloudflare（クラウドフレア）を使用してブラウザからリクエストされた情報をキャッシュとして保持し、提供しています。

## TypeScript
現在では、スタンダードとなっている TypeScript を採用しています。これにより、バグの発生を最小限に抑えつつ、高品質なコードを維持することができています。

## Next.js 13
私の Augmell ブログでは、Next.js 13 を採用しています。Ver.13 を使用するのは初めての試みであったため、App Router から全て学び直す必要がありました。しかし、新しいバージョンで開発してみると、情報の整理が向上しており、非常に使い勝手が良くなったと感じました。特に layout.js の存在により、何度も同じ component を書き直す必要が無くなり、コードがすっきりしました。また、サイトマップやメタタグの設定が以前よりも格段に簡単になったと感じています。

## ブログ記事の扱い: next-mdx-remote & gray-matter
Augmell では、記事のコンテンツを MDX 形式で扱っています。そのために、

# 続きは本家ブログからどうぞ（無料）
https://yurimell.dev/ja/diary/detail/2
SharePoint REST APIで「日付と時刻」データを扱うときに、覚えておきたいこと。

SharePoint REST APIを使ってSharePointからデータを取得するときに、個人的に結構な頻度で悩むのが「日付と時刻」列に設定した値の取り扱い方。

- 取得できる値って、UTC時間だっけ？JST時間だっけ？
- あ、この形式で返ってくるんだった、データ変換しないと・・

なんてことを繰り返している気がするので、まとめです。

## APIリクエストで取得できる値は、UTC時間。かつ文字列。

早速結論ですが、リクエストして帰ってくる値は、 **「UTC時間 かつ 日時文字列」** の形式です。

## 実際に取得してみる
事前準備として、サイトコレクションのタイムゾーンを日本時間（UTC +09:00）に設定。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781744/14a48a69-5a28-a0f0-f240-618d3013c38d.png)

:::note warn
リストにアイテムを登録した時点でのタイムゾーンが適用されます。
サイトコレクションのタイムゾーンを途中で変更した場合、すでに登録されているアイテムのタイムゾーンは切り替わりません。タイムゾーンを切り替えたい場合は、アイテムを更新する、再登録するなどする必要があります。
:::

<br>


以下の「日付と時刻のテストリスト」の [開始日] 列（内部名：StartDate）の値を取得してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781744/2db93e89-83a1-74b3-38b6-b216763c8478.png)
- リクエストURL
```typescript
GET: {site_url}/_api/lists/getbytitle('{list_name}')/items
```

- リクエスト結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781744/973a3771-e088-d3ab-b25f-86117eb7bf23.png)


上図のように、**UTC時間（日本時間 －9時間）** かつ **yyyy-MM-ddTHH: mm:ssZ** のISO8601フォーマットで文字列として返ってきます。

例えば取得した値を画面に表示したい場合、そのままではUTC時間で表示されてしまうので、地域のタイムゾーンに変換する、という処理が必要になることが多いです。

## 変換方法

方法はいくつかあると思いますが、備忘録も兼ねて一例を。
```typescript
let dateObj = new Date("2023-05-31T15:00:00Z"); // 取得した日付文字列を引数に設定

let year: string = dateObj.getFullYear().toString(); // 年
let month: string = (dateObj.getMonth() + 1).toString().padStart(2, '0'); // 月
let date: string = dateObj.getDate().toString().padStart(2, '0'); // 日
let hours: string = dateObj.getHours().toString().padStart(2, '0'); // 時
let minutes: string = dateObj.getMinutes().toString().padStart(2, '0'); // 分
let seconds: string = dateObj.getMinutes().toString().padStart(2, '0'); // 秒

let dateString = `${year}/${month}/${date} ${hours}:${minutes}:${seconds}`; // 2023/06/01 00:00:00
```
1. APIリクエストで取得した日付文字列("2023-05-31T15:00:00Z")をDateオブジェクトに変換。

1. Dateオブジェクトの各メソッドで年・月・日・時・分・秒を取得し文字列に変換。
→ これらのメソッドは、ローカル環境のタイムゾーンに基づき値が取得できる。

1. ゼロパディング（"00"）を行い2桁の形式に変換。

1. 変換した値を任意の形式に組み立てる。

### 参考
- [Date.prototype.getFullYear() - JavaScript - MDN Web Docs](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date/getFullYear)

- [JavaScriptでゼロパディングする](https://zenn.dev/captain_blue/articles/zero-padding-in-javascript)
1桁の数値を0埋めするためにsliceメソッドを使っていましたが、padStartメソッドというものがあるのですね。直観的でスッキリ書けるこちらを使いました。

## こんな風に表示できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2781744/b4d6d229-1dec-f7dd-cc19-5c8e6ed963de.png)
UTC時間をJST時間に、
yyyy-MM-ddTHH: mm:ssZ の文字列を yyyy/MM/dd HH: mm:ss に変換完了です。

繰り返しですが、忘れないように以下メモ。 

#### SharePoint REST APIでは、UTC時間かつ文字列で時刻情報が取得できる。
簡単なレジシステムを開発してみた


こんにちは，ご覧いただきありがとうございます。
来月，うちの大学で学祭があるのですが，その際に出店される方からレジシステムの開発をお願い？されました。
時間があまりなかったので簡単なものになりましたが，一応リリースにこぎつけたのでよければ使ってください！！

https://github.com/ikepu-tp/unifestill

Qiitaでの投稿なので一応，技術的なお話もしましょう。

## 使用技術

- Laravel 10
- React.js 18
- MySQL(MariaDB)

毎度同じの使い慣れた子たちです。
ちなみにReact.jsにおいて，Atomicの考え方になかなか馴染めず，結果的にMVC的にしました。

## 要望

- レンタルサーバで動く
- iPad等のタブレットでも利用しやすい
- オーダー画面がある（注文を受けたら注文内容が別の画面に表示される）
- 既存システムでもいいが，機能が多いし，お金を払ってまで使いたいわけではない

要望は特になかったのでざっくりとこんなものでした。
3つ目のオーダー画面はちょっとあれですが…

## システム名

システム名も勝手に決めればいい感じだったのでChatGPTにお世話になりました。
最終的に「大学"University"＋祭り"Festival"＋レジ"TilL"」を組み合わせて"UniFesTill"となりました。
ロゴはCanvaで適当に作りました。
これぞ，ザ・適当ですね。

## 機能

- ユーザー管理
- プロジェクト管理
- メンバー管理
- 支払方法管理
- 商品カテゴリー管理
- 商品管理
- 会計管理
- レジ打ち
- オーダー管理

それぞれの機能について簡単に説明します。

### ユーザー管理

ユーザーの作成，ログイン等の通常の機能です。
お馴染みのLaravel Breezeで実装しています。
毎回なのですが，フロントはBootstrapベースで作成するのでプロフィール画面をどうするか問題があるんですよね。
今回はそのまま`tailwindcss`を使用しました。
Bootstrapとスタイルが競合するところは上書きしてやりました。（強引）
ただ，今回は時間がなかったため，個人でアカウント作成し，団体に紐づけ，という方法ではなく，団体で1アカウントを作成し，それの共有という形にしました。

### プロジェクト管理

今回は学祭での利用がメインだったため，プロジェクト管理という方式にしました。
毎回同じメニューなわけでもないので年度ごとにプロジェクトを作成し，管理できたほうがよいかなと思いまして。
（来年以降も複数回使ってくれるかはわかりませんが…）
プロジェクト名と備考を設定するだけのシンプルなものになります。

### メンバー管理

レジ打ちをするときに担当者を選択するためのメンバー管理です。
特に担当者選択の必要性がなければ適当に1つ登録し，それで登録すればいいですね。

### 支払方法管理

支払方法（現金や前売り券等）と備考を設定するだけのシンプルなものです。
レジ打ちの際に使用します。

### 商品カテゴリー管理

商品のカテゴリーと備考を設定するだけのシンプルなものです。
当初は商品をカテゴリー別に表示しようと思って作ったものだったのですが，結局時間がなくそこまでたどり着けていません(-_-;)
ただの負の遺産です。

### 商品管理

商品を設定するものです。
商品カテゴリーや商品名，金額を設定できます。
当初は親商品，子商品を設定できるようにしようと思ったのですが，これも時間がなくたどり着けていません。

:::note info
親商品と子商品という言い方が正しいかはわかりませんが，例えば親商品「セットメニュー」を注文すると子商品「ドリンク」を選択するといった感じです。
:::

### 会計管理

会計を管理するシンプルなものです。
会計内容の表示や削除ができます。
削除後の会計内容の表示や会計レポートの出力もしたいのですが，これも時間がなく取り組めていません。
特にレポートは絶対いりますよね。

### レジ打ち

レジ打ちをする機能です。
UniFesTillのメインですね！（笑）
基本的には普通のレジだと思います。
工夫した点はiPad等のタブレットでも操作しやすいようにソフトウェアテンキーを作っているところでしょうか。
今回は工数削減のためにパーツの使いまわしをかなりしていますが，できれば，特にレジ打ちの画面はUIもこだわりたいですね。

### オーダー管理

個人的には一番推しの機能になります。
レジ打ちをすると，オーダー画面に注文内容が表示されます。
こんな感じです。
「注文済み」や「準備中」，「完了・引き渡し済み」を押せば状態の変更ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1089552/e529a216-e3d6-15ca-9d66-528e2849c982.png)

レンタルサーバの利用なため，WebSocketを使えません。
そのため，今回はSSEを利用し，リアルタイムチックな通信を実現しています。

https://developer.mozilla.org/ja/docs/Web/API/Server-sent_events/Using_server-sent_events

## とりあえず作り終えての感想

時間がない中とりあえずリリースするとこまでいけた自分を褒めたいですね。（笑）
そうはいっても気に入らないところ等はたくさんあるのでこれからもつついていきたいと思っています。
皆さんもぜひ利用してください！また，プルリクもお待ちしています！！
【メモ】Material UIでRobotoフォントをnpm経由で導入する方法

フォントファイルをnpm経由で導入し、Webpackで扱うのが初めてだったのでどハマりしました……。

# 公式ウェブサイトのインストールの項目
https://mui.com/material-ui/getting-started/installation/#roboto-font

npmでinstallして`import '@fontsource/roboto/300.css';`したら終わりのような書き方ですが、そこは世界水準のマニュアル、案の定説明不足で、**初心者には無理です。** 9割がた、真下に書いてあるCDN使って`link`タグ埋め込む方法に流れるかと。

# 1. css-loaderとstyle-loaderを導入する。
公式の記述を見て、「あれ、cssファイルimportするということは、css-loaderとかいるのでは。なんも書いてないけど……」と思ったそこの貴方、正解です。Material UIを使うということはstyled-components or emotionのどちらかを導入しているかと思うので、css-loaderとか入れてないかと思いますが入れましょう。

```shell
npm i -D css-loader style-loader
```
`webpack.config.js`の設定はごく普通に、
```js
    module: {
        rules: [
            {
                test: /\.css$/i,
                use: ['style-loader', "css-loader"],
            },
        ],
    },
```
こんな感じでOKです。

# 2. フォントファイルをwebpack上で扱うために設定を追加 or (webpack v4以下)file-loaderを導入する
フォントファイルをwebpackで扱うため`webpack.config.js`に設定が必要です。まずはフォントファイルを扱う用の設定を行います。

```js
    module: {
        rules: [
            {
                test: /\.(woff|woff2|eot|ttf|otf)$/i,
                type: 'asset/resource',
            },//←これを追加
            {
                test: /\.css$/i,
                use: ['style-loader', "css-loader"],
            },
        ],
    },


```

またv4以下は[file-loader](https://www.npmjs.com/package/file-loader)の導入が必要です。設定はググったら出てくると思うのでそちらに任せます。

# 3. ビルドする
この状態でビルドすると、importしたCSSファイル内で読み込まれているフォントファイルのみが抽出されて、出力先フォルダに大量にフォントファイルが生成されればOKです。

# 4. Gitの設定をする（必要な場合のみ）
ファイルが生成されたので一旦Gitにコミットしたくなるかもしれませんが、**Gitの設定によってはせっかく出力したフォントファイルが壊れます。** プレビュー時に開発者ツールのコンソール欄にファイルが壊れているとかなんとか、それらしきエラーメッセージが出たらこれを疑ってください。
Gitの改行コードの変換設定を入れている場合、バイナリファイルであるフォントファイルの中の改行コードまで変換をかけてしまうのが原因となります。対策としては`.gitattributes`を作成し、

```shell
# Webフォントをバイナリとして認識させないと、フォントファイル内の改行コードを変換してしまい壊れる
*.otf binary
*.eot binary
*.svg binary
*.ttf binary
*.woff binary
```
と設定することで回避することができます。

# 5. 確認する
主に開発者ツールの使い方的な話になります。ビルド自体が上手くいかない場合は……頑張ってください💪

## 読み込んだフォントが適用されているか確認する
開発者ツールの`要素(element)`タブを開き、Webフォントの適用されているDOM要素を選択してください。次にサブの`計算済み(確か英語だとcomputedだったような……)`タブの一番下に`レンダリングフォント`の項目があるので、そこで実際に適用されているフォントを確認することができます。僕の時はここがずっとArialになってて苦戦しました……

## （適用されていない場合）フォントファイルが読み込まれているか確認する
フォントファイルの読み込み過程も開発者ツールの`ネットワーク`タブで見れるのでまずは確認してください。そもそも読み込まれてない場合はloader周りに問題があり、読み込んでるけどエラーが出ている場合はおそらく出力しているパスと読み込もうとしているパスがズレてるので、読み込もうとしているファイルパスを確認し、`webpack.config.js`と格闘してみてください。フォルダ構成によっては、ここも中々ハマりポイントですね……。

## （ダメ元）キャッシュや生成したフォントファイルを削除する
自分はファイルパスの問題で読み込まれず、ビルド設定変更しながら何度もビルドしていたんですが、キャッシュ消しても変更した設定が反映されず、一旦出力した全フォントファイルを削除して再生成したら反映されたケースがあったので、ダメ元で試すのもありかと思います。
Javaコマンドでクラスパス指定時に注意すること

## 前提
**Windows11**を使用しています
ディレクトリの指定は**相対パス**で行います

## 構成
例えば以下のディレクトリ構成を想定します
```:H:\Java
H:\Java
├─B
│  │
│  └─Y
│          Sub.java
│
├─C
│  │
│  └─Z
│          Base.java
│
└─A    
    │
    └─X
            Main.java            
```
それぞれのコードの実装は以下の通りです
```:H:\Java\A\X\Main.java
package X;

import Y.Sub;
import Z.Base;

public class Main {
    public static void main(String[] args) {
        new Sub().Message();
        new Base().Run();
    }
}
```
```:H:\Java\B\Y\Sub.java
package Y;

public class Sub {
    public void Message() {
        System.out.println("a");
    }
}
```
```:H:\Java\C\Z\Base.java
package Z;

import Y.Sub;

public class Base {
    public void Run() {
        System.out.println("b");
        new Sub().Message();
    }
}
```

## コンパイル
これらのコードを実行するには、まずコンパイルによって.classファイルを作ります
クラスパスには**import文にてパッケージ名として採用されたフォルダを含むフォルダ**をトップディレクトリとして指定します
```:コマンドプロンプト
H:\Java>javac A\X\Main.java -cp B;C
```
```:PowerShell
H:\Java>javac A\X\Main.java -cp B";"C
```
今回であれば`パッケージY,Z`は`フォルダB,C`に属するフォルダです
```:H:\Java\A\X\Main.java
import Y.Sub;
import Z.Base;
```
```:H:\Java\C\Z\Base.java
import Y.Sub;
```
`PowerShell`(Windowsのみ？)上では複数のディレクトリを指定時に`";"`で区切る必要があります
**ダブルクオーテーションで囲う必要がある**ことに注意が必要です
```
javac javaファイル -cp クラスパス;クラスパス...
```
が、基本構文となります
ここまででディレクトリの状態は以下のようになりました
```:H:\Java
```:H:\Java
H:\Java
├─B
│  │
│  └─Y
│          Sub.java
│          Sub.class
│
├─C
│  │
│  └─Z
│          Base.java
│          Base.class
│
└─A    
    │
    └─X
            Main.java
            Main.class
```

## 実行
Javaコマンドを用いることでコードの実行が可能ですが、ファイルの指定方法には次の二つの種類があります
```:Javaコマンド
java -cp クラスパス;クラスパス... javaファイル
java -cp クラスパス;クラスパス... classファイル
```
ここで注意すべきなのが**classファイルを使用するケース**です
まず、javaファイルの使用時は以下のように実行します
```:コマンドプロンプト
H:\Java>java -cp B;C A\X\Main.java
```
```:PowerShell
H:\Java>java -cp B";"C A\X\Main.java
```
```:結果
a
b
a
```
実行対象のファイルを指定し、それぞれのパッケージのトップディレクトリを指定する直感的な方法です
では、クラスファイルの指定時はどうなるかというと、以下のような手続きになります
```:コマンドプロンプト
H:\Java>java -cp A;B;C X.Main
```
```:PowerShell
H:\Java>java -cp A";"B";"C X.Main
```
```:結果
a
b
a
```
クラスファイルの使用時は、**そのクラスファイルのソースコード内でpackageに指定されたフォルダを含むフォルダ**をトップディレクトリとしてクラスパスに含める必要があります
```:H:\Java\A\X\Main.java
package X
```
という宣言がありました
つまりこの`X`というフォルダを含んだ`A`を**クラスパスに含めなければならない**ということです
`A/X.Main`ような書き方はできません
<font color="red">罠</font>ですね

## あとがき
クラスファイルを使用して実行する際に忘れがちなポイントなのでまとめました
嵌りどころだと思うのでディレクトリの指定時には常に意識しておきたいところです
suとsu -の違いについて

## 環境
Ubuntu20.4

## はじめに
**su** と **su ‐** の違いについて知りたければ、ググると沢山の記事がでてきます。迷ったときはググればいいだけなのですが、本当に、記事内容の通りの動きになっているのか、自分で手を動かして確かめてみることは重要です。記事の内容を鵜呑みにすることなく、自分で手を動かして確認することで、本当の理解へとつながるものです。

違いについては、一言でいえばこの通りです。
```
$su - testuser   # 環境変数を引き継ぎません
$su testuser     # 環境変数を引き継ぎます
```

## やったこと
適当なユーザーを新規に作成して、パスワードも設定します。
```
$sudo useradd testuser
$sudo passwd testuser
```

TESTという環境変数が登録されていないことを確認します。
```
$printenv | grep TEST
```

TESTという環境変数を作成して、01という値をセットします。
```
$export TEST=01
$echo $TEST
```

testuserユーザーに切り替えます。
```
$su - testuser
```

TEST環境変数は引き継がれていません。
```
$echo $TEST
（空白）
```

再度、testuserユーザーに切り替えます。
```
$su testuser
```

TEST環境変数が表示されます。環境変数が引き継がれているということです。
```
$echo $TEST
01
```

:smile:はい！よくわかりました。

## 後始末

テストのために作成したユーザーは削除します。TEST環境変数は、シャットダウンすると消えるのでそのままにしておいてOKです。
```
$sudo userdel testuser
```
定価マスタのない取引データから定価と値引き率を求める(SPSS Modeler データ加工逆引き3-23)

## 定価マスタのない取引データから定価と値引き率を求める

![スクリーンショット 2023-10-25 12.50.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/31c652da-698d-574c-ff4a-777d76dba5f8.png)

## 1.想定される利用目的

・値引きによる顧客の反応度を求める

## 2.サンプルストリームのダウンロード

https://github.com/yoichiro0903n/blue/raw/main/price_rate.str

## 3.サンプルストリームの説明

![スクリーンショット 2023-10-08 11.27.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/ae694fa6-8bbc-4899-9876-9e4dd775ce99.png)

a.入力するデータは以下の通りです。一般的な顧客ID付きPOSデータです。
![スクリーンショット 2023-10-08 11.30.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/22bf5cb2-74e8-120a-d0ac-9913a6dd74d9.png)

b.[レコード集計]ノードを編集します。商品IDの売価の最大値を定価と仮定します。
![スクリーンショット 2023-10-08 11.30.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/8102715b-865b-f498-982f-ea8dee5cef25.png)

c.[レコード結合]ノードを編集します。元データの商品IDに定価フィールドを追加します。
![スクリーンショット 2023-10-08 11.30.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/0163266e-94ee-45f2-80e4-f10151562640.png)

[プレビュー]します。
![スクリーンショット 2023-10-08 11.43.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/8e75d857-8ecb-e106-6f43-3c7172d01725.png)

d.[フィールド作成]ノードを編集します。値引き率を求めます。

![スクリーンショット 2023-10-25 12.56.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/b33d657c-e5f7-4a05-c278-b1eb70143065.png)

e.[ソート]ノードを編集します。
![スクリーンショット 2023-10-08 11.31.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/ccd3772f-06b0-6e28-039b-15153b8f4024.png)

f.[フィールド順序]ノードを編集します。
![スクリーンショット 2023-10-08 11.31.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/93b73338-043a-5031-821e-0115c3b23803.png)

[テーブル]を表示します。
![スクリーンショット 2023-10-08 11.31.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/c0f80afd-b6ef-2666-1c46-3fe63d0572ff.png)

#### 注意点

　顧客毎の価格反応度を求める場合には[CUSTID]をキーに値引き率の平均値を求めます。

![スクリーンショット 2023-10-08 11.48.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/5bf10f35-397d-7438-eee2-d1e6dcd581db.png)


## 4.　参考情報

#### SPSSで価格を扱った記事

https://www.ibm.com/blogs/solutions/jp-ja/spssmodeler-push-node-2/

#### SPSS Modeler ノードリファレンス目次

https://qiita.com/416nishimaki/private/503037195081d3323eee

#### SPSS Modeler 逆引きストリーム集（データ加工）

https://qiita.com/416nishimaki/private/4bf813e6cba84b2a1008
GASを使ってGoogleグループを大量に新規作成と初期設定を一気に行いたい

## 背景
GoogleWorkSpaceでGoogleグループをML（メーリングリスト）として利用したりしていると、Googleグループを大量に作成する必要があったりします。

また、Googleグループには結構細かい初期設定を行うことができます。今回のような一括で新規作成と初期設定を行えるスクリプトをGASで作成しようと考えました。

## 事前準備
事前に以下のようなスプレットシートを作成します。
シート名は「group_ikkatsu」とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254772/833c9922-c488-331e-204f-4227bec8011a.png)


## コード
```
function googlegroup_ikkatsu(){
    var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('gruop_ikkatsu');
    var lastRow = sheet.getLastRow();
    var startRow = 2;
    var numRows = lastRow - 1;
    var dataRange = sheet.getRange(startRow, 1, numRows, startRow);
    var data = dataRange.getValues();

    for (var i = 0; i < data.length; ++i) {
        var row = data[i];
        
        row.rowNumber = i + 2;
        
        // 作成するGoogleグループの基本情報
        
        // アドレス
        var groupEmail = sheet.getRange(row.rowNumber, 2).getValue();
        
        // 名前
        var groupName = sheet.getRange(row.rowNumber, 1).getValue();
        
        // 説明
        var groupDesc = sheet.getRange(row.rowNumber, 3).getValue();
        
    
    //Result列がブランクであれば処理を実行
    if (!row[3]) {
        var result = "";
        
        try
            {
            //Groupの作成
            AdminDirectory.Groups.insert({email: groupEmail, name: groupName, description: groupDesc});
            
            //5秒インターバル
            Utilities.sleep(5000);
            
            //Googleグループの初期設定
            var group = AdminGroupsSettings.Groups.get(groupEmail);
            group.whoCanJoin = 'INVITED_CAN_JOIN';
            group.whoCanViewMembership = 'ALL_MEMBERS_CAN_VIEW';
            group.whoCanViewGroup = 'ALL_MEMBERS_CAN_VIEW';
            group.allowExternalMembers = 'false';
            group.whoCanPostMessage = 'ALL_IN_DOMAIN_CAN_POST';
            group.allowWebPosting = 'false';
            group.isArchived = 'true';
            group.primaryLanguage = 'ja',
            group.archiveOnly = 'false';
            group.messageModerationLevel = 'MODERATE_NONE';
            group.spamModerationLevel = 'ALLOW';
            group.whoCanLeaveGroup = 'NONE_CAN_LEAVE';
            group.memberscanpostasthegroup = "false";
            group.whoCanModerateMembers = 'OWNERS_AND_MANAGERS';
            group.whoCanAssistContent = 'NONE';
            group.whoCanDiscoverGroup = 'ALL_IN_DOMAIN_CAN_DISCOVER';
            
            AdminGroupsSettings.Groups.patch(group, groupEmail);
            
            result = "Success";
            }
        catch(e){
        result = "Error:" + e;
        }
    
        // 結果挿入(Success or Error Log)
        sheet.getRange(row.rowNumber, 4).setValue(result);
        
        }
    }
}
```

## 補足
A,B,C列で更新された行の情報をもとにアドレス作成と初期設定を行います。
実行結果はD列に記載されます、正常に完了した場合は「Success」、エラーを吐き出した場合は、（Error:***）としてエラー情報が吐き出されます。

Googleグループに対しては結構細かい設定ができます。
興味のあるかたは参考情報やGWSのAPIサイトから確認してみてください。

## 参考情報
[Googleグループの設定と管理｜吉田航｜note](https://note.com/w_yoshida/n/n2d31b254d005)
[Google WorkSpace Directory API: デベロッパー ガイド](https://developers.google.com/admin-sdk/directory/v1/guides/guides?hl=ja)
GASを使ってスプレットシートの特定のセルを含む行の情報をGoogle Chatに通知させてみた

## 目的
GAS（Google Apps Script）を利用して、スプレットシートの特定の条件に一致したセルを含む行（今回だと日付情報をトリガーにして）の情報をGoogle Chatに通知させたい。

## 背景
スプレットシートでサーバやNW機器等（結構の量）の資産情報を管理しているのですが、資産の保守期限等期日管理を行う場合に手動かつ目視でいちいち確認するのが非常に面倒だという課題がありました。

というわけで、保守期限の日付を自動で判定して、期限の〇日前だった時に通知を飛ばすGASで作ろうと考えました。通知方法はいろいろありますが、今回はGoogleChatに飛ばします。

## 事前準備
検証用に下記の画像のようなスプレットシートを用意します。
シート名は「list」とします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254772/214c29cb-243e-8454-4be4-17b043bf8e38.png)


## コード

```
function datecheck() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('list');

  // アクティブシートの最終行を取得
  var lastRow = sheet.getLastRow();
  lastRow = lastRow + 1;

  // チャットのWebhookのURL
  var url = '*******************************************';

  for (var i = 2; i < lastRow; ++i) {

    // 最新日
    var date = new Date();

    // 〆切10日前（サンプルは最新日から10日後）
    date.setDate(date.getDate() + 10);
    var YYYYMMDD = Utilities.formatDate(date, 'JST', 'yyyy/MM/dd');

    // E列の期限に表示された日付を取得
    var dt = sheet.getRange(i, 5).getDisplayValue();

    // 日付を比較して10日後の期限と一致した場合チャットに通知する条件式
    if (YYYYMMDD === dt) {

      // 期日の合致した拠点と資産名称を取得
      var ktn = sheet.getRange(i, 1).getDisplayValue();
      var nm = sheet.getRange(i, 2).getDisplayValue();

      // チャットに通知
      var text = "10日後に保証期限が切れる機器があります" + '\n' +
        '拠点：' + ktn + '\n' +
        '資産：' + nm + '\n' +
        '期日：' + dt + '\n';

      var payload = {
        text: text
      }

      // エンコード
      var json = JSON.stringify(payload);

      // ポストするためにヘッダーとボディをまとめて入力
      var options = {
        method: 'POST',
        contentType: 'application/json; charset=utf-8',
        payload: json
      }

      // 送信を実行
      var result = UrlFetchApp.fetch(url, options);
    }
  }
}
```

## 補足
Apps Script側でスクリプトの定期実行の時間設定を入れれば自動化の完成です。
コードの具体的な内容は基本コメントを見ていただければと思います（不明点在れば質問ください）。

仕組みはいたってシンプルです。

まず、このスクリプトを実行した日を取得して、その取得した日から〇日後（上記コードだと10日後）の日付とスプレットシートに記載されている日付（例だとE列）の値を比較します。

比較して一致していればその日付がある行に含まれているセル情報を通知で飛ばします。


## 参考情報
[GAS part3 ハングアウトチャットの便利機能を使った業務改善](https://techceed-inc.com/engineer_blog/5714/)
↑　GoogleChatで飛ばす際のコードの書き方で参考になりました。
LWC1503

+ [LWC エラーの対処方法に戻る](https://qiita.com/Keiji_otsubo/items/480135e98f6b0144ee63)
+ [LWC エラーの対処方法2](https://qiita.com/Keiji_otsubo/items/54983dba4eb802686a12)


+ [Communicate Between Lightning Web Components Project](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000NETMP)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/3c7bb0f8-24d9-eb87-5823-3995069b2e50.png)

:::note
Code must be written inside the body of the class; if you read carefully the error message indicates the same.

https://salesforce.stackexchange.com/questions/301342/lwc1503-parsing-error-leading-decorators-must-be-attached-to-a-class-declarati
:::

+ [【未解決】LWC1503: Dynamic imports are not allowed.](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000OsQW3)

:::note
we need to enable LWS under session settings. Setup -> Session Settings -> Enable "Use Lightning Web Security for Lightning web components and Aura components". Also please make sure your in VS code, org-api-version is 58.0 or above. You can check the same using 

```
sf config list 
```

:::

:::note
Also,  the meta-xml of your component should contain the capabilities property if you want to make use of dynamic instantiation

```xml
<?xml version="1.0" encoding="UTF-8"?>
    <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>59.0</apiVersion>
    <capabilities>
        <capability>lightning__dynamicComponent</capability>
    </capabilities>
</LightningComponentBundle>
```
:::


+ [Facing Problem in VS Code on LWC Concept](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007KYEtq)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/3abdc3f0-be34-ef09-cb68-293f646e921f.png)

:::note
change

```
@Salesforce/apex/WireApexDemo1.getoppo
```

to (small 's' since JS is case-sensitive):

```
@salesforce/apex/WireApexDemo1.getoppo
```

and as already been said check your java version:

https://www.youtube.com/watch?v=BwBNsLBpnVs
:::

# LWC1702

+ [How to fix vscode probrem LWC1702 on lwc's default test.js](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007MaYZW)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/eb340d9e-1266-f0d8-1650-72af03eae202.png)

:::note
I have got the same problem , solved the issue with forward slash  

import { createElement } from "lwc/"; 
:::

:::note

```
import { createElement } from '@lwc/engine-dom';
```


Adding this resolved the error for me.
:::
GASを使ってGoogleグループのメンバーを一括で追加（削除）する

## 目的
GASを使って、Googleグループへのメンバー追加（削除）を一括で処理したい。

## 背景
GoogleグループをML等で活用する場合、入社時や依頼時に大量のメンバー追加（削除）が求められるので、その処理を簡単にしたいと考えました。

## 事前準備
下記のようなスプレットシートを用意しておきます。スプレットシート名は「member」とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254772/68d9384f-c6a0-1829-d4a9-cfdc5b179069.png)

画像の2行目以降は空白ですが、本来であれば以下の①～⑥の情報が入っている想定です。
①Member,②ML,③Email,④Action,⑤Result,⑥Interval

①：testuser@test.test（追加/削除したいメンバーのアドレス）
②：testgroup（Googleグループの@より前の部分）
③：testgroup@googlegroup（Googleグループのアドレス）
④：add or remove（追加ならadd、削除ならremove）
⑤：空白（GAS実行時に処理の結果が入力されます）
⑥：ALLMAIL or NOMAIL etc（詳しくは補足を参考ください）


## コード
```
function mem_change() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('member');
  var lastColum = sheet.getLastColumn();
  var lastRow = sheet.getLastRow();
  var startRow = 2;
  var numRows = lastRow - 1;
  var dataRange = sheet.getRange(startRow, 1, numRows, lastColum);
  var data = dataRange.getValues();

  for (var i = 0; i < data.length; ++i) {
    var row = data[i];

    row.rowNumber = i + 2;

    // Result列がブランクであれば処理を実行
    if (!row[4]) {
      var result = "";

      try {
        var action = row[3];

        if (action === "add") {
          result = AdminDirectory.Members.insert({ email: row[0], role: "MEMBER", delivery_settings: row[5] }, row[2]);
          result = "Success";
        } else if (action === "remove") {
          result = AdminDirectory.Members.remove(row[2], row[0]);
          result = "Success";
        } else {
          result = "Error: Actionパラメータに誤りがあります。add/removeのいずれかをセットしてください。";
        }
      } catch (e) {
        result = "Error: " + e;
      }

      // 実行結果をResult列にセットします
      sheet.getRange(row.rowNumber, 5).setValue(result);
    }
  }

  // インターバルを設定
  Utilities.sleep(5000);

  // 2回目のループが何を行うかを追加する必要があります
  for (var i = 0; i < data.length; ++i) {
    var row = data[i];

    row.rowNumber = i + 2;

    // Result列がブランクであれば処理を実行
    if (row[5]) {
      var result = "";
      // 何かの処理を行う
    }
  }
}
```

## 補足
基本的に、参考情報にあるサイトのコードを活用しています（ほぼコピです笑）。
手を加えたところでいくと、F列のIntevalの設定の追加しています。

あまり知られていないことですが、Googleグループには所属するメンバーに対してメールを送るタイミングを細かく設定することができます。

このタイミングは、大きく３パターンあり、常に所属メンバーへメールを送る（通常の形）か、1日に１回昨日分のメールがまとめてメンバーに送られるパターン、そして一切メンバーにメールが送られないパターンです。

その設定をF列のところで行っています。細かい引数についてはGoogleGroupのAPIサイトから確認することができます。

また、メンバーを追加/削除する対象のGoogleグループの指定はあくまで③（C列）になります。
②（B列）は、GWSの管理コンソール上のGoogleグループの一覧表示の部分で、会社によってはアドレス表示で問題ないと思います（②を設定しなくても③が設定されていればOK）。


## 参考情報
[Googleグループの設定と管理｜吉田航｜note](https://note.com/w_yoshida/n/n2d31b254d005)
AZ-104試験対策のまとめ「VM編①」

## はじめに
Microsoft Azure Administrator(AZ-104)合格に向けた勉強でまとめた内容を本記事では投稿しています。
試験範囲の内容を私が勉強していく中で、
・忘れてしまっていた内容
・他の知識と絡めて理解した方が良い内容
・Microsoft Learnの読解に苦戦した内容
などをできるだけ分かりやすく簡潔にまとめることを意識しています。
Azureの勉強を一から始め、AZ-104取得を目指している方の参考になれば幸いです。
※本記事の内容が必ず試験に出るわけではないこと、ご注意ください

## 試験対策のまとめ
### **ディザスターリカバリーの設定方法**
1. 下記のアクセス許可があることを確認
    * Recovery Servicesコンテナの作成
    * ターゲットリージョンのVM作成
1. VMに下記があることを確認
    * アウトバウンドネットワーク接続
    * 最新のルート証明書
1. ソースリージョンを除く任意のリージョンにRecovery Servicesコンテナを作成し、Site Recoveryを有効化
1. コンテナで下記を設定しVMのレプリケーションを有効化
    * ソースの設定
    * 有効にするVM(Max10個)
    * ターゲットの設定
    * レプリケーションポリシー
        - 復旧ポイントのリテンション期間(デフォルト24時間)
        - アプリ整合性スナップショットの頻度

### **フェールバックの準備**
1. プライマリリージョンからセカンダリリージョンにフェールオーバー
1. フェールオーバーされたVMの復旧ポイント確認・変更
1. フェールオーバーされたVMをコミット(以降は復旧ポイントの変更不可)
1. プライマリリージョンが利用可能であることを確認
1. フェールオーバーされたVMを再保護
1. フェールバックの準備完了

* 参考
    - https://learn.microsoft.com/ja-jp/azure/site-recovery/azure-to-azure-tutorial-enable-replication
    - https://learn.microsoft.com/ja-jp/azure/site-recovery/azure-to-azure-tutorial-failover-failback


## 次の試験対策のまとめ
* **AZ-104試験対策のまとめ「VM編②」**

https://qiita.com/fsd-jume/items/8aa6e8aa444e8a30cfb1

## 学習内容をまとめたページの一覧

https://qiita.com/fsd-jume/items/6eda24728c9c03c36bc3
プログラムのテスト方法＝（ホワイトボックステスト, ブラックボックステスト）

# 1.はじめに
+ 基本的にGPTで調べたことを自分の言葉に置き換えているので精度は保証しない
+ テーマは、プログラムのテスト方法
ホワイトボックステストとブラックボックステストについて書く


# 2.本文
+ ホワイトボックステスト（内部）とブラックボックステスト（外部）の違い

|  | ホワイトボックス |ブラックボックス|
|:-:|:-:|:-:|
| テスト対象へのアプローチ | ソフトウェアの内部構造やコードの実装に焦点を当てる。　ソースコードを見ることがあり、コードの特定のパスや条件をテストする。　制御フロー、ループ、例外処理などプログラムの内部構造テストする。| ソフトウェアの内部構造を無視し、外部の振る舞いに焦点を当てる。　ソースコードを見ずに、ソフトウェアの仕様や要求仕様に基づいてテストケースを設計する。　機能や振る舞いが正確であることを確認する。|
| テストケースの設計  | コードの内部知識を必要とするテストコードを設計する。 条件分岐やコードカバレッジを使用して、どの部分のコードを実行したか、どのパスを通過したか追跡する。　ユニットテスト（関数やメソッドのテスト）に適している。|ユーザがソフトウェアをどのように使用するかを考え、ユーザ視点でテストケースを設計する。　ソフトウェアの利用者が必要な操作を模倣し、ソフトウェアの機能や振る舞いをテストする。　システムテスト、統合テストなど、ソフトウェア全体のテストをするのに適している。|
|テストの焦点|コードの正確性や内部ロジックのテストに焦点を当てる。　通常、単体テストに使用され、ユニット内の個別なコンポーネントや関数の振る舞いに当てられる。|ソフトウェアの外部振る舞いや機能をテストする。　ユーザがソフトウェアを操作する際の期待される動作を確認し、ソフトウェアが要求事項を満たすかどうかを評価する。|
|主に使うテスト|単体テスト（ユニットテスト）|システムテスト、統合テスト|

一般的にユーザからの視点のテスト（ブラックボックステスト）とコードからのテスト（ホワイトボックステスト）の両方がプロジェクト全体で行われることが多い。
ゲームで例えると、ゲームユーザーからのバグ報告はブラックボックス側のテストで、開発者側で内部のコードをいじってバグ対策するのがホワイトボックス側のテストかな。

+ ホワイトボックステスト（内部）
・内部コンポーネントを考慮するテスト方式
・内部を考慮するので値を副作用で変更しないことを保証できる。

|特徴|説明|
|:-:|:-:|
|  テスターがソースコードを見る |  ソースコードやプログラムの内部構造を調査し、コードの特定のパスや条件をテストする。 |
|  条件分岐とコードカバレッジ | 条件分岐やコードカバレッジの情報を使用して、どのコードを実行したか、どのパスを通過したかの確認をする。  |
|  制御フローのテスト | 制御フロー、ループ、例外処理などのプログラムの制御構造のテストをする。 |
|  ユニットテストに適している |  ユニットテスト（個別のコンポーネントのテストや関数のテスト）に適している。 |
|  テストケースの設計が技術的 | テストケースの設計がソフトウェアの内部知識を必要とするため、技術的スキルが必要。  |

・例
```white_box.py
"与えられる整数リストから最大値を見つける関数のテスト（ユニットテスト）"
def find_max(numbers):
    if not numbers:
        return None
    max_val = numbers[0]
    for num in numbers:
        if num > max_val:
            max_val = num
    return max_val

"ホワイトボックステストをするために以下のテストケースを作成する。　正しければ最大値を返すはず。"
"空のリストを与えた場合のテスト"
def test_empty_list():
    assert find_max([]) is None
"正の整数を与えた場合のテスト"
def test_positive_numbers():
    assert find_max([1, 3, 5, 2, 4]) == 5
"負の整数を含むリストを与えた場合のテスト"
def test_negative_numbers():
    assert find_max([-1, -3, -5, -2, -4]) == -1
"重複した最大値を含むリストを与えた場合のテスト"
def test_duplicate_max():
    assert find_max([1, 3, 5, 5, 4]) == 5
```

+ ブラックボックステスト（外部）
・プログラムの全体の入力と出力に焦点を当てている。
・内部コンポーネントを考慮しないテスト方式
・内部を考慮しないので値を副作用で変更する可能性がある。

| 特徴 | 説明 |
|:-:|:-:|
| ソースコードを見ない |  テスターは、ソースコードを見ない。代わりに、ソフトウェアの仕様や要求仕様に基づいてテストケースを設計する。 |
| 機能と動作のテスト |  ソフトウェアが要求された機能や振る舞いを満たしているかどうかを確認する。 |
| ユーザ視点のテスト | ユーザがソフトウェアをどのように操作するかを模倣し、ユーザ視点からテストをする。 |
| テストケースの設計がドメイン知識に基づく | ソフトウェアの利用者が必要な操作を考え、テストケースを設計するため、ドメイン知識が必要。  |
| 統合テストやシステムテストに適している | ソフトウェアの様々なコンポーネントやシステム全体のテストに適している。  |

・例
電卓アプリケーションのブラックボックステスト
アプリには加算ボタンがあり、２つの値を入力し、その合計値を表示する機能がある。
テストケースの設計：
1.正の整数の加算：
　・入力：2,3
　・期待される結果：５
　・テストの目的：アプリは正の整数を正しく加算できるかどうかを確認する。
2.負の整数の加算：
　・入力：-2,-3
　・期待される結果：-５
　・テストの目的：アプリは負の整数を正しく加算できるかどうかを確認する。
3.少数の加算：
　・入力：2.5, 3.7
　・期待される結果： 6.2
　・テストの目的：アプリは少数を正しく処理できるかどうかを確認する。
4.ゼロを含む加算：
　・入力：0, 5
　・期待される結果：５
　・テストの目的：アプリはゼロを正しく処理できるかどうかを確認する。
5.エラーケース：無効な入力：
　・入力："abc", 3
　・期待される結果：エラーメッセージ
　・テストの目的：アプリは無効な値を正しく処理し、エラーメッセージを表示できるかどうかを確認する。

# 3.所感
・調べるまでは両方ともコードを書くだけだと思っていた。
・説明だけではイメージしにくかったが例を出せばイメージしやすくなった。

# ex.参考資料
ChatGPT-3.5
[Recursion](https://recursionist.io/)
UnrealEngineにおいてWindowsのタッチをシミュレートしてみる



![touchWindow.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16768/e707e07c-8295-8c8c-010d-f3069353e347.gif)

上記のようにUnrealEngineのUI上でゲームコントローラーの右と左のスティックでポインタを操作してタッチ処理を行わせることができました。
2つのタッチを扱ってピンチインやピンチアウトの操作もできます。

# 試した環境

* UE5.3.1
* Windows11

# 参照サイト

* [InjectTouchInput 関数 (winuser.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-injecttouchinput) 
タッチを実現するInjectTouchInputの公式説明
特にタッチ インジェクション シーケンスの図が参考にしました
* [C# + InjectTouchInput でタッチをシミュレートする - Qiita](https://qiita.com/soumi/items/b1bd4ec8525d7110b317)
ハマりどころを参照
* [Unity で Windows のマルチタッチ操作をエミュレートできる uTouchInjection を作った - 凹みTips](https://tips.hecomi.com/entry/2018/07/21/171632)
C++コードを参照

# 作成してみたコード

以下のWindowsのウィンドウをキャプチャするWindowCapture2Dプラグインにタッチ機能を追加してみました。
サンプルプロジェクトにおいてゲームコントローラーでキャプチャしたスクリーンのタッチが行えるようにしています（キーボードでも操作できるようにしてますが、タッチをするとフォーカスが外れてうまくいかない感じです）。

https://github.com/mechamogera/WindowCapture2D

以下がタッチ機能のコア部分の機能です。
ウィンドウキャプチャの機能とはあまり依存関係がないので単独でも使用できるかと思います。

https://github.com/mechamogera/WindowCapture2D/blob/master/Plugins/WindowCapture2D/Source/WindowCapture2D/Public/Touch.h
https://github.com/mechamogera/WindowCapture2D/blob/master/Plugins/WindowCapture2D/Source/WindowCapture2D/Private/Touch.cpp
https://github.com/mechamogera/WindowCapture2D/blob/master/Plugins/WindowCapture2D/Source/WindowCapture2D/Public/TouchManager.h
https://github.com/mechamogera/WindowCapture2D/blob/master/Plugins/WindowCapture2D/Source/WindowCapture2D/Private/TouchManager.cpp

# InjectTouchInput関数をUnrealEngineで利用する

WinUser.hではInjectTouchInputと関係する構造体は以下の#ifの中にあります。
```c++
#if(WINVER ?= 0x0602)
```

UnrealEngineのデフォルトではWINVERは0x0601なのでそのままではInjectTouchInputが使えません。

[How to define _WIN32_WINNT and WINVER? - Programming & Scripting / C++ - Epic Developer Community Forums](https://forums.unrealengine.com/t/how-to-define-win32-winnt-and-winver/460549)
このため、上記のページを参照してプロジェクトのbuild.csに以下を追加して対応しました。

```c#
PublicDefinitions.Remove("WINVER=0x0601");
PublicDefinitions.Remove("_WIN32_WINNT=0x0601");
PublicDefinitions.Add("WINVER=0x0602");
PublicDefinitions.Add("_WIN32_WINNT=0x0602");
```

WindowCapture2Dプラグインでは以下の個所にあたります。
https://github.com/mechamogera/WindowCapture2D/blob/master/Plugins/WindowCapture2D/Source/WindowCapture2D/WindowCapture2D.Build.cs#L65-L68

以前はdllを用意して対応しましたがこの方が楽ではありますね（[UE4で新しいWin APIを使う - Qiita](https://qiita.com/mechamogera/items/983487dee4c29cc31028)参照）。
ただ、ビルド時に定義の重複で警告は出てしまいます。
[Flutter] 新規プロジェクト作成時にサンプルコード & コメント無しで始める

```bash
flutter create -e your_project_name
```

と、`-e` フラグを付けてプロジェクト作成すると、以下の様にテキスト表示のみのアプリが作成されます。コメントもつきません :no_good: 

```main.dart
import 'package:flutter/material.dart';

void main() {
  runApp(const MainApp());
}

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      home: Scaffold(
        body: Center(
          child: Text('Hello World!'),
        ),
      ),
    );
  }
}
```

```pubspec.yaml
name: your_project_name
description: A new Flutter project.
publish_to: 'none'
version: 0.1.0

environment:
  sdk: '>=3.1.0 <4.0.0'

dependencies:
  flutter:
    sdk: flutter

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^2.0.0

flutter:
  uses-material-design: true
```


### 関連
[Add --empty to the flutter create command #113873
](https://github.com/flutter/flutter/pull/113873)
ReactのuseReducerとは？useStateとの違いやユースケースなど

`useReducer` は React のフックの1つで、より複雑なコンポーネントの状態管理に役立ちます。特に多数のサブバリューや、次の状態が前の状態に依存する場合や、ロジックを再利用やテストしたい場合などに有用です。

`useReducer` は、以下のようなシグネチャを持つ関数です:

```javascript
const [state, dispatch] = useReducer(reducer, initialState);
```

ここで、

- `reducer` は、現在の状態とアクションを引数として取り、新しい状態を返す関数です。
- `initialState` は、状態の初期値です。
- `state` は、現在の状態を保持する変数です。
- `dispatch` は、アクションをreducerに送信するための関数です。

簡単な例として、カウンターのアプリケーションを考えてみましょう:

```javascript
import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  );
}
```

この例では、`reducer` 関数は2つのアクションタイプ (`increment` と `decrement`) を処理します。ユーザーがボタンをクリックすると、`dispatch` 関数を使用して、適切なアクションタイプを `reducer` に送信します。そして、`reducer` は新しい状態を返し、コンポーネントが再レンダリングされます。

`useReducer` は、状態の変更ロジックを一箇所に集め、状態の変更を予測可能にすることができるため、大きなコンポーネントや複雑な状態のロジックに特に有用です。

## useReducerとuseStateの違いは？

`useReducer` と `useState` は、React コンポーネントの状態を管理するためのフックですが、それぞれ異なるユースケースや特性があります。以下に、それぞれの特性や違いを挙げてみましょう。

### `useState`:
1. **単純性**: `useState` はシンプルな状態管理に適しています。状態が一つの値や簡単なオブジェクトの場合、`useState` の方が簡潔で理解しやすいことが多いです。
2. **API**: `useState` の API は簡単です。初期値を与えると、現在の状態とその状態を更新する関数のペアが返されます。
   ```javascript
   const [state, setState] = useState(initialValue);
   ```
3. **更新の方法**: 新しい状態の値を直接与えるか、前の状態を基にした関数を使用して更新できます。
   ```javascript
   setState(newValue);
   // または
   setState(prevState => newValueBasedOnPrevState);
   ```

### `useReducer`:
1. **複雑な状態**: `useReducer` は、複雑な状態ロジックや、前の状態に基づく計算が多い場合に有用です。
2. **API**: `useReducer` は、状態とディスパッチ関数のペアを返す関数です。reducer 関数と初期状態を引数として受け取ります。
   ```javascript
   const [state, dispatch] = useReducer(reducer, initialState);
   ```
3. **更新の方法**: `dispatch` 関数を使用して、アクションを reducer に送信します。reducer は現在の状態とアクションを受け取り、新しい状態を返します。
   ```javascript
   dispatch({ type: 'ACTION_TYPE', ...additionalData });
   ```

### 主な違い:
- `useState` はシンプルな状態管理に適しており、`useReducer` は複雑な状態やロジックの管理に適しています。
- `useState` は状態の更新関数を提供し、新しい状態の値や関数を直接受け取ります。一方、`useReducer` はアクションを通じて状態の更新を行い、reducer 関数を使用して新しい状態を計算します。
- `useReducer` は、複数のサブバリューや複雑な状態遷移、ロジックの再利用、中央集権的な状態管理などにより適しています。

最終的に、どちらのフックを使用するかは、特定のユースケースや状態管理の要件によって異なります。状態の管理がシンプルであれば `useState`、複雑であれば `useReducer` を選択することが一般的なアプローチです。