OmniAICreator/Qiita-1.07M huggingface dataset:
フロンエンド周りで今年取り組んだこと

この記事は[Supershipグループ Advent Calendar 2022](https://qiita.com/advent-calendar/2022/supership)の21日目の記事です。

## はじめに

今年取り組んだフロントエンドに関しての内容になります。

## WebpackからViteに移行

[Vite](https://vitejs.dev/)に移行してビルドが速くなり快適になりました。

Viteのはアプリケーションのソースコード上で使う環境変数は`VITE_`というプレフィックスを付けて`import.meta.env`から読み込むように修正が必要ですが

* 既存の環境変数名を全部修正する必要が出る
* 既存のソースコードの環境変数を参照してる箇所を　`import.meta.env` 経由に修正する必要が出る
* 将来的にVite以外のビルドツールに移行した時に`VITE_`というプレフィックスを取るのに環境変数名を再度修正する必要が出る

ということもあり`import.meta.env`を使わない方法を取りました。

```ts:vite.config.ts
import { defineConfig } from 'vite';

export default defineConfig({
  define: {
    'process.env.API_URL': JSON.stringify(process.env.API_URL),
    'process.env.API_TOKEN': JSON.stringify(process.env.API_TOKEN),
  }
});
```

## Apollo Clientからgraphql-requestに移行

GraphQLのクライアントとして[Apollo Client](https://www.apollographql.com/)を使っていました。

以下の理由から[graphql-request](https://github.com/prisma-labs/graphql-request)に移行しました。

* Apollo Clientのキャッシュを使い込むぐらい使わなかった

* サーバーがステータスコード200番以外を返した時のエラーハンドリングが[onError](https://www.apollographql.com/docs/react/api/link/apollo-link-error)でハンドルすることになるのが扱いずらかった

## Sentry

エラー通知に[Setnry](https://sentry.io/welcome/)を活用してまして、使ってみて分かったことです。

### tracesSampleRate
  https://docs.sentry.io/platforms/javascript/guides/react/#configure

  `tracesSampleRate` はドキュメントのサンプルコード通り1.0に設定するのではなく、適切な値を設定するように注意したほうが良さそうです。
  実際に `tracesSampleRate` を1.0にしてみたところ全部サンプリングするという意味になり、Sentryのクオーター上限を超えそうになりました。

### ソースマップをSentryにアップロード
  https://docs.sentry.io/platforms/javascript/sourcemaps/
  ビルドした時のソースマップをSentryにアップロードすることで、JavaScriptのエラーが発生した時に箇所のソースコードがSentryで表示されるようになるので調査しやすくなります。

### Auth Token
  例えばデプロイ時にSentryのAPIを使ってなにかを行っていた場合にAuth Tokenを個人のアカウントで発行してしまうと、退職や異動などでアカウントを削除した場合に動かなくなってしまうということが起こりえます。

  [Internal Integration](https://docs.sentry.io/product/integrations/integration-platform/internal-integration/)を使うとorganization単位になり個人に依存しなくなります。

## Corepack

Node.js [v14.19.0](https://nodejs.org/en/blog/release/v14.19.0/)からCorepackが標準バンドルされることになりました。
使うパッケージマネージャーを固定できます。

```json:package.json
{
  "packageManager": "yarn@1.22.19"
}
```


```sh
$ corepack enable
```

## Reactの気になった話題

### Add fetch Instrumentation to Dedupe Fetches

https://github.com/facebook/react/pull/25516

React側でfetchをパッチして、GETかHEADの時はレスポンスをキャッシュするという内容です。

[否定的な意見](https://github.com/facebook/react/issues/25573)もあるようです。

### RFC: First class support for promises and async/await

https://github.com/reactjs/rfcs/pull/229

RFCの段階なので導入されるかは未定ですが、Reactコンポーネントの中でuseというhookにPromiseを渡すと結果を取り出せるという内容です。

今まで他のライブラリにあったuseQueryなどを使わなくても非同期処理を扱いやすくする流れにみえますね。

## 来年やっていきたいこと

### Zero-runtime CSS in JS
  CSS in JSをビルド時に静的なCSSを吐き出してレンダリングを高速化するアプローチです。
  [Linaria](https://linaria.dev/)や[vanilla-extract](https://vanilla-extract.style/)などのライブラリが出てきてるので検証してみたいです。

### Yarn v3
  Zero-installsで使ってるnpmパッケージのcacheファイルをgitリポジトリに含めるのはやりたくなかったのでアップデートしてなかったという経緯です。

いつまでもv1系を使い続けるのもというのがあります。

## 最後に

Supershipではプロダクト開発やサービス開発に関わる人を絶賛募集しております。
ご興味がある方は以下リンクよりご確認ください。

 [Supership 採用サイト](https://supership.jp/recruit/)

是非ともよろしくお願いします。
justify-contentが効かない

下記サイトで模写コーディング中、`justify-content`が効かなかったので色々調べて解決するまでの備忘録です。

https://code-step.com/profile-menu/


https://zero-plus.io/media/css-justify-content/
justify-contentが効かない原因についてこちらのサイトにまとめられていたので引用します。
```
以下は justify-content が効かないおもな原因です。

①　display: flex; の指定を忘れている
②　子要素に margin: auto; を指定している
③　before・after を使用している
④ 子要素のテキストを中央寄せにしようとしている
```
上記の原因に当てはまっていないか自分が書いたコードとにらめっこしてしても原因がわからない...

https://developer.mozilla.org/ja/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox

ディベロッパーツールで見本のコードと見比べて何が違うか、調べてみたところ...

***原因***
入力したテキストが長くて横に伸びていた。
![スクリーンショット 2022-12-16 12.57.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3014485/6e443b39-d294-b834-4df6-354de8ed0661.png)

**解決方法**
テキストに`<br>`を入れて改行したら見本通りになりました！
![スクリーンショット 2022-12-16 12.56.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3014485/8163bca5-3399-055c-6870-b7dc258c865b.png)
Databricks にて 空白の値、あるいは、スペースの値となっているカラムを NULL に置換する Python関数の作成方法

## 概要

Databricks にて 空白の値、あるいは、スペースの値となっているカラムを NULL に置換する Python関数を共有します。

```python
from pyspark.sql.functions import expr


def replace_blank_or_space_to_null(
    tgt_df,
    tgt_cols,
    value_after_replacement="NULL",
):
    for tgt_col in tgt_cols:
        case_state = f"""
        CASE WHEN TRIM({tgt_col}) != ''
            THEN {tgt_col}
            ELSE {value_after_replacement}
        END
        """
        tgt_df = tgt_df.withColumn(tgt_col, expr(case_state))
    return tgt_df
```

Spark 3.3 以降であれば、下記の関数を利用してください。

```python
from pyspark.sql.functions import expr


def replace_blank_or_space_to_null(
    tgt_df,
    tgt_cols,
    value_after_replacement="NULL",
):
    with_conds = {}
    for tgt_col in tgt_cols:
        case_state = f"""
        CASE WHEN TRIM({tgt_col}) != ''
            THEN {tgt_col}
            ELSE {value_after_replacement}
        END
        """
        with_conds[tgt_col] = expr(case_state)
    tgt_df = tgt_df.withColumns(with_conds)
    return tgt_df
```

## 動作確認
### 1. 関数を定義

```python
from pyspark.sql.functions import expr


def replace_blank_or_space_to_null(
    tgt_df,
    tgt_cols,
    value_after_replacement="NULL",
):
    for tgt_col in tgt_cols:
        case_state = f"""
        CASE WHEN TRIM({tgt_col}) != ''
            THEN {tgt_col}
            ELSE {value_after_replacement}
        END
        """
        tgt_df = tgt_df.withColumn(tgt_col, expr(case_state))
    return tgt_df
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/18dff52b-9b8f-22e6-3532-a7b7801bf1c8.png)

Spark 3.3 以降の場合

```python
from pyspark.sql.functions import expr


def replace_blank_or_space_to_null(
    tgt_df,
    tgt_cols,
    value_after_replacement="NULL",
):
    with_conds = {}
    for tgt_col in tgt_cols:
        case_state = f"""
        CASE WHEN TRIM({tgt_col}) != ''
            THEN {tgt_col}
            ELSE {value_after_replacement}
        END
        """
        with_conds[tgt_col] = expr(case_state)
    tgt_df = tgt_df.withColumns(with_conds)
    return tgt_df
```

### 2. データフレームを作成

```python
data = [
    {
        "col_001": "1",
        "col_002": "",
        "col_003": " ",
        "col_004": "  ",
    }
]
 
df = spark.createDataFrame(data)
df.display()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/4bedb706-f47e-971b-55d2-a541faf3d967.png)


### 3. 関数の利用

空白、あるいは、スペースとなっている`col_002`、`col_003`、及び、`col_004`が`NULL`となっていること確認

```python
tgt_col_info = {
    "col_001",
    "col_002",
    "col_003",
    "col_004",
}

result_df = replace_blank_or_space_to_null(df, tgt_col_info)

result_df.display()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24031/6def20a8-4741-0445-a995-a6b968335755.png)
PMP®受験を通してためになったこと

## はじめに
こんにちは！
この記事は[ミロゴス Advent Calendar 2022](https://qiita.com/advent-calendar/2022/milogos) 17日目の記事です。

今回は私が2022年6月に受験したPMP（Project Management Professional）試験の学習を通して、勉強になった内容をピックアップしてご紹介できればと思います。

ちなみにPMP試験を知ってから合格するまでの体験記は[こちらの記事](https://qiita.com/koji_fumi/items/64c1026d05c36f194b67)で紹介しておりますので、良かったら併せてご一読いただけますと幸いです！

## 前提：PMBOKのプロジェクトマネジメントプロセスの考え方
PMP試験は「PMBOK」と呼ばれるプロジェクトマネージメントの体系本の内容から多く問題が出題されます。
PMBOKはアメリカのPMIという機関が策定しており、数年に一度内容が改定されておりますが、現在のPMP試験では、PMBOK第6版の内容が多く出題されています。

私も例に漏れずPMBOK第6版の内容を学習したのですが、その中で出てくる「プロジェクトマネジメントプロセス」の考え方が実際のプロジェクトを進める際に役に立っているなと感じています。

このPMBOKで定義されている「プロジェクトマネジメントプロセス」は49のプロセスで構成されており、各プロセスは以下のマトリックスのいずれかに割り当てられています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609800/20b293fc-cd5d-d3d8-946a-a24a2d84a7ba.png)

※引用元：[PMサムライ](https://www.pm-samurai.net/pmp-49processes/)

プロジェクトは有期性と独自性が特徴ですので、上記と全く同じ形で進むことはまず無いですが、実際のプロジェクトを進める上でこのプロセスの考え方は私の指針の1つになっているなと感じます。


## ためになったこと：プロジェクト上流工程においての考え方
もう少し具体的な内容について以下で触れたいと思います。
私はプロジェクト上流工程における考え方という点でためになったなと感じます。

### 最初に目的を明確にすることの重要性
プロジェクト立ち上げ段階においての「目的の明確化」の重要性はPMBOKではかなり強調されており、私自身、PMPの学習を通して目的を明確にすることの意識をより強く持つようになりました。

PMBOKでも唱えられている通り、全てのプロジェクトは、そのプロジェクトの目的を明確化するところから始まります。
逆説的に言うと、全てのプロジェクトは何かしらの目的があるからこそ行われます。

当たり前のことですが、私はこの内容を見た際、少々ドキッとしました……。
というのも、プロジェクトの目的が明確になっておらず、故に成果物が出来上がった後に大きな仕様変更が必要になる、などの経験をしたことがあったからです。

と同時に、実際世の中には明確な目的が定まっていないまま進行し、結果失敗に終わってしまうプロジェクトは多く存在していそうだなと思いました。

例えば、「DX化推進プロジェクト」はその最たる例なのではないかと思いました。
「DX化推進プロジェクト」は上手くいかないことが多いとよく耳にします。
その理由を考えたときに、「DX化推進プロジェクト」は、「DX化」という本来は何かを解決するための「手段」が目的化しており、「本来の目的（なんのためにDX化するのか）」が明確になってないからなんだろうな、とPMBOKを学習しながら勝手に腑に落ちていました。

「そもそもなんのためにやるのか？」という問いをプロジェクト進行していく際に常に持ち続けたいと強く思いました。


### スケジュール作成までのステップ
プロジェクトを進める際にスケジュールの作成は必須です。

ただ、必要な成果物やタスクを洗い出す前にスケジュールを切って、後になってタスク漏れやスケジュール遅延を引き起こしてしまったことはありませんか？
私自身、そういった経験が何度かありました……。

以下にプロジェクトの立ち上げからスケジュール策定までの大まかな順序をPMBOK「プロジェクトマネジメントプロセス」の内容を基に記載します。（若干意訳しており、細かい部分は割愛しています）

1. プロジェクトの目的を明確にする（プロジェクトのアウトカムを定義する）
1. 要求事項を収集する（要求収集・整理）
1. 目的・要求事項を基にスコープを定義する（要件定義）
1. WBSを作成する（成果物をワークパッケージ単位まで分解する）
1. 成果物を生み出すために必要なタスクを定義する
1. タスクの着手順序を設定する
1. 各タスクの所要期間を見積る
1. スケジュールを作成する

上記の通りPMBOKでは、スケジュール作成までに「目的の明確化」、「スコープの定義」、「WBSの作成」、「タスクの洗い出し」などのステップが定義されています。
当然といえば当然の内容なのですが、私はこの大枠の流れを体系的に抑えられたことで、プロジェクト上流工程であたふたしなくなった気がします。

例えば、以前はスケジュールを組む際、タスクの洗い出しとスケジュール設定を並行してやろうとしてしまうことがありました。
ただ、上記の流れを掴んだことで、「まずは成果物・ワークパッケージから逆算したタスクの洗い出しを先に行おう。そして、洗い出しきった上でそれぞれのタスクの順序・所要期間・スケジュールを設定していこう」と考える順序がクリアになり、より効率的かつ正確なスケジュール設定ができるようになった気がします。


## 最後に
最後までご覧いただきありがとうございました！
まめとめると、PMP受験（PMBOK学習）を通して今まで何となくは分かっていたけれど、明確に言語化できていなかったプロジェクトの流れや考え方を体系的にインプットでき、その点が非常に良かったと思います。
私自身、PMP受験を通して学んだことをより業務に活かしていければと思っております！

この記事が少しでも多くの方のお役に立てれば幸いです。
技育CAMP vol9に参加してみた

# 技育CAMPとは
参加の敷居が1番低いハッカソンを目標にしている、開発期間2日のハッカソンです。
開発開始日の8日前にキックオフがあり、そこからデザイン等を考え、開発日の2日間でコードを書きまくります。
賞金等は
・最優秀賞：5万円
・優秀賞：3万円
・努力賞：5千円
・参加賞：各メンバー１千円

## 技育CAMPの良かった点
・メンターの方が質問から3分でレスポンスが来る
・質問の解決率が高い
・Slackの動きが活発
・参加するだけで1000円以上の参加賞がもらえる

## 私(私たち4人チーム)が作ったもの
私たち（チーム名：転プロ）が開発したものは **「♡のカレンダー」** です。
https://strong-toffee-42101a.netlify.app/　←スマホ画面推奨
作品概要は、記念日を登録して管理することができ、記念日が近くなると通知もしてくれるというものです。
使用技術はVue3・PHP・MySQLです。

今回初めてVue3で開発したのですが、メンターさんのレスポンスが速かったため開発を完成までもっていくことができました。
v-if,v-for,v-show,component,v-routerとVueの要素てんこ盛りで作成しました笑
## 受賞結果
**参加賞** でした😭

チームのメンバーもかなり納得のできるものが作成できたので、予選を抜けて努力賞はもらえるんじゃないかと思っていましたが、予選を抜けることができませんでした。

色々原因を探ったところ、発表の仕方が悪かったのではないかという反省に至りました。
当日行った発表の仕方が、
「投稿はこのようにしてこうします。」
「通知はこのようにしたらこのように出てきます。」
のように、ただ機能を説明しただけの発表をしてしまっていました。
ギリギリまで開発していたので、発表資料の作成に力を入れることができませんでした。
時間管理は大切だと改めて痛感させられました...

## 今後の展望
今回開発した「♡のカレンダー」自体はそこまで悪くないとチームでも感じたので、もうちょっとブラッシュアップして、別のコンテストに応募しようと考えています。
発表資料と発表原稿をしっかり考えて挑もうと思います！
パフォーマンスを意識するロジックの書き方

# はじめに
今回は、普段の開発におけるソースレビューを通じて見つかった改善点を紹介したいと思います。

オンラインゲームやソーシャル系のアプリでは、サーバー側は常に大量のデータを扱っています。細かい修正でも積み重なると大きなパフォーマンス改善になりますので、この辺を常に意識して開発に取り組むことが大切です。

# 例１： 複数条件
例として、あるアイテムを所持してる場合に10%の確率で当たるという仕様の実装だと、javaではこのような感じで書きます。
```java
List<Item> userItems = this.userItemService.findUserItemByItemId(userId, itemId);
int rand = RandUtils.nextInt(100);
if (userItems.size() > 0 && rand < 10) {
     // あたった場合のロジック
}
```
改善すべきところがお分かりですか?

そうです！条件に必要なデータを全て揃えるのではなく、段階的に取得したほうが効率のいい書き方になります。ちょっとだけの修正で、DBへのアクセスを90%減らすことができます。
```java
int rand = RandUtils.nextInt(100);
if (rand < 10) {
  List<Item> userItems = this.userItemService.findUserItemByItemId(userId, itemId);
  if (userItems.size() > 0) {
     // あたった場合のロジック
  }
}
```
すべでの条件にはDBにアクセスが必要の場合でも、軽い処理を優先させることによってDBの負荷軽減に繋がります。
> つまり、軽い処理を優先にするとのことです。

# 例２：データ取得その１
１つ目の例の中に、実はもう1点改善できる部分があるのですが、気づきましたか？

ユーザーが特定のアイテムを所持してるかのチェックをする際に、所持アイテムをすべて取得していました。
```java
  List<Item> userItems = this.userItemService.findUserItemByItemId(userId, itemId);
```
しかし、実際は必要なアイテムだけを取得すれば問題ないため、クエリの改善を行ってみます。
```java
  Item userItem = this.userItemService.findFirstByUserIdAndItemId(userId, itemId);
```
# 例３：データ取得その２
こちらは、他人のほしいリストを表示した時に、リスト内のアイテムを自分も持っていることを示すflagを返すための実装です。
```java
Map allMyItems = this.service.getUserData(userId);
List<Item> pageList = this.service.fetchPagingList(friendUserId, fromNo, pageCnt);
for (Item item : pageList) {
  item.setExists(allMyItems.containKeys(item.getKey()));
}
```
こちらも同様に、自分の全ての所持アイテムデータを取得するよりも、1ページ分のアイテムのデータだけを取得したほうがパフォーマンスの良い実装になります。
```java
List<Item> pageList = this.service.fetchPagingList(friendUserId, fromNo, pageCnt);
Set<Integer> itemKeys = pageList.stream().map(Item::getKey).collect(Collectors.toSet());
Map myItems = this.service.getUserDataByKeys(userId, itemKeys);
for (Item item : pageList) {
  item.setExists(allMyItems.containKeys(item.getKey()));
}
```

> たとえDBのindexがあっても、必要な件数、必要なコラムだけを取得することでネットワークのトラフィク軽減にもつながるので、積極的そういう実装にしましょう。


# 例４：データ取得その３
最後に、ループ内でDBへのアクセスも可能な限りしないように実装を行いましょう。

普段はそういうことをしないと思いますが、既存のメソッドを呼んで、データを整形したり、情報を取り出したりした場合、そのメソッドの中さらにDBアクセスしてることを気づかずに行なってしまうケースが多いです。

今回は具体例は割愛しますが、ループ内でメソッドを呼び出す場合には中身をしっかりと確認したりしたほうがいいでしょう。

# まとめ
今回はいくつの典型的な例を紹介しました。どれも難しいことではなくシンプルなものですが、シンプルなものこそ気づきにくいかもしれないのですね。
Rundeckで同時実行ジョブを増やす設定

Tuning RundeckのQuartz job threadCountに記載されている

https://docs.rundeck.com/docs/administration/maintenance/tuning-rundeck.html#quartz-job-threadcount

```/etc/rundeck/rundeck-config.properties```に下記の行を追加して、マシンに適切なスレッド数を設定すれば良い。

```
quartz.threadPool.threadCount = 20
```

デフォルトは10なので、
同時に初めて終わってほしいジョブが、10ジョブずつ処理されて時間がずれるので困っていた。
真面目に運用してる人は知ってるんだろうな。
Teradata Studio/Studio Expressを使ってみよう！

こんにちは！

今日はVantageにクエリーをポストするのに最も使われているであろうツール、Teradata StudioとStudio Expressについて紹介します。

StudioとStudio Expressの違いは何かというと、Studioには
|機能|概要|
|:-|:-|
|システム管理|ユーザーを作ったり権限設定してみたり、データベースやテーブルのサイズをみてみたりをGUIで行うことができる|
|クエリー実行|エディタでクエリーを書き、Vantageにポストし結果を得る。クエリーの実行履歴も管理できます|
|簡易ロード機能|Vantageのネイティブローダ―「Fastload」を実行するためのウィザードと実行結果を管理する|

がありますが、Expressは真ん中のクエリー実行だけが実装された簡易版となっています。
クエリーを実行することに関してはExpressで十分ですので、この記事ではStudio Expressを使って説明を進めます。

# インストール
別でポストされている記事「[ダウンロードサイトを知ろう！]()」を参考にダウンロードしてください。
ダウンロードしたファイルを解凍し、「Setup」というアプリケーションを実行するというごくごく普通のアクションでインストールしていただくことができます。

# クイック・ツアー
インストール後に初めて起動した際には「Quick Tour」というタブが表示されます。基本的な利用ガイドを見ていただけます。（英語ですけどね…そういえばStudio/Studio Express自体、現在は英語版のみが提供されています…）

タブの×印をクリックすればクイック・ツアーを終了できます。（これも普通のアクションですね）

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742714/86bed003-e565-67f2-7c3d-0322ccf8b281.png)

# Vantageへの接続設定
初回起動後のクイックツアーを閉じると今度はVantageへの接続設定ウィンドウがポップアップします。
「Connection Profile Types」では「Teradata」（昔の名前でごめんなさい）を選択しNameにこのコネクションの名前を入力したら「Next」ボタンをクリックです。

![画像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742714/3b3040d2-6995-a879-c9ff-21420e3191a1.png)

次はVantageの詳細を入力する画面がポップアップしますので入力していきます。
「Database Server Name」には接続するVantageサーバーのIPドレスやURLを入力します。
「User Name」は最初は「dbc」（Vantageのユーザー階層のトップに君臨する人。いわゆるスーパーユーザー）になります。
「Password」にユーザーdbcのパスワードを入力してください。
とりあえずこの3つを入力すれば問題なくクエリーを実行していただくことができます。

またウィンドウ下部にある「test Connection」をクリックすると入力した情報が正しいものであるか確認できます。簡単に言えばログイン・プロセスを試行してログインできるかを確認してます。

最下部の「Finish」ボタンをクリックしていただけばこの実際にVantageにログインしさらに接続設定が保存されます。

![画像3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742714/4c8cd277-3fa5-72d2-b448-4abf2893209c.png)

# クエリーの実行
クエリーの実行は右側上のエディターで行います。

![画像6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742714/1357f172-8e74-354f-9f6d-e4ba15950e80.png)

ちなみにこのエディター、書かれているクエリーに間違いがあるとクエリーの左側に「赤い丸に×」のマークが出てそれを教えてくれます。

![画像5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2742714/8a2d7caa-54d5-ed29-6a4d-c596a4e83510.png)

クエリーを書いたらF5キーかエディターペインの右上にある横向きの△をクリックすればクエリーを実行できます。

# もっと詳しく知りたい方のために
Teradata StudioとStudio Expressには日本語マニュアルも用意されていますのでぜひ参考になさってください。

[Teradata® Studio™ and Studio™ Express インストール ガイド](https://docs.teradata.com/r/o0xKjV7i4dczVL~BYT4ocg/root)
[Teradata® Studio™ ユーザー ガイド](https://docs.teradata.com/r/KbmBAMJMfFMtbqBmI1B8GA/root)
[Teradata® Studio™ Express ユーザー ガイド](https://docs.teradata.com/r/FZJrJPL4cFv2V9tYkfvEXQ/root)

#### Teradata Vantageへのお問合せ
[Teradata Vantage へのお問合せ](https://www.teradata.jp/About-Us/Contact)
NextcloudのPHPバージョン変更警告

## Nextcloud 25.0.2

:::note alert
PHPバージョンを7.4系から8.2系に引き上げないでください
:::

Nextcloud 25.0.2では、セキュリティ＆セットアップ警告にPHPバージョンを7.4からアップデートを推奨する旨のメッセージが表示されています。
![Screenshot 2022-12-16 105746.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532025/d15e6389-0899-d2b2-5a0c-97f26ee4a5ac.png)

しかし、現状Nextcloud 25はPHP8.2系以上に対応していません。
アップデートするのであればPHP8.0系またはPHP8.1系にアップデートしてください。
Nextcloud 26からPHP8.2系に対応する予定になっています。

すでにPHP8.2にアップデートし、PHPのバージョンが違う旨のメッセージが表示されている場合は設定ファイルをバックアップしてPHPパッケージを再インストールしてください。

:::note info
参考リンク
https://help.nextcloud.com/t/php8-2-with-nextcloud-25-0-2/151769
:::

## NextcloudではPHP8.0またはPHP8.1系を使いましょう

Nextcloudミドルウェア対応バージョン
Nextcloudで対応しているPHPのバージョンは以下となっています。
PHP8系はPHP7系よりもパフォーマンスがアップしており、安定性も高いのでPHP8系にアップデートすることお勧めします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15176/ad1d0a81-3cc9-1b21-e94b-d94e95acf587.png)

System requirements — Nextcloud latest Administration Manual latest documentation 
https://docs.nextcloud.com/server/latest/admin_manual/installation/system_requirements.html#server

## PHP7.4のセキュリティサポートは終了しています

PHP7.4のセキュリティサポートは以下の通り2022/11/22に終了しており、PHP7.4をご利用の方はPHP8系にアップデートを計画してください。

PHPのリリース日とサポート期限 - Qiita
https://qiita.com/bezeklik/items/72d1ff8393f66673e2bc

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15176/efdef767-9a58-5a71-56c7-c4f7f6fe3d3e.png)
【13日目】discord.jsで「お腹空いた」と言ったら美味しそうな食べ物の画像を出す

# はじめに
こんにちは、なりかくんです。
今回は、discord.jsを使ってテキストチャンネルで「お腹空いた」という文字列が含まれたメッセージが送られたら美味しそうな食べ物の画像を出すプログラムを作ってみようと思います。

# 美味しそうな食べ物の画像を出す仕組み
今回は、TenorのAPIを使って「foods」というワードでランダムに検索してヒットした画像を出すという超簡単な仕組みにしようと思います。
TenorのAPIを使ったプログラムは、11日目に投稿していますので是非そちらをお読みください。

https://qiita.com/narikakun/items/88908ebda4fe2da793d7

# お腹空いたと関連付けたワードを用意する
今回は、関連ワードが何個メッセージに含まれているかでお腹が空いているかを判断します。
以下のように私はワードを配列化して用意しました。
```js
const words = ["腹", "おなか", "空いた", "すいた", "減った", "へった", "ぺこぺこ", "すっかかん", "背中がくっついちゃう", "死ぬ", "やばい", "ぴえん"];
```
そして、以下のようなコードで何個含まれるかを確認します。なお、このコードが一番いい書き方では私は思っていません。
２個以上含まれた場合に、画像を送る処理をするプログラムになっています。
```js
let wordCount = 0;
for (const word of words) {
    if (message.content.includes(word)) wordCount++;
}
if (wordCount > 1) {
    // ここに処理を追加
}
```

# コード全体
これがコード全体になります。まあ、非常に簡単なプログラムですね。
```js:index.js
const request = require("request");
const { Client, GatewayIntentBits, IntentsBitField } = require('discord.js');
const { token } = require('./config.json');

const client = new Client({ intents: [GatewayIntentBits.Guilds, IntentsBitField.Flags.GuildMessages, IntentsBitField.Flags.MessageContent] });

client.on('ready', () => {
	console.log(`${client.user.tag}でログインしました。`);
});

client.login(token);

const words = ["腹", "おなか", "空いた", "すいた", "減った", "へった", "ぺこぺこ", "すっかかん", "背中がくっついちゃう", "死ぬ", "やばい", "ぴえん"];

client.on('messageCreate', async message => {
	try {
        if (message.author.bot) return;
        let wordCount = 0;
        for (const word of words) {
            if (message.content.includes(word)) wordCount++;
        }
        if (wordCount > 1) {
            let gifUrl = await getGif("foods");
            if (!gifUrl) return;
            message.reply({
                embeds: [{
                    image: {
                        url: gifUrl
                    }
                }]
            });
        }
	} catch (error) {
		console.error(error);
	}
});

function getGif(query) {
    return new Promise((resolve, reject) => {
        request({
            url: `https://tenor.googleapis.com/v2/search?q=${query}&key=<API Key>&random=true`,
            json: true
        }, function (error, response, body) {
            if (error) {
                reject(error);
            } else {
                if (body.results.length != 0) {
                    resolve(body.results[0].media_formats.gif.url);
                } else {
                    resolve(null);
                }
            }
        });
    });
}
```

# 実際に動かしてみる
試しに「お腹すいたぺこぺこ」と送ってみるとこのように飯テロをされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549951/4e1cddd4-910c-96f1-bedb-80bd723f5cb9.png)

以上です、素晴らしく需要のないプログラムですがぜひ何かの参考にしていただければと思います。
最後までお読みいただきありがとうございました。
Jotaiを使ってみて（Providerの要不要について）

こちらの記事は金属加工プラットフォームを開発・運用する[Catallaxy](https://catallaxy.me/)の[Catallaxy Advent Calendar 2022](https://qiita.com/advent-calendar/2022/catallaxy)の12/16の記事となります。

# 思ってたんとちゃう

この記事ではJotaiを使って実装してみてなんとなくこうした方がいいんじゃないかというのをまとめてみます。
なので単純にJotaiの使い方を知りたい人には向かないかも。

# Atomをexportしない

Atomをexportすると予期しないところから状態を操作されたり、値を取得されて使われるようになるので（この辺はこの記事で対応したやり方も含めて運用次第だとは思いますが）、カスタムフックから利用するようにして操作できる範囲を制限するようにしました。

## Atomを操作するカスタムフックを作る

要はGetter、SetterみたいなものでAtomを操作するカスタムフックを作ります。

```ts
// exportしない
const foo = atom<number>(0);

// 取得するだけのカスタムフック
export const useGetFoo = () => {
  const val = useAtomValue(foo);
  return { val };
};

// インクリメントするだけのカスタムフック
export const useIncrementFoo = () => {
  const [val, setVal] = useAtom(foo);
  setVal(val + 1);
};

```

こうすると少なくとも不用意にAtomを操作されることは無くなります。
操作されるスコープ自体はシステム設計とかルールによってしまいますが、自由に操作されるよりはましになります。

# Providerについて

Atomを操作されるスコープを制限したいなぁと思って調べていたら`Provider`の`scope`にたどり着いたのですが、どうも想定とは違うようでライブラリ内でJotaiを使う場合にライブラリを使う側とバッティングしないようにしたりできるよ！みたいな事がGitHubのDiscussionに書かれていました。
https://github.com/pmndrs/jotai/discussions/384#discussioncomment-534100
> A minor correction: The scope is for a different purpose. Even without scope, you can use multiple providers to provide different values for different sub trees (even nested).

うまく利用すると解決できるのかもしれませんが設計が思いついていません。。

その他のコメントによるとデバッグ目的での利用が考えられるとのこと。
> You need Provider to see all values in the provider for debugging purpose.

あとは同じAtomを持つコンポーネントを複数使う場合はProviderを使う必要があるそうです。

```tsx
<Provider>
  <Foo />
</Provider>

<Provider>
  <Foo />
</Provider>
```

これ以外のケースでは特にProviderを定義しなくてもいいようになっているようです。

# 最後に

まだ使い始めでわからないことが多いですがJotaiいいですね！
クラウド・データカタログのここがすごい、5選！！

この記事は インフォマティカ [Advent Calender 2022 Day 17](https://qiita.com/advent-calendar/2022/informatica)の記事として書かれています。

新しくリリースされたデータカタログとデータガバナンスを融合したクラウド版の新サービスである、[CDGC (Cloud Data Governance & Catalog)](https://www.informatica.com/jp/products/data-governance/cloud-data-governance-and-catalog.html) <※以下「CDGC」と記載> のすごい所、5選をお届けします！

## 1. テクニカルとビジネスのメタデータ統合がすごい！！
旧来のオンプレ版では、データカタログ(テクニカル・メタデータ)とデータガバナンス(ビジネス・メタデータ)は別製品として提供していましたが、CDGCでは統合され、インターフェースも一新されました。テクニカルユーザーだけでなく、ビジネスユーザーにも活用いただける基盤となっています。
![chrome_MubnO6O560.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110338/ec37b0c8-88d8-e7bd-980a-a64a38d4015b.png)

テーブルやカラムのテクニカル情報を見ながら、それらに対する利用ポリシーやデータ品質ルールなどのガバナンス情報を確認して、「使えるデータ」なのか、「使っても良いデータ」なのかを総合的に判断することができます。
![chrome_3OQ1ojV3AD.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110338/ea3b7aa3-2ea3-75cf-9865-a04a1659a47a.png)


## 2. クラウド・ネイティブがすごい！！
クラウド版となった事で、インストールやバージョンアップの作業、日々のパフォーマンス監視や格納データ量に対するメンテナンス作業などの煩わしい作業から開放されます。またハードウェアの準備が全く不要なオプションなどもあり、オンプレ版で必要であった商用データベースなどの準備も不要となっています。

さあ、メタデータを活用する事に集中しましょう

## 3. リネージュのオーバーレイがすごい！！
インフォマティカのデータカタログは元々、リネージュ(データの流通経路の見える化)に力を入れて来ました。様々なETL製品、BI、スクリプト等からデータのリネージュを自動取得することができます。
![KWC2PoACTa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110338/f494550f-bc8f-5c3e-8fcc-ee76d25d9ccf.png)



さらにデータガバナンス機能と統合することで、例えば、ポリシーやプロセス、データ品質、ビジネス用語をリネージュ上でオーバーレイ(重ねて表示)する事ができ、メタデータの理解が大幅に効率化されます。
![chrome_G59mCWA1Ku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110338/c962a0cf-a5bc-bc68-5ca0-63e5bbeeacb8.png)


## 4. データ品質の管理がすごい！！
やっと見つけたデータが蓋を開けてみると、不正確だったり、足りなかったり、汚れているなどして、期待通りには分析に使えずと言うのは、データ分析あるあるです。
CDGCでは、そんな状況を打破するために、品質データ管理機能との連携をさらに強化しています。

データ品質管理サービスである [Cloud Data Quality](https://www.informatica.com/jp/products/data-quality/cloud-data-quality-radar.html)との連携により、品質スコアの見える化を行います。
![chrome_Ih3Xab9JFS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110338/16b7f2ab-62d4-8407-da65-1ada45aea4be.png)


また品質の履歴について、時系列のグラフで可視化する事で、いつから問題が発生したかを正確に把握できます。これにより原因分析を加速させる事ができます。
![eCZuxAbF26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110338/2e164ad1-7f4d-1202-82f8-9908d9283963.png)


## 5. AIがすごい！！

AIデータモデルのメタデータ管理機能が追加されました。
AIモデルのアーキテクチャやライブラリ、ルール、IN/OUTなどと言った、AI/ML利用で有用なメタデータを管理することができます。
![chrome_OrYicUBDPO.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110338/a27fcd0c-6ef1-089e-75c9-91d041322c1e.png)

他方で、データガバナンスのメタデータ管理作業自体を手動で行う事は、骨が折れる作業となります。
担当者はサービスを整備するのに手が掛かり、本来的に注力したい分析支援やガバナンスの強化へ割く時間が少なくなってしまいます。
CDGCでは、CLAIREと呼ばれるAIエンジンを備えており、様々な場面でその恩恵に受ける事ができます。

例えば、自然言語解析による品質ルール作成ができるようになっています。
「It should be between 1 and 5」と言う英語の文章を解析して、４つのパターンのルールが自動生成されるので、それを選択するだけで、ルールの設定が完了します。
![chrome_PthXgYBjg9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110338/ab9c1e17-39e0-ceb1-a839-a6896ced79db.png)

# 最後に
この記事では、データカタログとデータガバナンスを融合したクラウドソリューション CDGC (Cloud Data Governance & Catalog)について、すごい所の5選を解説しました。今後も様々な拡張が予定されているので、ご期待ください！！

CoE部
嶋田真人(Max)
[OCI]Autonomous Database：ログインに失敗しているDBユーザーのクライアント情報を確認する方法

# はじめに

Autonomous DatabaseのDBユーザーのパスワードを変更後、クライアント（アプリケーション）側のパスワード変更漏れでDBユーザーがロックされてしまう場合に、原因となっているクライアントを特定する方法です。

# 確認方法
Autonomous Databaseでは、デフォルトでユーザーのログイン失敗が監査情報として取得されているため、UNIFIED_AUDIT_TRAILビューを使用して、ログインに失敗しているユーザーのクライアント情報を確認することができます。

こちらのSQLの「DBユーザー名」の部分をロックされてしまったDBユーザー名に変更して実行することで、ログインに失敗しているユーザーのクライアント情報を確認できます。

```sql
set linesize 100
col userhost for a15
col os_username for a15
col client_program_name a30
SELECT
userhost, -- クライアントアプリケーションのホスト名
os_username, -- クライアントアプリケーションを実行しているOSユーザー名
client_program_name, -- クライアントプログラム名
authentication_type -- クライアントの認証情報
FROM unified_audit_trail
WHERE dbusername = 'DBユーザー名'
AND unified_audit_policies = 'ORA_LOGON_FAILURES';
```
DBユーザー名にADMINを指定して実行してみます。
```
SQL> set linesize 100
SQL> col userhost for a15
SQL> col os_username for a15
SQL> col client_program_name a30
SQL> SELECT
  2  userhost,
  3  os_username,
  4  client_program_name,
  5  authentication_type
  6  FROM unified_audit_trail
  7  WHERE dbusername = 'ADMIN'
  8  AND unified_audit_policies = 'ORA_LOGON_FAILURES';

USERHOST	    OS_USERNAME	    CLIENT_PROGRAM_NAME
--------------- --------------- ------------------------------------------------
AUTHENTICATION_TYPE
----------------------------------------------------------------------------------------------------
apserver1       opc		        sqlplus@apserver1 (TNS V1-V3)
(TYPE=(DATABASE));(CLIENT ADDRESS=((PROTOCOL=tcps)(HOST=xxx.xxx.xxx.xxx)(PORT=34350)));


SQL>
```
こちらの例では、
IPアドレスxxx.xxx.xxx.xxxを持つapserver1という名前のホスト上のOSユーザーopcが実行しているSQL*Plusからの接続
でログインが失敗していることがわかりました。

# まとめ
UNIFIED_AUDIT_TRAILビューを使用して、Autonomous Databaseへのログインに失敗しているDBユーザーのクライアント情報を確認することができました。めでたし、めでたし。

# 参考情報
・[Autonomous Databaseの監査](https://docs.oracle.com/cd//E83857_01/paas/autonomous-database/adbsa/adb-audit.html#GUID-76742693-33D2-46D3-8B13-CC838818B036)
・[UNIFIED_AUDIT_TRAIL](https://docs.oracle.com/cd/F19136_01/refrn/UNIFIED_AUDIT_TRAIL.html#GUID-B7CE1C02-2FD4-47D6-80AA-CF74A60CDD1D)
Batch to add / remove environment variables by Vercel CLI

Not sure why [Vercel CLI](https://vercel.com/docs/cli) 28.9.0 has no way to import whole `.env` file or all environment variables in one project or environment. Write a simple bash script to achieve it:

(Tested on macOS zsh)

## Check current environment variables and backup

```zsh
# List up all current environment variables
vercel env ls

# Backup
vercel env pull .env.production.bak --environment production
vercel env pull .env.development.bak --environment development
vercel env pull .env.preview.bak --environment preview
```

## Add environment variables

```zsh
cat .env.production | sed 's/=/ /' | xargs -n 2 bash -c 'echo -n $1 | vercel env add $0 production'
cat .env.staging | sed 's/=/ /' | xargs -n 2 bash -c 'echo -n $1 | vercel env add $0 development'
cat .env.staging | sed 's/=/ /' | xargs -n 2 bash -c 'echo -n $1 | vercel env add $0 preview'
```

## Remove all Vercel environment variables

```zsh
vercel env ls | sed 's/=/ /' | awk 'NR>2 {print $1, $3}' | xargs -n 2 bash -c 'vercel env rm $0 ${1,,} -y'
```
【PHP8.1~】列挙型(enum)を使ってセレクトボックスを作ってみる

## 概要

php8.1から列挙型(enum)が使える様になりましたね。

今まで定数ファイルに書いていたような事をenumを使って良い感じにしてみます。


## 実践

ステータスという名目のセレクトボックスを作ります。

- 要件
    - 選択肢は「未確定」,「完了」,「キャンセル」
    - optionのvalueにはキーが入る様に
    - optionのlabelには日本語のステータス名



### コード

```php:enum
namespace App\Enums;

enum Statuses: string implements Label
{
    case PENDING = 'pending';
    case COMPLETE = 'complete';
    case CANCEL = 'cancel';

    public function labelName(): string
    {
        return match ($this) {
            self::PENDING => '未確定',
            self::COMPLETE => '完了',
            self::CANCEL => 'キャンセル'
        };
    }
}
```

```php:interface
namespace App\Enums;

interface Label
{
    public function labelName(): string;
}
```


```html:blade
<select name="status">
    @foreach(\App\Enums\Statuses::cases() as $status)
        <option label="{{ $status->labelName() }}" value="{{ $status->value }}"></option>
    @endforeach
</select>
```

### 生成されるHTML


```html
<select name="status">
    <option label="未確定" value="pending"></option>
    <option label="完了" value="complete"></option>
    <option label="キャンセル" value="cancel"></option>
</select>
```


## 列挙型について

公式ドキュメント

https://www.php.net/manual/ja/language.types.enumerations.php

ルートモデルバインディングに列挙型で制御をかけられます

https://readouble.com/laravel/9.x/ja/releases.html#implicit-route-bindings-with-enums


指定した属性を列挙型でキャストできます。

https://readouble.com/laravel/9.x/ja/eloquent-mutators.html#enum-casting
HTML×Djangoの字下げ（インデント）、改行について

# 1. HTMLやDjangoの字下げ、改行について

単一の言語（ここではHTML、CSSを言語と定義）だけでサイトを作ることはほとんどない。個人的には3つ、ないしは4つは使うと考えている。（もっと壮大なものを作るとなると更に。。。？）
それが集結するHTMLファイルはきちんと表示できるのはもちろん、書き方にも気を使いたい。私の中で字下げ、改行のモットーがあるのでそれを共有したい。

## HTML:タグはtabキーで入れ子の形に。
```html:bad.html
<div class="design">
<div>
いろいろな内容A
</div>
<div>
いろいろな内容B
</div>
<div>
```

複数同じタグを使われた場合、関係性が見えにくい。字下げを行い見やすくしよう。

```html:good.html
<div class="design">
    <div>
        いろいろな内容A
    </div>
    <div>
        いろいろな内容B
    </div>
<div>
```

## HTML:style属性は極力使わない。

```html:bad.html
<div style="
           background-size: contain;
           object-fit: cover;
           background-color: #000000;
           background-blend-mode: lighten;">
    <div>
        いろいろな内容A
    </div>
    <div>
        いろいろな内容B
    </div>
<div>
```
今回は改行しているが、divに他の属性をつけたり、デザインの更新を行う際にもとても使いにくい。デザインはCSSで記述したほうがいいだろう。

```html:good.html
<div class="design">
    <div>
        いろいろな内容A
    </div>
    <div>
        いろいろな内容B
    </div>
<div>
```
```css:good.css
design {
    background-size: contain;
    object-fit: cover;
    background-color: #000000;
    background-blend-mode: lighten;
}
```

## Django:テンプレートタグ{%%}は字下げは行わない。

```html:badDjango.html
<div>
    <span>タイトル</span><br>
        {% for user in userAll %}
            <form class="tag_box" action="{% url 'example' pk=user.pk %}" method="get">{% csrf_token %}
                <input class="tag_box" id="example1" name="example1" value="example1">
            </form>
        {% endfor %}
</div>
```

もしテンプレートタグ{%%}がなかったら、二回字下げを行ったことになると考えればわかりやすいかもしれない。

```html:goodDjango.html
<div>
    <span>タイトル</span><br>
    {% for user in userAll %}
        <form class="tag_box" action="{% url 'example' pk=user.pk %}" method="get">{% csrf_token %}
            <input class="tag_box" id="example1" name="example1" value="example1">
        </form>
    {% endfor %}
</div>
```
気が付いたら随時更新予定


# 2.参考URL
https://docs.djangoproject.com/ja/4.1/ref/templates/language/#templates

https://docs.djangoproject.com/ja/4.1/ref/templates/language/#tags
配列の要素の重複値を取り除く(PHP)

PHPで配列の要素の重複値を取り除く処理です
PHPのarray_unique関数を使います

```PHP
<?php
    $array1 = ['cat','dog','cat','rabbit','bear'];
    // 重複値を取り除くため、array_unique関数を使う。
    $array2 = array_unique($array1);
    // preタグで囲んでprint_r関数の出力結果を見やすくする。
    echo "<pre>";
    print_r($array2);
    echo "</pre>";
?>
```

以上の方法を使うとよいでしょう。
preタグは配列の出力結果を見やすくするために使用します。
Understand Account and Contact Relationships

+ [Trailhead関係　個別の課題別](https://qiita.com/Keiji_otsubo/items/56eb7a90bb72138ece1f)


[Understand Account and Contact Relationships](https://trailhead.salesforce.com/content/learn/modules/accounts_contacts_lightning_experience/understand-account-and-contact-relationships-lightning)

# We cant find the related accounts related lists to the contact page layout 

何か案外多い質問です。独立させます。

+ [We cant find the related accounts related lists to the contact page layout](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000LI7zj)
+ [I get an error message](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000MguWF)

:::note
Use page layouts for Contact objects instead of Accounts.


From Setup, enter Contacts in the Quick Find box, then select Page Layouts.
Next to the appropriate page layout, click Edit.
Select Related Lists.
Drag Related Accounts onto the page layout. Because the Related  Accounts related list automatically includes all direct accounts and related accounts.


https://developer.salesforce.com/forums/?id=9060G0000005XCLQA2
:::

Contactのページレイアウトに追加しないといけない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/090544a6-42a1-7709-7247-baeae95120db.png)

+ [Location of the Account Contact Relationship layout](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000Mgh7V)
Railsでメモ化を使ってパフォーマンスを向上させる

# TL;DR

Railsでメモ化というテクニックを使えば一度のリクエストに対して同じ処理が複数走る箇所のパフォーマンスを改善できます。
```ruby
@hoge ||= #重い処理 
```
このようにインスタンス変数の自己代入を使ってメモ化すれば、2回目以降の呼び出しはインスタンス変数に格納された値を使ってその後の処理を実行できます。

# 解決したい課題

次のようなProductモデルを考えてみます。
```ruby:product.rb
# == Schema Information
#  id   :bigint           not null, primary key
#  name :string(255)      not null
#
class Product < ApplicationRecord
    has_many :shops, through: :malls
    has_many :malls

    def has_shops?
      # 内容は省略
      # 要件を満たしたshopかどうかを判定して真偽値を返す
    end
end
```

こちらの`Product`に対してnameの検索を行うような`Searchable`モジュールの`search`メソッドを考えてみます。

```ruby:product_filterable.rb
module ProductFilterable
    def filter(query)
         target_products = Product.all.inlcludes(malls: :shops).select(&:has_shop?)
         target_products.select { |product| product.name == "query" }
    end
end
```

対象となる商品(`products`)から`name`が文字列`query`と一致するものを抽出するようなメソッドです。
ただし、検索対象として予め要件で決められた`shop`をアソシエーションで持っている`product`から抽出したいです。
（本番環境で`Product.all`を使うとメモリを圧迫する可能性がありますが、今回はサンプルとしています）


# ２回目の呼び出し以降も同じ結果を返すならば無駄な処理が走る

このコードの問題として、検索の度に引数として渡されたすべての`Product`に対して`has_shop?`という判定がされていまいます。
もしも`Product`レコードが頻繁に追加されることがなく、抽出元となる対象がかわらないのであれば、　`filter`の実行の度に全件に対しての判定処理は無駄になってしまいます。


# メモ化を使って初回の実行結果をキャッシュする

そんなときに使えるのがメモ化のテクニックです。

## メモ化とは

メモ化をWikipediaで調べると下記のような説明があります。

> メモ化（英: memoization）とは、プログラムの高速化のための最適化技法の一種であり、サブルーチン呼び出しの結果を後で再利用するために保持し、そのサブルーチン（関数）の呼び出し毎の再計算を防ぐ手法である。メモ化は構文解析などでも使われる（必ずしも高速化のためだけとは限らない）。キャッシュはより広範な用語であり、メモ化はキャッシュの限定的な形態を指す用語である。

https://ja.wikipedia.org/wiki/%E3%83%A1%E3%83%A2%E5%8C%96

    今回のケースで言うと、メモ化とは同じ処理を走らせる場合に、1回目の処理で返したレスポンスをキャッシュとして残しておくことで2度目以降の処理が走らないようにしてパフォーマンスを向上させるテクニックだと言えます。


## メモ化を使ったパフォーマンス改善

実際に上記のコードは下記のように書き換えることができます。


```ruby
def fitler(query)
     @target_products ||= Product.all.inlcludes(malls: :shops).select(&:has_shop?)
     @target_products.select { |product| product.name == "query" }
end
```

# メモ化の効果 

こうすることで、最初の実行時のみ
```ruby
Product.all.inlcludes(:shop).select(&:has_shop?)
```
こちらの処理が呼ばれてインスタンス変数`@target_products`に格納されます。二度目以降はインスタンス変数がそのまま使われて`select`の処理が実行されます。

ちなみに、`||=`という書き方はRubyの自己代入という仕組みで「左辺が偽か未定義ならば右辺を代入する」という意味です。

https://docs.ruby-lang.org/ja/latest/doc/spec=2foperator.html#selfassign

# 参考

https://qiita.com/kt215prg/items/3c0fd89468dcfe6075df
Azure Administrator(AZ-104)：Azure管理者向けの前提条件「Azure CLIとスクリプト」

Microsoft Learnを用いたMicrosoft Azure Administrator(AZ-104)の試験内容をまとめる投稿です。
この投稿ではMicrosoft Learnで学ぶ内容をできるだけ簡潔にまとめております。
Azureの勉強を一から始め、AZ-900取得後の資格としてAZ-104取得を目指している方の参考になれば幸いです。
※AZ-900の内容は本投稿では省略しております。再度復習したい方は下記をご参照ください。
[Azure Fundamentals(AZ-900)：試験合格に向けた学習内容のまとめ](https://qiita.com/fsd-jume/items/e4ec76959adb547ac5e8)

## 今回の学習内容
**◆「Azure CLIとスクリプト」**
◇対応するMicrosoft Learn ラーニングパス
[AZ-104:Azure 管理者向けの前提条件](https://learn.microsoft.com/ja-jp/training/paths/az-104-administrator-prerequisites/)
◇対応するモジュール
[Azure CLI とは](https://learn.microsoft.com/ja-jp/training/modules/control-azure-services-with-cli/2-what-is-the-azure-cli)

※前回の投稿は下記から確認できます。
[Azure Administrator(AZ-104)：Azure管理者向けの前提条件「PowerShellスクリプト(パラメーター)」](https://qiita.com/fsd-jume/items/dba1485f0334610aa54a)

## Azure CLIとスクリプト
### Azure CLI
・AzureリソースをCLIで管理できるツール
・使用可能な環境
　-Linux、Mac、WindowsのローカルPC
　-Azure Cloud Shellを介したブラウザ
・使用可能な方法
　-対話形式：シェル(Windows→cmd.exe、Linux/macOS→Bash)のプロンプトでコマンドを実行
　-スクリプト形式：複数のCLIコマンドを1つのシェルスクリプトにまとめて、スクリプトを実行

**◆Azure CLIでスクリプトを使用する場合**
・スクリプトの実行に使用するシェル(環境)で差異がある
 例)BashシェルとPowerShellでそれぞれ変数を設定する場合
 　-Bashシェル：変数名でそのまま値を指定
 　-PowerShell：変数名の前に`$`をつけて値を指定
```bash:Bash
variable="value"
```
```powershell:PowerShell
$variable="value"
```
## 次の学習内容
**◆Azure CLIによるAzureリソースの作成**

https://qiita.com/fsd-jume/items/52615a782a30548eedd3

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

https://qiita.com/fsd-jume/items/6eda24728c9c03c36bc3
Elixir Phoenix の REST API でバイナリデータを受信する

## はじめに

LiveView が盛り上がっている中 REST API かよ、という感じですが、画像 AI を提供するエンドポイントを REST API として提供する形態を想定しています

## 参考にした Elixir Forum の質問

https://elixirforum.com/t/download-binary-data-in-post-request-to-a-file/19316/4

## JSON データで受信する

特に何も考えずに Phoenix で REST API を構築すると、 Controller でリクエストを受ける実装は以下のようになります

```elixir
defmodule ApiWeb.PredictionController do
  use ApiWeb, :controller

  action_fallback ApiWeb.FallbackController

  # param に Map でリクエストが入ってくる
  def index(conn, param) do
    ...
  end

...
end
```

例えば curl で以下のように送信したとします

```bash
curl -XPOST http://localhost:4000/api \
  --data "{\"xxx\":\"aaa\"}" \
  --header "Content-Type:application/json"
```

すると JSON がデコードされ、 `param` には以下のような Map が入ってきます

```elixir
%{"xxx" => "aaa"}
```

ここに JSON ではなくバイナリデータを送ってみます

```bash
curl -XPOST http://localhost:4000/api \
  --data-binary @sample.jpg \
  --header "Content-Type:image/jpeg"
```

そうするとデコードできないので、空の Map が入ってくるようになります

```elixir
%{}
```

JSON 形式で画像データを送信したい場合、画像を文字列に変換する必要があります

というわけで、よく BASE64 が使用されます

https://ja.wikipedia.org/wiki/Base64

curl で BASE64 エンコードした画像を以下のように送信します

```bash
echo {\"image\":\"$(base64 -i sample.jpg)\"} | \
  curl -XPOST \
    --data @- \
    --header "Content-Type:application/json"
```

ちょっと解説

- `base64 -i <画像ファイルパス>` で画像を base64 文字列に変換できます

- `$(<シェルコマンド>)` でシェルコマンドの実行結果を値としてシェルコマンドの中に展開します

- `echo {\"image\":\"$(base64 -i sample.jpg)\"}` で　`{"image":"<画像のBASE64変換結果>"}` を標準出力に渡します（`\` はエスケープ文字で、 `"` を出力するために使っています）

- `|` (パイプ)で標準出力を次のシェルコマンドに渡します （Elixir でいうところの `|>`）

- `@-` で `|` で渡ってきた値を受けます（つまり、　`{"image":"<画像のBASE64変換結果>"}` が JSON として送信される）

この場合、　 Controller では以下の様にパターンマッチして受け取り、 `Base.base64` でデコードすることができます

```elixir
...
  def index(conn, %{"image" => base64}) do
    binary = Base.decode64!(base64)
    ...
  end
...
```

この実装で確かに画像データを REST API で受けて処理することは可能です

しかし、いちいち BASE64 エンコードとデコードを挟むのは無駄ですよね

というわけで本題です

バイナリデータのまま受信します

## バイナリデータで受信する

答えは以下の様な実装になります

curl 送信

```bash
curl -XPOST http://localhost:4000/api \
  --data-binary @sample.jpg \
  --header "Content-Type:image/jpeg"
```

Phoenix 受信

```elixir
...
  def index(conn, %{}) do
    {:ok, binary, _} = Plug.Conn.read_body(conn)
    ...
  end
...
```

`Plug.Conn.read_body` で接続 `conn` を読み込むと、そのままバイナリデータを取得することができます

## まとめ

すっごく単純な答えでしたが、普通は JSON しか送受信しないのでなかなか辿り着きませんでした
PHP Laravel データがViewに含まれているかテストする

:::note info
この記事は[miriwoお一人様 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/miriwo)の16日目の記事です
:::

# 概要

- Viewに指定したキーと値が含まれることをテストする方法をまとめる

# 方法

- レスポンスに対してassertViewHasを使ってチェックする。

    ```php
    $response->assertViewHas(キー名, 値);
    ```

- 例えばキー名がidで値が10の情報がViewに含まれていることをチェックしたい場合下記の様に記載する。

    ```php
    $response->assertViewHas('id', 10);
    ```
FigmaのAuto Layoutでハマった話

# Auto Layoutとは？
Auto LayoutとはGridレイアウトが再現できる機能です。
オブジェクトを等間隔に並べ、崩れにくいデザインを作ることができます。
決められた間隔・方向でレイアウトをとにかくゼッタイ崩さないマン。

そんなAuto Layout、覚えればすごく便利なのですが、その特徴が故に初めてFigmaを触った人は訳わかんないことになりがちだと思っています。

この記事が少しでも参考になれば幸いです。

# 1. フレームの伸縮と一緒にオブジェクトが伸縮しない
![a836eba7-afb5-4bfb-80a5-8565a120b65e.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1486484/11454d0a-3c0b-b195-88fd-5626fab080f8.gif)
この現象の原因は、**オブジェクトの横幅が固定されている**ことです。
赤枠の「Fixed」となっているところを「Fill」に変えることで、フレームに合わせて伸縮するようになります！
![30fc1333-fe2c-418a-8a13-372fa090eae3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1486484/f2f95d07-ce74-cd41-e884-32501f33a2d6.jpeg)
![dad670fd-d7b3-4a77-a5a4-cbce36153895.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1486484/dae485c4-de50-da8b-f4e1-6906a426dad9.gif)

余談ですが、この選択肢には`Fixed` `Hug` `Fill`の3つがあり、それぞれ以下のような役割をします。

- `Fixed`：幅を固定する
- `Hug`：子オブジェクトの幅に合わせる
- `Fill`：親オブジェクトの幅に合わせる
イメージがつきにくいのは「Hug」でしょうか。

HugはAuto Layoutを適応しているフレームに対して設定するものです。
![fb60cd89-6850-4726-89ee-5d109d6aeda0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1486484/9742c4fb-a907-a66d-9ae3-2a59540908a3.gif)
このように、一番幅のある子オブジェクトに合わせてフレームの幅が伸縮します。

# 2. 並べたくないオブジェクトも並んでしまう
例えばカレンダーUIを作成するとして、今日の日にちに丸のバックグラウンドを付けたい時、Auto Layoutだとこうなります。
![294ab408-c793-4dd5-92d1-08c8bd48fcfe.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1486484/24e7874a-6613-fc22-0196-87e2355e097d.gif)
先程も書いたように、Auto Layoutはとにかくゼッタイ崩さないマンなので当たり前っちゃ当たり前ですね😂

この要望を叶えたいときは`Absolute`機能が使えます。
![6a73d475-efba-4bb5-8457-1017713ad102.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1486484/bfe58507-d39a-63dc-cb4a-ad88e5481ef0.gif)
この機能は2022年5月に追加された新機能です！

表現できる幅が広がって、デザインがしやすくなりました🥰

# 3. 左右に寄せたい
このようなデザインの時、「Auto Layoutだと文字の長さによって間隔が違うから使えない…」と思ってAuto Layoutを使っていない方もいるんじゃないでしょうか。
![3680e806-f56d-456d-881c-de7c9edbe55c.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1486484/8fb9010b-4bef-94c8-b947-7b4d81a88286.jpeg)
実はそんなことはなくて、Auto Layoutの三点ドットの中にある「space mode」を「Space between」にすることでちゃんと左右に寄せることができます。
![ef0179ea-9e22-4ae6-bbd7-af2c1a87f289.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1486484/322cf499-23e2-76a2-4d8f-69c66c0ed562.jpeg)

# 最後に
初めはAuto Layoutの概念に慣れるのに時間がかかると思いますが、使いこなせるとデザインの幅がぐっと広がります！

こんな機能便利だよ〜などありましたらぜひ教えて下さい😊
これからレジのアプリやEコマースサイトを作ろうとしている若造に教えてやる

間違っても変数名(フィールド名)に `price` なんて名前をつけるんじゃない。

フィールド名は `price_tax_excluded` と `price_tax_included` としろ。両方作れ。
`tax_excluded_price` でもいいし `price_without_tax` でもいいが、
`price` なんて名前はつけるな。早死にするぞ。

そして価格の型は `Integer` にするんじゃない。`Float` や `Double` もダメだ。**`Decimal` にしろ**。絶対だ。
JS のことは知らんけど多分ライブラリがあるからうまくやれ。そして Json でやりとりする時は文字列で送って Decimal に復号しろ。

会計システムを税別主体でいくか税込主体で行くか決めたら、計算で求めるもう片方は**小数点以下も保持して最後まで失わない**ようにしろ。

小数点以下は、お客さんには見せる必要は無いし、実際に請求する際は整数にするため最後の最後は四捨五入や切り捨てをすることもあるが間違っても**各商品(小計)単位で税の端数を切り捨てたりするんじゃない**。違法だ。

https://www.mof.go.jp/tax_policy/summary/consumption/a_001.htm#18

セブンイレブンで税込100円のものを3買って301円になるのは何も間違っちゃいない。正しい計算だ。笑い飛ばす前に自分を反省しろ。
ただあれは説明が不足していただけだ。お客様に誤解を与えないように徹底的に説明しろ。

あれは税別主体で動作しているレジで、税別で 93円 の商品が、税込だと税率 1.08 で 100円に見える。内部的には 100.44円だ。 3つ買うと 301.32 になって301円の請求になる。世の中のモダンなレジは、このように小数点以下の金額も保持している。端数の .44 円はお客様には見せなくとも絶対に失うな。**最後まで保持してDBにもそのまま入れろ**。
お客様に 税込 100.44 円 と表示したほうが親切かもしれないがサービスに応じてうまくやれ。最近のコンビニやスーパーでは表示してるのをよく見る。


税率も商品ごとに格納するようにしろ。ついでに、税率とは別に、軽減税率対象かの真偽値も入れておくと、税率が変わった後に過去のデータを精査する時、困らなくていい。情報はなるべく残せ。さらに言うと、通貨フィールドも作っておくと海外展開をする時にいい。

YAGNIポリシーというのは常識だが、DBに関しては一歩先を考えておいたほうがいいこともある。覚えとけ。

**お客様や使用者の操作は全部ログに残せ**。どのバーコードをスキャンしたか、どのボタンを押したか、逐一残せ。ログがない分お前の睡眠時間が減ると思え。

ページを作る時はわかりやすさを最優先にしろ。ページ数は多くなってもいい。使用者に、**今すべきことに集中させろ**。コンビニのセルフレジを使い倒して勉強しろ。

ディスカウント系の処理を作る時は、税込み商品金額に対する**支払いの代替**なのか、税別金額に対する**割引処理**にするのか、サービスのマネージャーや会社の会計部門と一緒にしっかり設計しろ。
最終的にはおそらく両方が必要になる。

会計に対しての支払い方法は1対多の関係とし、複数登録できるようにしろ。会計の一部を商品券を支払って、残りは現金で、おつりも出す。なんてことはザラにある。気を抜くな。

その場合、会計に軽減税率と通常税率が混じっている場合、商品券の使用はどちらに対して行われるのか、どのような処理になるのが理想か会計部門としっかり相談しろ。

あとは商品券に関して、釣りが出るものと釣りが出ないものがある。釣りが出ないものは、使われなかった金額分を最終的に計上しなければならないため、商品券の額面、種別、本来釣りとして扱わなければいけなかった金額などを細かく記録しろ。

わかったか若造。これから**インボイス制度**という地獄が待っている。インボイス制度を乗り切って長生きするためにこれは守っとけ。じゃあな。
フロントエンドエンジニアができるサイトパフォーマンス改善調査法

ページパフォーマンスを最適化するためにフロント側でできること紹介します！
最適化後の効果は様々な要因によって上下しますので、1つの案として考えていただければ幸いです。

:::note info
【こんな方にオススメ！】
・ページのパフォーマンスを上げたいけど、どうすればいいのか困っている方
・何をどう調査していいのかわからない方
:::

何か1つでもヒントになることがあれば大変嬉しいと思っています！

## 最適化すべきところを探る
どうすればサイトのパフォーマンスを下げているペインを見つられるのか。
私の実施した方法を紹介します。

### Chromeの開発者ツール
開発者ツールにはサイトパフォーマンスを知るためのさまざまなツールが用意されています。

たくさんありすぎて**何をどう使えばいいのか迷ってしまう**と思うので今回は3つのツールについて紹介します。

:::note ingo
それぞれの特徴とどんな値を計測しているのかを理解して根拠のある施策立案に結びつけていただければ幸いです。
:::

#### Lighthouse
サイトのパフォーマンスを計測するのに一番ポピュラーなツールです。

測定期間（Timespans）やスナップショットについては下記の記事で触れているのでよろしければご覧ください。

https://qiita.com/tanimoto-hikari/items/e781a4228c58f1fcd4d5

これまで使ってきている方も多いと思うので改めて説明はしません。
ただ、注意したいのはLighthouseの計測結果はPCのスペックに左右されることです。

https://www.zachleat.com/web/lighthouse-deception/

同一のPCで計測するのには問題ではありませんが、チームで数値を観測するときは語弊が生まれないように「Page Speed Insights」で計測することをオススメします。

https://pagespeed.web.dev/

#### performance
レコーディングを開始して、一度リロードすると画像のような表示が出ます。
画像は10秒間記録した時のパフォーマンスです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/47cea528-cb28-b788-3639-59bed102a3d1.png)
ここで確認すべきは「ネットワーク」と「メイン」のところだと考えています。

### ネットワーク
わかりやすいように先ほどの画像から拡大しています。
いつどのファイルがどれくらいの時間で呼び出されているのかを視覚的にわかるようなっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/0a951d55-61c5-8de4-266d-795fb4720c16.png)

さらに1つの項目に着目します。
グレーの線、薄い背景色と濃い背景色で構成されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/431e4a0d-57ca-6634-690c-0041ef6a9f03.png)
**おさえておきたいのは、「左の線、薄い四角、濃い四角、右の線が何を示しているか」です。**

|場所|何が原因で悪くなるか|
|--|--|
|左側の直線|サーバーとの接続、他に優先度の高い処理をする|
|薄い色の四角形|サーバーの処理時間やクライアントとサーバー間のネットワーク経路の状態など物理的な距離|
|濃い色の四角形|データのサイズが大きい|
|右側の直線|メインスレッドが他の処理をする|

参考にした記事

https://engineering.mercari.com/blog/entry/2018-12-12-090156/


#### Performance insights
Chromeのみで、Chromiumでは利用できません。
Chrome102から追加されたプレビュー機能です。

パフォーマンスパネルでは情報が多すぎることやよりパフォーマンス向上へのヒントをわかりやすいようにしたのが「Performance insights」です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/0b2485da-ec39-1dc3-acd0-271bf2a05844.png)
パネルを開いて「Measure page load」をクリックすると計測が開始されます。
表示される情報はパフォーマンスパネルと似ていますが、大きな違いは右側にある「Insights」と「Details」の項目です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/063ea60c-9982-ead7-294c-e62e829bede5.png)

### Insights
Insightsはどのタイミングで何が発生しているかを時系列で上から順番に表示してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/e6166c3c-16d7-8fc6-a966-d7a2414099e4.png)
Insightsの項目（画像のフォーカスが当たっているところ）をクリックするとDetailsのタブが開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/146612/b33587b8-96cf-ead4-c019-a07fc62cab79.png)
クリックしたタスクが表示されてFixのヒントを表示してくれます。

また、右上の歯車のアイコンを押すと「Performance insights」の表示を増減させられます。
状況に合わせて調整してください。

### まとめ
いろんな制約がある中で、ペインを見つけて改善するのは難しいことも多いと考えます。
細かい原因を改善しながら大きな改善を時折実施していくのがいいと考えています。

:::note alert
何か1つのものがパフォーマンスに影響していることもありますが、小さな問題の積み重ねによりパフォーマンスが下がっていることもあると考えているからです。
:::

それよりも**パフォーマンスが悪くなってどこを改善していいのかわからなくなる前に改善に着手できることが望ましい**と考えています。

今の状況を打開することが最優先ですが、それと同じぐらい**改善を継続的に続けていける仕組みを作る**ことも重要だと考えています。

引き続きサイトのパフォーマンス改善と継続的に改善活動ができる仕組みづくりも考えたいと思います。
M5Stack+ROSで4脚ロボットを作る


[こちら](https://qiita.com/nisshan_/items/939395f077b59ad4709b)を見てください。

(経緯： Qiitaの仕様でAdvent Calendarに記事を1対1でしか紐付けられないようです。お手数ですがクリックをお願いします。)