OmniAICreator/Qiita-1.07M huggingface dataset:
【CakePHP3】環境構築【新人がyoutube動画でPHP学習part1】

# はじめに、、、

僕は2023年3月にシステムエンジニアとして転職しました。
前職は金融機関でIT未経験、、、
転職前の半年間、職業訓練学校でHTML、CSS、JavaScript、jQueryを学習しました。
システム開発に関してはスキルも経験も無い状態です。

なので「先ずはお勉強から始めましょう」と上司からお達しがありました。
教材はチャンスラボ株式会社の公式YouTubeチャンネル。
https://www.youtube.com/@asterisk_cl_lab/videos

僕が動画を見ていてハマったところ、理解が難しかったところをまとめていきます。


今回は環境構築です。
参考にするのはチャンネルの第１回～第３回の動画です。

【＃１　Webシステムについて】
https://www.youtube.com/watch?v=Cco9S1ZDOa4

【＃２　XAMPPインストール】
https://www.youtube.com/watch?v=PDLEry8GxDA

【＃３　CakePHPインストール】
https://www.youtube.com/watch?v=pg6Ih-eIKXg


# 目次
１．はじめに
２．Webシステムについて
３．XAMPPインストール
４．CakePHPインストール
５．終わりに


# 【＃１　Webシステムについて】
第１回は環境構築とはあまり関係ありません。
しかし**３：２０～**「Webシステムの要素」で説明されている、リクエストとレスポンス、各サーバーの役割などは重要です。意識しておくと今後の学習で理解が進みやすくなるかも？？


# 【＃２　XAMPPインストール】
**３：２５～**
XAMPPのインストールですが、動画が2021.5.17にアップされたものなので、Webサイトに配置されているXAMPPのバージョンが当時と現在では異なります。
「More Downloads >>」をクリック、さらに「XAMPP　Windows」をクリックすると過去バージョンを選択できるページに飛べるので、そこから探してみましょう。
![スクリーンショット 2023-03-27 102356.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297208/1b1d74d6-1864-4dce-3292-3bbf695b5486.png)
![スクリーンショット 2023-03-27 102924.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297208/324173b5-0e07-1f10-0b9c-b9449896e332.png)
https://sourceforge.net/projects/xampp/files/XAMPP%20Windows/
↑このURLから7.4.18を探してもOK。

＊ちなみに僕は「7.4.18見つからないしXAMPP8.2.0じゃダメなん？」と考え最新版をインストール。CakePHP4.4もインストールして無理やり進めようとしました。
しかし動画内で扱われるCakePHP3とCakePHP4.xでは仕様の異なる部分があり、動画＃４、＃５あたりで思いっきり詰まってしまい、、、結局XAMPP7.4.18を入れ直しました。やはり初心者ですから素直に動画通り進めるべきですね。

# 【＃３　CakePHPインストール】
**４：５３～**
環境変数へパスを通します。
僕はここで初めて「環境変数」というものを知ったのですが、説明を聞いても調べても分かったような分からないような感覚、、、ただ、コマンドプロンプトをいろいろ触っていて感じたのですが、環境変数へパスを通すとコマンドを扱うのが楽になります。
とりあえず初心者はファイルパスがどうなっているか分かっていればいいはず！

**７：３５～**
PHP-intlを有効にします。
国際化モジュールってなんぞや？と思って公式ドキュメントを読んでみましたが、、、PHPをいろいろ触ってみた後に改めて読んだ方が理解できそうです。
https://www.php.net/manual/ja/intro.intl.php

ちなみにCakePHPインストールについて他の方の解説を見たとき、php.iniを編集する前に「php.ini_bak」などの名前でバックアップを取っておきましょうと勧めている方が何人かいましたので、僕も一応バックアップを取っておきました。
ファイルは　XAMPP/php/php.iniとなっているはずなので探してみてください。


# 終わりに、、、

動画内で手順が丁寧に説明されていたので、結構すんなり環境構築できました。
しかし用語などの説明は簡潔でしたので、分からない単語は自分で調べておきたいですね。

僕は今後、PHPの基本構文を学習する予定です。
というのも＃３動画の**１：５３～**から「PHP言語、HTML、CSS、JavaScriptをある程度理解していればCakePHPは簡単に利用できる」との説明がありますが、逆を言えばこれらをある程度理解してないと今後の内容が理解できません。
実際僕はPHPの基本構文をまだ学習しておらず、＃６以降も動画を見ても記述されたコードを読み解くことができませんでした。

とういことで次回の投稿はPHPを学習した後になるかと思います。
最後までご覧いただきありがとうございました！！
TypeScript 環境構築

## はじめに
TypeScriptの環境構築をしたので、メモ代わりに書いていきます。

## Node.jsのインストール
はじめに、TypeScriptを書いていく上で、必要になるNode.jsをインストールしていきます。
Node.jsを使用する理由は、JavaScriptやTypeScriptで作られたライブラリやコマンドなどを使うための、パッケージマネージャを備えている「npm」を使えるようにするためです。

私は、以下のページで、Node.jsの導入はv18のバージョンをインストールしました。
https://nodejs.org/ja

## 環境構築
まずは適当なディレクトリを作成します。今回は「test」というディレクトリの中に環境構築していきます。
まずは以下のコマンドでディレクトリ内で、npmを使用できるようにします。
```
npm init
```
以下が出力されれば成功です。
```
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help init` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
```
ここで以下のような出力がされて、「パッケージの名前を決めろ」とと言われます。
```
package name: (test) 
```
今回は以下のような名前にして、Enterを押します。
```
package name: (test) typescript-test
```
次に以下のような表示が出てきますが、全てEnterで大丈夫です。
```
package name: (test) typescript-test
version: (1.0.0) 
description: 
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC)
```
全てEnterを押すと以下のような出力があるので、Enterで大丈夫です。
```
About to write to /Users/fukushimaryouhei/Desktop/test/package.json:

{
  "name": "typescript-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this OK? (yes)
```
この時点で、Node.jsのプロジェクトが作成されるので、以下のコマンドでTypeScriptのコンパイラをインストールします。
```
$ npm install -D typescript

added 1 package, and audited 2 packages in 1s

found 0 vulnerabilities

```
インストールができたら、コンパイラのバージョンを確認します。
```
npx tsc -v
Version 5.0.2
```
次に、TypeScriptの詳細設定をします。
```
$ npx tsc --init --rootDir src --outDir lib --esModuleInterop --resolveJsonModule --lib es6,dom --module commonjs

Created a new tsconfig.json with:                                                                                       
                                                                                                                     TS 
  target: es2016
  module: commonjs
  lib: es6,dom
  outDir: lib
  rootDir: src
  strict: true
  esModuleInterop: true
  skipLibCheck: true
  forceConsistentCasingInFileNames: true


You can learn more at https://aka.ms/tsconfig

```
そうすると、ディレクトリ内にtsconfig.jsonファイルが作成されます。
ディレクトリ内が以下のようになっていると思います。
```
% ls
node_modules            package-lock.json       package.json            tsconfig.json
```
## 実際に動かしてみる
実際にTypeScriptを使って、出力をしてみましょう。
ルートディレクトリにsrcディレクトリを作成して、その中にtest.tsファイルを作成しましょう。
コードはこんな感じにします。
```:src/test.ts
const hello: string = "hello";
const world: string = "world";

console.log(`${hello} ${world}`);
```

コードを作成したので、コンパイルして実行します。
###### 1.コンパイル
```
npx tsc -p .
```
###### 2.実行
```
% node lib/test.js
hello world
```
hello worldが出力されました！
以上になります。
一週間でAWS認定クラウドプラクティショナー（CLF）をとってみた

# 背景
一週間の勉強でAWSクラウドプラクティショナー(CLF)を受験しました。
筆者にクラウドの知識はほとんどなく、当然AWSの実務経験もないです。
なにか一つくらい資格とっとくかーくらいの気持ちで受験しました。

# やったこと
## 最初の3日
[AWS認定資格試験テキスト AWS認定 クラウドプラクティショナー](www.amazon.co.jp/dp/4797397403)
定番らしいこの本を適当に一周読みました。
## 残りの4日
[Tech Stock](https://techstock.jp/)というサイト（有料）のCLF問題集をセクション50まで（約250問）を一周＆間違えたところを復習しました。
全部で76セクション（約400問）あるのですが、似たような問題が多いっぽいので全部は解かなくていいと判断しました。（単純に全部解く時間がなかった...）

# 試験概要
試験時間：90分
問題数：65問
受験料：11,000円

# 試験当日
自分はピアソンvueのテストセンターで受験しましたが、本人確認の身分証さえ忘れなければ大丈夫な感じでした。
時計も受験するPCに表示されてて不要でしたし。
試験時間は90分あるのですが、30分で見直し含めて終わったのですぐ退出しました。
結果は775点で合格でした。(合格ラインは700点なのでそんなに余裕じゃなかった)

# 感想
理解を問うというよりは暗記問題が多く、模擬問題を解きながら覚えればいける印象でした。
次はSAA頑張ります。
観点に軸を持つ

QAとしてプロダクトに向き合う上で「観点に軸を持つ」って大事なことだなぁと思います。

どういうことか？

平たく言うと、自分の担当するプロダクトの1機能から1仕様に至るまで、
1つの一貫した観点で向き合ってみるということです。

わかりづらいですねｗ実例を挙げてみましょう。

筆者にとってその1つの観点とは「リスク」であることに最近気づいたんです。
とある新機能があったとして、その機能には...もっというとその仕様にはどんなリスクがあるか？
という目線でプロダクトに向き合っているのかなと。
で、「リスク」という軸でもってプロダクトに実装される様々な機能を見てみると

・Aという機能にはあんなリスクがある
・Bという機能にはこんなリスクがある

という風に、自分の中でリスクの観点が増えていくように思ったんです。
その増えた観点を、別の機能に当てはめて考えたときに、他の人とは一味違った切り口での指摘ができるのではないかと。

なので、プロダクトに向き合う上で「観点に軸を持つ」ことって大事だなぁと思ったわけです。


と偉そうに語りましたが、これをやったから「いい指摘」ができたわけではないですｗ
ただ「いい指摘」ができた、と思えた理由を振り返ったら、こういうことだったんじゃないか？というお話です。

とある知り合いは、PJ内でも名の知れた「連打によるバグを見つける天才」だったらしいです。
彼曰く「俺はビルドが触れるようになったら、とにかく色んなところを連打してるよ」といってましたｗ
これもある意味、観点に軸を持ってプロダクトに向き合っている例かもしれません。
<br>


本稿をまとめるとこんな感じです
・プロダクトに対し、1つの観点で見てみよう
・その観点でプロダクトの様々な機能を見ているうちに、いずれその観点に特化してくる
・そうすることで、他人には見つけられないようなバグや欠陥が見つかるかもしれない


誰もが「すごい」と思うような指摘は、閃きのような、ある意味運ゲー要素もあるかと思います。
ですがそういう指摘をバシバシできる人は、実はこういう見えない部分の積み重ねによって、
その運を手繰り寄せてるのかもしれません。
【Flutter】gpt-3.5-turboの使用方法

# はじめに
FlutterでChatGPTのAPIを使う時、エンドポイントなど、GPT3（davinci）の時とは、若干異なっていて詰まったので、ここにまとめておきます。
同じ部分で詰まっている方の助けになれば嬉しいです！

# GPT3（davinci）の場合
```dart:api.dart
import "dart:convert";
import "package:flutter/material.dart";
import "package:http/http.dart" as http;
import "package:dart_openai/openai.dart";

String apiKey = "APIキーを入力";

class ApiServices {
  static String baseUrl = "https://api.openai.com/v1/completions";

  static Map<String, String> header = {
    "Content-Type": "application/json",
    "Authorization": "Bearer $apiKey"
  };

  static sendMessage(String? message) async {
    var res = await http.post(
      Uri.parse(baseUrl),
      headers: header,
      body: jsonEncode(
        {
          "model": "text-davinci-003",
          "prompt": "$message",
          "max_tokens": 1000,
          "temperature": 0,
          "top_p": 1,
          "frequency_penalty": 0.0,
          "presence_penalty": 0.0,
          "stop": ["Human:", "AI:"]
        },
      ),
    );

    if (res.statusCode == 200) {
      var data = jsonDecode(res.body.toString());

      // ここに必要なデータが格納されている
      var msg = data['choices'][0]["text"];
      // 文字化け対策
      return utf8.decode(msg.runes.toList());
    } else {
      print("Feiled to fetch data");
    }
  }
}

```

# ChatGPT（gpt-3.5-turbo）の場合
```dart:api.dart
import "dart:convert";
import "package:flutter/material.dart";
import "package:http/http.dart" as http;
import "package:dart_openai/openai.dart";

String apiKey = "APIキーを入力";

class ApiServices {
  static String baseUrl = "https://api.openai.com/v1/chat/completions";

  static Map<String, String> header = {
    "Content-Type": "application/json",
    "Authorization": "Bearer $apiKey"
  };

  static sendMessage(String? message) async {
    var res = await http.post(
      Uri.parse(baseUrl),
      headers: header,
      body: jsonEncode(
        {
          "model": "gpt-3.5-turbo",
          "messages": [
            {
              "role": "user",
              "content":
                      "$message",
            }
          ]
        },
      ),
    );

    if (res.statusCode == 200) {
      var data = jsonDecode(res.body.toString());

      // ここに返ってきた文字が格納されている
      var msg = data["choices"][0]["message"]['content'];

      // 文字化け対策
      return utf8.decode(msg.runes.toList());
    } else {
      print("Feiled to fetch data");
    }
  }
}
```
Vue3 CompositionAPIで子コンポーネントの関数を呼び出す

Vue3 CompositionAPIで親コンポーネントから子コンポーネントの関数を呼び出したい場合、子コンポーネントに `ref` を設定し、関数を呼び出す。

```Vue:ChildComponent.vue
<template>
  <div>
    <button @click="onButtonClick">Child Button</button>
  </div>
</template>

<script>
import { defineComponent } from 'vue';

export default defineComponent({
  name: 'ChildComponent',
  setup() {
    function onButtonClick() {
      console.log('Child button clicked');
    }

    return { onButtonClick };
  },
});
</script>
```
```Vue:ParentComponent.vue
<template>
  <div>
    <ChildComponent ref="childComponent" />
    <button @click="clickChildButton">Click Child Button</button>
  </div>
</template>

<script>
import { defineComponent, ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

export default defineComponent({
  name: 'ParentComponent',
  components: {
    ChildComponent,
  },
  setup() {
    const childComponent = ref(null);

    function clickChildButton() {
      // 子コンポーネントのメソッドを呼び出す
      childComponent.value.onButtonClick();
    }

    return {
      childComponent,
      clickChildButton,
    };
  },
});
</script>
```

本題はここから。
CompositionAPIを使う場合、より簡潔に書くために `setup` シンタックスシュガーを使いたいところ。しかしこの場合、各プロパティがデフォルトで公開されないため、 `defineExpose` 関数を使って公開してあげる必要がある。
親も子も `setup` を使った場合の例はこちら。

```Vue:ChildComponent.vue
<template>
  <div>
    <button @click="onButtonClick">Child Button</button>
  </div>
</template>

<script setup lang="ts">
const onButtonClick = () => {
  console.log('Child button clicked');
}
defineExpose({ onButtonClick })
</script>
```

```Vue:ParentComponent.vue
<template>
  <div>
    <ChildComponent ref="childComponent" />
    <button @click="clickChildButton">Click Child Button</button>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const childComponent = ref(null);

const clickChildButton = () => {
  // 子コンポーネントのメソッドを呼び出す
  childComponent.value.onButtonClick();
}
</script>
```

ただし今更ではあるが、親から子の関数を直接呼び出す方法はVueのベストプラクティスに反するため、避けられる場合は避けるべき。データフローとイベントを適切に使って親子間のコミュニケーションを行うことが推奨される。
seed.rbの文字列を改行する方法

# はじめに
　以前ポートフォリオを作成した際に、テストデータの文章を改行させる方法が分かりませんでした。
　本記事は「フォームで入力した改行をsimple_formatで反映できるようにしたのに、テストデータの文章で改行できない！」と困っている方に向けて書いています。
　あくまで私個人の事例なので、再現性については不明です。
※ simple_format...?と思われた方は参考URLの項目をご参照ください。

# 前提
- 改行した文章を表示するViewファイルにsimple_formatを記述済み

# ポイント
　seed.rbの文字列を改行するために必要な考え方は以下の2点です。
1. 改行コード"\n"を使用する
2. %Q{}で文字列を囲う

# 記述例
　先述のポイントを踏まえた記述例は、下記の通りです。
　![スクリーンショット 2023-03-28 9.14.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3071944/6a3b35a8-62d6-8342-6b86-e637b32bda9f.png)
※1. バックスラッシュ（\）は、Macの場合、option + ¥で入力できます。
※2. rails db:seedを実行すると、改行が反映されていることが確認できます。

# 解説
1. 改行コードとは、改行の指示を出す制御文字を指します。
また制御文字とは、コンピュータに指示を出す時に使う特別な意味を持つ文字のことです。
実際に、コンソール画面で改行した文章を含む投稿等を検索してみると、文中に"\n"が入っていることが確認できるかと思います。
🔍バックスラッシュ（\）は通常書き記すことができない特殊な文字を表すエスケープ文字であり、
　 改行は英語で"new line"と表現します。
　 これらを組み合わせて改行コード"\n"が生まれたのではないかと推測します。
2. %Q{テキスト}は"テキスト"と同じ意味で文字列の生成に使用されます。
　最初は"テキ\nスト"で改行を試みましたが出来ず、%Q{テキ\nスト}とすることで改行できました。
❓原因不明

# 参考URL
- [Railsドキュメント simple_format](https://railsdoc.com/page/simple_format)
- [Qiita simple_formatの使い方](https://qiita.com/mojihige/items/c01682774e8ef29b361f)
- [IT用語辞典 e-Words](https://e-words.jp/w/%E3%83%90%E3%83%83%E3%82%AF%E3%82%B9%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5.html#:~:text=C%E8%A8%80%E8%AA%9E%E7%B3%BB%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0,%E3%83%A1%E3%82%BF%E6%96%87%E5%AD%97%E3%81%A8%E3%81%97%E3%81%A6%E7%94%A8%E3%81%84%E3%82%89%E3%82%8C%E3%82%8B%E3%80%82)
- [Piyopiyo 改行コードとは](https://wa3.i-3-i.info/word11576.html)
- [JavaDrive Ruby入門 文字列 %Qと%qを使って文字列を作成する](https://www.javadrive.jp/ruby/string/index3.html#:~:text=%25Q%20%E3%82%84%20%25q%20%E3%82%92%E4%BD%BF%E7%94%A8,%E5%87%A6%E7%90%86%E3%81%8C%E4%B8%8D%E8%A6%81%E3%81%A8%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82)
NextJS middlewareでError: The edge runtime does not support Node.js '...' module. などというエラーが出る場合

![Screenshot 2023-03-27 at 12.10.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16757/9d94cdfd-a05b-62a8-454c-b9b62bf10945.png)

# ここを見ろと言われる

https://nextjs.org/docs/messages/node-module-in-edge-runtime

> However, the Edge Runtime does not support Node.js APIs and globals.

https://nextjs.org/docs/api-reference/edge-runtime

以下のこれらは駄目 (unsupported-apis)

https://nextjs.org/docs/api-reference/edge-runtime#unsupported-apis

middleware内で大きめなnpmなどを使っていると（firebaseとか）、だいたい引っかかる。

# 解決方法

https://nextjs.org/docs/api-reference/edge-runtime には`fetch`は使えると書いてある。

![Screenshot 2023-03-27 at 12.14.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16757/49ffecf8-d8f2-1524-c8dc-477384c10388.png)

なので`/pages/api/...`にmiddleware内で処理したい内容を書いて、そこを`fetch`してしまえばいい。

```ts:middleware.ts
export async function middleware(request: NextRequest) {
  const redirect404 = NextResponse.redirect(new URL("/404", request.url))

  const value = request.cookies.get("login.user.cookie.name")?.value
  if (!value) {
    // クッキーがないなら404とか
    return redirect404
  }

  if (!(await fetch(new URL(`/api/user/cookie/check/${value}`, request.url))).ok) {
    // クッキーのデータからユーザーが見つからないなら404とか
    return redirect404
  }

  const response = NextResponse.next()

  // クッキーを更新するとか
  response.cookies.set("login.user.cookie.name", value, {
    httpOnly: true,
    path: "/",
    sameSite: "strict",
    secure: true,
    expires: ...
  })

  return response
}
```

https://nextjs.org/docs/advanced-features/middleware#using-cookies
【Riverpod】List, mapの値を更新したのにref.watchで更新されない。


# 結論
```diff_dart
- ref.read(_hogeListProvider.notifier).state = hogeList;
+ ref.read(_hogeListProvider.notifier).state = [...hogeList];
```

`...` を使って再挿入する。どうやら同じポインタのままだと再描画されないっぽい。

参考


https://zenn.dev/shu_illy/articles/d43e842ea9ad4e

🌝
社内向けフロントエンド開発法方法


# デバッグ方法

Visual Studio Code を開き、「ターミナル」メニューの「新しいターミナル」を開く。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849422/21196f0a-5111-297f-5a8e-e5d02f1c5283.png)

画面下にターミナルが開くので`pnpm install`と入力し、Enterキーを押す。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849422/b5209547-bc2a-f24a-1cc5-c7c43476f764.png)

社内のプロジェクトではVisual Studio Codeから実行できる設定がしてあるので、「実行」メニューから「デバッグの開始」を選択すると、ブラウザが開き、ページが表示される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849422/99288109-2f59-3ca3-2fd4-f0ea88fe533c.png)

ブラウザ画面を確認しながら、コードの修正を行う。

更新予定。
【C#で戦う】Coding Test 突破のためのテクニック集、競技プログラミング

# 前書き
就職活動や、競技プログラミングでは
* 正確性
* 素早さ
が必要なので、下記を行うと効率的です。


# テクニック
* 外部ライブラリをとことん使う。例えば、LINQ
* 要所ごとに、 Console.WriteLine();で確認
* 再起処理を活用。
* array よりも list

🐡
GitHubActionsのシークレットをCLIで登録する

# インストール
以下を参照。devcontainerにも対応しています。

https://github.com/cli/cli#installation

```bash
$ gh --version
gh version 2.25.1 (2023-03-21)
https://github.com/cli/cli/releases/tag/v2.25.1
```

最初にGithubにログインします。
対話形式でブラウザでログインすることができます。
```bash
# GitHubにログイン
$ gh auth login
```

# シークレットの操作
```bash
# シークレットの一覧を取得
$ gh secret list
no secrets found

# シークレットの登録
$ gh secret set AZURE_SUBSCRIPTION_ID --body "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

# シークレットの一覧を取得
$ gh secret list
NAME                   UPDATED
AZURE_SUBSCRIPTION_ID  less than a minute ago

# シークレットの削除
$ gh secret delete AZURE_SUBSCRIPTION_ID
```

ファイルを読み込んでシークレットを登録することもできます。
GitHubActionsをローカルデバッグする場合、`act` に読み込ませるシークレットを `.secrets` に記述するので、それをそのまま読み込んで登録してみます。

```bash
# ファイルを元にシークレットを登録
$ gh secret set -f .secrets
```
```.secrets
AZURE_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
AZURE_SUBSCRIPTION_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/402286/dc549a9a-f6a2-b580-8081-96767e0b456e.png)

まとめて削除は `gh secret delete` ではできないようです。

# 参考リンク
[GitHub CLI](https://github.com/cli/cli)
[gh_secret](https://cli.github.com/manual/gh_secret)
Elixirでbinary streamをNバイトのchunkに分ける

# この記事について

binaryのstreamをNバイト単位にまとめる方法を記載します。

## 結論

```elixir
defmodule BinaryStream do
  @doc """
  byte_sizeのchunkにまとめる関数

  ## Examples:
  iex> Stream.cycle([<<1>>, <<2, 3>>, <<4, 5, 6>>, <<7, 8, 9, 10>>])
  iex> |> BinaryStream.chunk_by_byte(7)
  iex> |> Enum.take(3)
  [<<1, 2, 3, 4, 5, 6, 7>>, <<8, 9, 10, 1, 2, 3, 4>>, <<5, 6, 7, 8, 9, 10, 1>>]

  iex> Stream.cycle([<<195>>, <<166, 195>>, <<167, 195, 168>>])
  iex> |> BinaryStream.chunk_by_byte(4)
  iex> |> Enum.take(3)
  [<<195, 166, 195, 167>>, <<195, 168, 195, 166>>, <<195, 167, 195, 168>>]
  """
  @spec chunk_by_byte(Enumerable.t(), pos_integer()) :: Enumerable.t()
  def chunk_by_byte(enum, chunk_size) when is_integer(chunk_size) and chunk_size > 0 do
    chunk_fn = fn element, acc ->
      binary = acc <> element

      if byte_size(binary) >= chunk_size do
        {chunk_list, rest} = do_chunks(binary, chunk_size, [])
        {:cont, chunk_list, rest}
      else
        {:cont, binary}
      end
    end

    after_fn = fn
      <<>> -> {:cont, nil}
      acc -> {:cont, [acc], nil}
    end

    enum
    |> Stream.chunk_while(<<>>, chunk_fn, after_fn)
    |> Stream.flat_map(& &1)
  end

  defp do_chunks(binary, chunk_size, acc) when byte_size(binary) < chunk_size do
    {Enum.reverse(acc), binary}
  end

  defp do_chunks(binary, chunk_size, acc) do
    <<chunk::binary-size(chunk_size), rest::binary>> = binary
    do_chunks(rest, chunk_size, [chunk | acc])
  end
end

```

## 使い方

```elixir
Stream.cycle([<<1>>, <<2, 3>>, <<4, 5, 6>>, <<7, 8, 9, 10>>])
|> BinaryStream.chunk_by_byte(7)
|> Enum.take(3)
# [<<1, 2, 3, 4, 5, 6, 7>>, <<8, 9, 10, 1, 2, 3, 4>>, <<5, 6, 7, 8, 9, 10, 1>>]

Stream.cycle([<<195>>, <<166, 195>>, <<167, 195, 168>>])
|> BinaryStream.chunk_by_byte(4)
|> Enum.take(3)
# ["æç", "èæ", "çè"] === [<<195, 166, 195, 167>>, <<195, 168, 195, 166>>, <<195, 167, 195, 168>>]
```

## 簡単な説明

`Stream.chunk_while/4`を使って、各バイナリを読み込んでいます。
バイナリのサイズによって、複数chunkを作ったり、もしくは次のバイナリと結合したりする必要があるので、accと結合してサイズを判定し、分割する必要があれば再帰処理でchunkを切り出すことにしました。

chunk_whileについては以下の記事にまとめています。

https://qiita.com/SF-28/items/27432d9cf64f253fab84

## なぜこの処理が必要になったのか？

S3へのマルチパートアップロードにおけるパートサイズ制限(5MiB以上)に対応したかったため。

`File.stream!/3`や`ExAws.S3.stream_file/2`を使えばある程度対応できるんですが、ZIPストリームを扱いたい時に上手いやり方が見つけられなかったので、色々調べて実装してみました。

## 雑感

- もう少しスマートな実装方法がありそうな気がしている。
    - もっと言うと、見つけられなかっただけで、既に同等の機能を提供するモジュールがありそう。
- Enumと同じ使用感でStreamを扱えるのが素晴らしい。
    - 巨大データでも全然メモリを使わずに処理できて感動する。
    - エッジコンピュータから大量のデータをアップロードしたりする場合とか役立ちそう。
    - これをさらに並列処理できれば・・・mapと違って各要素が独立した処理ではないから厳しい？
- ある程度のchunkにまとめることで、オーバヘッドの割合が小さくなって通信効率も改善しそう。
- [Packmatic](https://hexdocs.pm/packmatic/readme.html)や[unzip](https://hex.pm/packages/unzip)と組み合わせると、もはやローカルにファイルがなくても作業できちゃう。
    - データ保存容量(=ストレージコスト)を抑えつつ、そのままアクセスできるという欲張りセット。
    - 流石に読込時間は長くなりそうだけど、アクセス頻度が低いデータならZIP解凍の手間もなくて快適になりそう。

## 参考

以下記事のコードを参考にさせていただきました。
Streamに対応するため `Stream.chunk_while/4`と組み合わせ、分割する単位を bitstring -> binary に変更しています。

https://stackoverflow.com/questions/38734845/how-to-split-a-binary-into-n-bit-chunks
trimeshの使い方

# この記事は？
trimeshを使う際の備忘録。trimeshは `3.21.0` を使用しています。

# trimeshの使い方
## カメラ座標系
trimeshはOpenGLをバックエンドで使用しているため、カメラ座標系は以下の図のような座標系を持っています。図は[OpenGLの公式](https://learnopengl.com/Getting-started/Coordinate-Systems)からお借りしました。
![coordinate_systems_right_handed.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/238125/284d66d4-36e4-67a0-9c59-39d9fb05f096.png)

従って、COLMAPやpytorch3dなど別のカメラ座標系を持つライブラリと連携する場合、座標系を変換する必要があります（後述）。各ライブラリの座標表現は[自分の過去記事](https://qiita.com/Simossyi/items/d95de9925c0fccdbc9cc)にまとめたのでご参照ください。

## モジュール解説
### `mesh`
3Dオブジェクト本体となるモジュールです。メッシュは頂点と頂点同士の繋がり（メッシュの面、3つの頂点の組み合わせの集合）によって表現されます。これらは `mesh.vertices` `mesh.faces` などでアクセスできます。これらのアトリビュートはnumpyの配列と同様に扱えます。また、メッシュオブジェクトは色々なメソッドを備えているのでメモしておきます。

#### 読み込み
```python
mesh = trimesh.load_mesh(MESH_PATH)
```

#### 保存
```python
mesh.export(MESH_PATH)
```

#### メッシュの分割
メッシュオブジェクトが連結でない複数のパーツから構成される場合、`split` メソッドでそれらをリスト化することができます。NeRFなどで学習するとfloating objectsが生成されることがありますが、`split` して体積最大のメッシュを抽出するとこういうゴミを消せます。（ゴミの方が大きいと無理ですが。）

```python
meshes = mesh.split(only_watertight=False)
# メッシュの面が最大のものを抽出する。体積最大を取り出したい場合は lambda x: x.volume でOK
mesh = max(meshes, key=lambda x: x.faces.shape[0])
```

#### メッシュの面を減らす
メッシュの面の数を適当に減らしたいときは `mesh.simplify_quadric_decimation` で面を減らせます。メッシュの表面がガタガタしてて無駄に頂点数が増えてる時とかに使えると思います。

```python
mesh = mesh.simplify_quadric_decimation(NUM_FACES)
```

#### メッシュの頂点座標を変換する
COLMAPなど別のカメラ座標系を持つライブラリで作成したメッシュをtrimeshで利用する場合、カメラ座標の変換が必要です。これは頂点座標の変換によって実現できます。頂点座標に対する変換は `mesh.apply_transform` で実現できます。（頂点座標が変換されてもfaceは不変なので、変換はあくまで頂点座標に対してのみ適用されます。）この変換はhomogeneous transformationなので4x4行列を渡す必要があります。

```python
# COLMAP -> OpenGL
permute_axis = np.diag([1, -1, -1, 1])
mesh.apply_transform(permute_axis)
```

#### メッシュの表示
```python
mesh.show()
```

#### カメラ設定とカメラ姿勢の反映
メッシュをレンダリングする際にはカメラの内部パラメータと外部パラメータの設定を反映する必要があります。これは `scene` メソッドによって実現できます。内部パラメータは `camera`、外部パラメータは `camera_transform` で渡します。ここで `camera_transform` はcamera2worldで表現された4x4行列です。

```python
focal = (FOCAL_LENGTH_X, FOCAL_LENGTH_Y)
resolution = (IMAGE_HEIGHT, IMAGE_WIDTH)
camera = trimesh.scene.cameras.Camera(
    focal=focal, 
    resolution=resolution
)
scene = mesh.scene(
    camera=camera,
    camera_transform=RT,
)
```

#### 光線とメッシュの交点を求める
`mesh.ray.intersects_*` メソッドを使って求めます。光線の原点と光線の方向を渡すことで光線とメッシュの交点の面のインデックスや座標を求めることができます。光線の原点は全て同一の場合であっても光線方向の数だけ作成する必要があります。`numpy.tile(origin[None], (num_rays, 1))` などでの数を調整する必要があります。

```python
face_indices = mesh.ray.intersects_first(
    ray_origins=origins,  # (num_origins, 3)
    ray_directions=directions,  # (num_origins, 3)
)
```

また、pyembreeというライブラリを使うと光線とメッシュの交点計算が高速にできるようになります。`RayMeshIntersector` インスタンス初期化時に交点を計算したいメッシュを渡すと交点計算系のメソッドを高速にやってくれます。

```python
intersector = trimesh.ray.ray_pyembree.RayMeshIntersector(mesh, scale_to_box=False)
face_indices = intersector(
    ray_origins=origins,  # (num_origins, 3)
    ray_directions=directions,  # (num_origins, 3)
)
```
cli53によるRoute53ゾーンファイルのエクスポート

Route53でDNSを構築、管理している場合が多いと思うが、基本的にGUIで、オンプレやサーバを立てた時のようにゾーンファイル単位での管理ができない。
そんな場合、cli53を使ってゾーンファイルをエクスポート（生成）する。

インストール方法はこちら。

cli53 とシェルを使って AWS Route 53 に複数レコードを追加してみた
https://engineers.weddingpark.co.jp/sli53-aws-route-53/

公式
https://github.com/barnybug/cli53

# 前提条件
- 操作する端末がLinux/UNIX（Macとか）
- AWSCLIがインストール済
- AWSCLIでAWSアカウントへの認証が通っている

# エクスポート方法

まず、AWSマネジメントコンソールのRoute53　GUI画面で、エクスポート対象ドメインのホストゾーンIDを確認し、テキストにコピーしておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/229539/cc829b0f-bfb7-adb0-8ad0-30f8c7bb06ac.png)

GUIで見るのが嫌な方は、以下AWSCLIコマンドで、ホストゾーンID一覧出力して、エクスポートしたいドメイン名のホストゾーンIDをチェックする。
```shell-session
# aws route53 list-hosted-zones | jq -c -r '.HostedZones[]|[.Name,.Config,.Id]'
```


次にcli53をインストールしたマシンで、以下コマンドでエクスポートする。

```shell-session
# cli53 export ホストゾーンID > 任意ゾーンファイル名
```

例えば「example-cli53.co.jp」というドメインをR53で管理しているとして、ホストゾーンIDが「ZXXXXXXXXXXXXXXXXXXXXX」の場合、こんな感じにコマンド実行。
※example-cli53.co.jp.txt　の部分は任意名称に指定可能。ここではわかりやすくするために、ドメイン名.txtにしている。
```shell-session
# cli53 export ZXXXXXXXXXXXXXXXXXXXXX > example-cli53.co.jp.txt
```

エクスポートしたファイルを開くと、ゾーンファイル形式で各レコードが記載されている。
```shell-session
# cat example-cli53.co.jp.txt
```

イメージとしてこんな感じの内容が出力される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/229539/afe0ebac-12e2-f5cf-cf45-954ca5fd1216.png)

DNS - ゾーンファイルとリソースレコード（ネットワークエンジニアとして）　より
https://www.infraexpert.com/study/tcpip23.html

これでファイルとしてR53のゾーンファイル情報を管理することが可能。
定期的にエクスポートしておくと、何かあったときのために役立つかと思います。
rcloneの認証情報がバックアップ先の2段階認証に伴い通らなくなった話

そりゃそうだよねという単純な話と備忘録です。

## そもそもrcloneとは

https://rclone.org/

rsyncをクラウド上のドライブ（GoogleDrive, Onedrive, Dropboxなど）に拡張したみたいなツール。  
バックアップに大変便利。弊社ではサーバーのDBダンプを夜間にOneDrive(Office 365 Business Sarndardに付随してきたもの)にバックアップするために大変便利に使わせていただいている。

当然ながら、クラウド上のドライブから認証を得る必要があるが、設定中に自動的にブラウザが立ち上がって認証を通してくれるので便利。

## 発端

バックアップのログに以下のような文言が。

```
2023/03/22 22:00:12 Failed to create file system for "od:/backup/www/hogehoge": failed to get root: Get "https://graph.microsoft.com/v1.0/drives/b!hogehogehoge-hogetakusan/root": couldn't fetch token - maybe it has expired? - refresh with "rclone config reconnect od:": oauth2: cannot fetch token: 400 Bad Request
```

ルートに到達できず、トークンが取得できなくなった、とのこと。  
人間に聞いてみると **Office365の認証がスマホで二段階認証しろとうるさいので、社内スマホで二段階認証を通すようにした** らしい。
連絡と共有大事。

## 解決

ブラウザが使えるローカルマシンで再度

```
$ rclone config
```

を走らせると、ローカルでブラウザが立ち上がり、スマホからの2段階認証が求められ、それを通すと新たな認証情報が生成される。  
.config/rclone.clone/rsync.conf に生成された認証情報をぺたっとサーバーの方の(.config/rclone.clone/rsync.conf)に貼れば万事解決。（設定時のリモート名などは元のものと同じにすること）

”この認証情報を持ったアプリのトークン取得を許可する”という観点なので、同一マシンでなくてもいいんですね。

## 教訓

- 認証情報を変えたら影響範囲を調べて情報共有しませう
【ネットワーク】Vlanの、tagg・untaggって何？

# 概要
仕事ではポートVlanではなく、タグVlanを使用している。
タグVlanはポートVlanと違い、一つのポートに複数のVlanを割り当てることができるということはわかるのだが、
tagとuntagの違いがよくわかっていなかった。そのため、ここに記載していく。


# まず、タグVlanとは？
VLANにおける接続方式のひとつ。
データにそのデータが所属するネットワークの情報を付与し、制御する側はその情報を見て所属するネットワークを判断するやり方。
1本のケーブル内に異なるVLANに所属するフレームを流すことができ、物理的に離れたネットワークスイッチ同士を同じネットワークに参加させることができる。
ポートVLANは、VLANひとつにつきスイッチのポートがひとつ必要になるが、タグVLANは一つのポートだけで解決する。
しかし、今回の自分のように、構成が複雑になる恐れがある。

# 結論
- tag
    - 複数のVlanを把握するための設定。スイッチのトランクポートに設定する。
    - VLANのタグを付与するポート。
- untag
    - 一つのVlanだけ使用するための設定。いわゆるポートVlanと同じようなもの。
    - VLANのタグを付与しないポート。

# 説明

以下のように構成されている。
- サーバAとサーバCは同じ`VLAN赤`に属している。
- サーバBとサーバDは同じ`VLAN黒`に属している。
- 赤枠と緑枠は物理的に離れているが、AとC、BとDは互いに同じネットワークに入っている。

![スクリーンショット 2023-02-18 17.39.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/e3c654d6-29d2-25d0-594e-0fadb0da5aad.png)


サーバAが、Cにデータを送る流れを考える。
1. まず、サーバAが、スイッチにデータを送る![スクリーンショット 2023-02-18 17.58.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/19e0b774-3af8-dd8f-2463-04461369ccb8.png)

1. 次に、スイッチAは、データにサーバAの所属するVLANのタグを付与する。（これを、`tagged`という。）![スクリーンショット 2023-02-18 17.59.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/d175b74e-80b3-2df9-1ff8-f99ab5ca84d5.png)

1. その後、スイッチBはタグからサーバCの属するネットワークを判別し、タグを外す。![スクリーンショット 2023-02-18 18.11.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/82abe837-a2c7-2fb4-3599-290978a0bbba.png)
1. その後、データをサーバCに送る。![スクリーンショット 2023-02-18 18.17.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/d824b5dc-dd65-5d56-49e4-a006e0f316fb.png)

このとき、スイッチAとスイッチBは繋がっており、Vlan赤とVlan黒が通っている。
![スクリーンショット 2023-02-18 18.21.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/5992d9fc-a086-a089-71d1-eaad7ce287dd.png)

この`二つがつながっているポートを、トランクポートという`。ここには、複数のVLANを通す必要があるので、`Taggedのポートにする必要がある`。tagged(VLAN赤、VLAN黒)

また、サーバAとスイッチ間では、一つのVlanしか使用しない。そのため、ここでは`untagVLAN赤`の設定を
サーバAがスイッチAに接続するポートに設定してあげる必要がある。
![スクリーンショット 2023-02-18 18.31.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/79bf3594-9702-ae2c-5408-3689a53eb45d.png)

以下、図解するとこんな感じ。
サーバA〜Dが仮想ホストで、中にVLAN黒を使うゲストと赤を使うゲスト両方存在するが、ホストの物理的なポートは一緒という場合は、untagではなくtagで設定する必要がある。
Ex：サーバC <=> スイッチBの間で設定するなら、
スイッチBのサーバCのポートに、`tag黒＆tag赤`を設定してやる。

![スクリーンショット 2023-02-18 18.43.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1288524/ee238eaf-5684-bb2a-252a-b3ac58b3b65c.png)

# 参考サイト

https://www.alaxala.com/jp/techinfo/archive/manual/AX5400S/HTML/10_10_/APGUIDE/0081.HTM#:~:text=VLAN%E5%86%85%E3%81%AE%E3%83%9D%E3%83%BC%E3%83%88%E5%8D%98%E4%BD%8D,Untagged%E3%83%9D%E3%83%BC%E3%83%88%E3%81%A8%E5%91%BC%E3%81%B3%E3%81%BE%E3%81%99%E3%80%82

https://wa3.i-3-i.info/word12091.html
React Nativeで多言語化する

以前、下記の記事を記載しましたが、TypeScript化されていなかったので、TypeScript版を記事にします。

https://qiita.com/shimizu-you/items/3757d809c47b09edfc7d

インストールなどは、前回の記事を参考にしてください。

## 実装

```typescript:translations/i18n.ts
import {default as i18n} from 'i18next';
import {initReactI18next} from 'react-i18next';
import {getLocales} from 'react-native-localize';

import EN from 'locales/en.json';
import JA from 'locales/ja.json';

export const defaultNS = 'en';

export const resources = {
  ja: {
    translation: JA,
  },
  en: {
    translation: EN,
  },
} as const;

const locales = getLocales();

i18n.use(initReactI18next).init({
  resources,
  lng: locales[0] && locales[0].languageCode,
  fallbackLng: defaultNS,
  compatibilityJSON: 'v3',
  interpolation: {
    escapeValue: false,
  },
});
export default i18n;

```

## 型定義ファイル

TypeScriptで使用する型定義を行う。

```typescript:types/i18next.d.ts
import 'react-i18next';

import {resources, defaultNS} from 'locales/i18n';

declare module 'react-i18next' {
  interface CustomTypeOptions {
    defaultNS: typeof defaultNS;
    resources: typeof resources;
  }
}
```

## 実際に使ってみる

```typescript:App.tsx
import {useTranslation} from 'react-i18next';
import './translations/i18n';

const App = () => {
  const {t} = useTranslation();
  const isDarkMode = useColorScheme() === 'dark';

  const backgroundStyle = {
    backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
  };

  return (
    <SafeAreaView style={backgroundStyle}>
      <StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
      <ScrollView
        contentInsetAdjustmentBehavior="automatic"
        style={backgroundStyle}>
        <Header />
        <Text style={styles.sectionTitle}>{t('TITLE')}</Text>
      </ScrollView>
    </SafeAreaView>
  );
};
```

## トランス コンポーネント

翻訳で、コンポーネントを挿入する。

```json:ja.json
{
  "TITLE": "<terms>利用規約</terms> と <policy>プライバシーポリシー</policy>",
  "TERMS": "利用規約",
  "POLICY": "プライバシーポリシー",
}
```

```typescript
import {Trans, useTranslation} from 'react-i18next';

  const onLinkPressed = () => {
    Linking.openURL('https://google.com');
  };

  <Trans
  i18nKey="app.text.consent"
  components={{
    terms: (
      <Text style={styles.sectionTitle} onPress={onLinkPressed}>
        {t('TERMS')}
      </Text>
    ),
    policy: (
      <Text style={styles.sectionTitle} onPress={onLinkPressed}>
        {t('POLICY')}
      </Text>
    ),
  }}
/>
```

React Nativeでの翻訳関連の記事が少ないので、参考になればと思います。
LINEに投稿されたMP4ファイルの作成時刻を知る (残課題あり)

# 試してみたこと

LINE BOTに投稿された動画の作成時刻を、動画のメタデータから取得してみました

# 結果

MP4のメタデータから `creation_time` を取得することができました
しかし、LINEに投稿した動画の場合
`creation_time` は、撮影開始時刻とも、撮影終了時刻とも異なるものでした

18:28:01から19秒間撮影された動画
- 撮影開始時刻 : `18:28:01`
- 撮影終了時刻 : `18:28:20`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102797/195c4fbe-0ed0-317a-4f68-da99ea01a493.png)

`creation_time`は `18:28:35`
うーん、これは何の時間なのだろうか...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102797/16bd0abf-734f-745a-7e7c-68ee3adfd788.png)

# コード

## LINE BOT

動画ファイルが投稿されたら、ローカルに保存するLINE BOT

```javascript:server.js
'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const exec = require('child_process').exec;

const PORT = process.env.PORT || 3000;

// LINE credentials
const config = {
    channelSecret: process.env.CHANNEL_SECRET,
    channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
};

const app = express();

app.post('/webhook', line.middleware(config), (req, res) => {
    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

async function handleEvent(event) {
  if (event.message.type == 'video' && event.message.contentProvider.type == 'line') {
    exec("curl -v -X GET https://api-data.line.me/v2/bot/message/" + event.message.id + "/content -H 'Authorization: Bearer " + process.env.CHANNEL_ACCESS_TOKEN + "' --output video." + event.message.id + ".mp4", (err, stdout, stderr) => {
      return client.replyMessage(event.replyToken, {
        type: 'text',
        text: '動画を保存したよ'
    });
  }
  else {
    return Promise.resolve(null);
  }
}

app.listen(PORT);
console.log(`Server running at ${PORT}`);
```

## メタデータ (`creation_time`) 取得

ローカルに保存されたMP4ファイルから `creation_time` を取得するコード

```javascript:get_creation_time.js
'use strict';

const exec = require('child_process').exec;

console.log(process.argv[2]);

exec("ffmpeg -i " + process.argv[2], (err, stdout, stderr) => {
	var lines = stderr.split('\n');
	var result = lines.find(line => line.includes('creation_time'));
	console.log(result);

  var sep = result.split(':');
  var time = sep[1].trim() + ':' + sep[2].trim() + ':' + sep[3].trim();
  console.log(time);
  var utc_time = new Date(time);
  console.log("creation time:");
  console.log(utc_time);
  console.log(utc_time.toLocaleString('sv'));
  // var jst_time = new Date(utc_time.getTime() + (9 * 60 * 60 * 1000));
  // console.log(jst_time);

  var now = new Date();
  console.log("now:");
  console.log(now);
  console.log(now.toLocaleString('sv'));

});
```

# 参考

LINE BOTを作るには、こちらの記事が参考になります。

https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d


EOF
Googleデジタルマーケティング＆Eコマース：Eコマースにおけるブランディング

# はじめに

”Googleデジタルマーケティング＆Eコマース プロフェッショナル認定証"　の受講が完了しました(2022/3/3)。私がこのコースから得られた素晴らしい体験を、テーマごとに分割して、わかりやすくご紹介しています。興味を持たれた方は、是非下記を受講してみてください。

https://jp.coursera.org/professional-certificates/google-digital-marketing-ecommerce

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/8957baaf-ce73-a2a4-19cd-72db89b6874f.png" width="800">

# ブランディングとは

***<font color="MediumVioletRed">『製品やサービスを特定のブランドで識別して宣伝すること』</font>***

ブランディングは、企業のロゴや名前だけではありません。ブランディングの核心は、顧客が企業や製品に対してどのような感情を抱いているかということです。

- お客様をどう扱うか、間違いがあったときにどう修正するか。
- その企業がどのようにスタートしたのか。
- その企業が支持する価値観とお客さまがどのように結びついているのか。

その背景にあるストーリーがブランディングなのです。

Eコマースストアのほぼすべての側面が、企業のブランド・アイデンティティに反映されます。

# 企業のWebサイト

まず、企業のWebサイトから始めましょう。Webサイトのルック＆フィールは、ブランド・アイデンティティに影響を与えるものの一部です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/c23a3ba9-ca4c-1f8e-b59c-02fa048abf90.png" width="500">

- Webサイトのデザイン、写真、フォントスタイル、配色はすべて、顧客がブランドに対して抱く印象に影響を与えます。
- Webサイトで使用される言葉の声やトーンもそうですし、すべての広告やマーケティング・チャンネルで使用されるデザインや言葉遣いもそうです。
- 企業が販売する製品の品質も、ブランディングの大きな要素です。
    - 顧客がその製品を信頼できると感じるか？
    - 生活を豊かにしてくれるか？
    - お客さまのライフスタイルに合っているか？

# カスタマーサービス

カスタマーサービスにおいても、品質は重要です。お客様が必要なサポートを簡単に受けられるかどうか。何か問題があったらどうするか？

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/b6c85f4d-32d1-e8d7-ac9d-9cadfa5b2ff9.png" width="550">

例えば、お客様のお手元に届いた商品が破損していたとします。そのときの対応や対応の速さは、その企業のブランドに影響を与えます。ブランドを大切にする企業は、事態を収拾するために必要なことを行います。そして、最終的にお客さまに満足していただけるようにします。そうなれば、たとえガティブな経験であっても、お客様がブランドに対して抱く印象にプラスの影響を与えることができます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/eb567f1c-3d4f-868b-fa98-2548b294b808.png" width="６00">

# Webサイトに役立つ情報

Webサイトに役立つ情報を掲載することも、企業ブランドにプラスの影響を与えることができます。

- 例えば、Webサイトでは、お客様が質問や問題がある場合に、問い合わせフォームを簡単に見つけ、記入できるようにする必要があります。

また、よくある質問や、お客様が自分で問題を解決できるようなセルフヘルプ記事など、役に立つ情報へのリンクも掲載する必要があります。

# 返品や交換に関する説明

返品や交換に関する明確でわかりやすい説明も必要です。寛大な返品規定は、顧客のブランドに対する認識を向上させることができます。企業が販売する製品を保証してくれるなら、顧客は信頼感を高めます。返品や交換の選択肢があれば、安心して商品を購入できる可能性が高くなります。

# ストーリーテリング

Webサイトでは、その企業がどのようにスタートし、何を大切にしているのかというストーリーを伝える必要があります。ストーリーテリングは、ブランディングの重要な要素です。お客様は、その会社がどのような価値観を持っているのかを知りたがります。その企業がどのように世の中を変えているのかを知りたがっています。

調査によると、77％の消費者が、自分と同じ価値観を持つブランドから購入することが分かっています。お客様は、気持ちよく買い物をしたいのです。企業のストーリーを知り、その価値観に共感することで、顧客とブランドとの関係はより強固なものになります。

# お客様中心

企業のブランドは、お客様を中心としたものでなければなりません。そのため、企業はターゲットとなる顧客が誰であるかを知り、顧客のニーズや願望を中心にブランドを構築することが重要です。

- 会社の価値観と顧客の価値観は一致しているか？
- それを証明する行動はできていますか？


例えば、ペットを飼っている人が多いペット用品メーカーであれば、売上の一部をペットの養子縁組の支援に寄付することが考えられます。顧客は、購入した商品の一部が困っているペットを助けるために使われることを知り、良い気分になるでしょう。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/a7412523-a0e0-046a-5798-4f36c53bbd6d.png" width="６00">

:::note info
ブランディングとは、顧客との感情的なつながりを確立し、顧客に最高の体験を提供することです。それは、会社のストーリーを共有し、顧客に良いサービスを提供し、顧客の価値観とつながることです。
:::
OpenAPI(swagger)+Prismを使用してモックサーバー作成


# 挨拶
初めまして！お久しぶりの方はお久しぶりです！
最近ダイエットの為、自炊を頑張り節制をしているミツクマです🐻
今日は、業務でバックエンドの作業に引っ張られず
作業を進めるために使用したSwaggerについてお話します！

# Swaggerって？
Swagger は RESTful APIを構築するためのオープンソースのフレームワークのことです。
「Open API Initiative」という団体がRESTful APIのインターフェイスの記述をするための
標準フォーマットを推進していて、その標準フォーマットがSwaggerです。


端的に言うと、Swaggerを使用することでAPIのモックを簡単に作ることができます！

# Swaggerを使ってAPIのモック作成
Swaggerは、それぞれ便利なツールが存在するのですが
今回はVisual Studio Code(以降"VSCode")の拡張機能とPrismを使用していきたいと思います。

以下の拡張機能をVSCodeにインストールします。
- OpenAPI(Swagger)Editor 
- Swagger Viewer

Prismのインストールは以下のコマンドです。
```
npm install -g @stoplight/prism-cli 
```
#### Prismが正常に動作するには、NodeJS 16以降のバージョンが必要になります。

#  APIのモック作成

今回は適当な値を埋め込んだAPIを作成したいと思います。
今回はyaml形式で書いていきます。


```yaml
openapi: 3.0.2
info:
    description: サンプルです。
    version: 1.0.0
    title:API mocks
servers:
  - url: "http://127.0.0.1:4010"
    description: "local development server"

tags:
  - name: "test"
    description: APIのモック
    externalDocs:
      description: "test"
      url: "http://localhost:4010/api/test"
paths:
    "/api/customerList":
      get:
	  response:
	    description: Succsess
	    content:
	      application/json:
		  scheam:
		    $ref: "#/components/schemas/result"

components:
  schemas:
    result:
	type: "object"
	properties:
	  title:
	    type: "string"
	    example: "テストAPIの返り値です"	
```

これでswaggerの準備は完了です。
次にswaggerを書いたディレクトリで以下のコマンドを入力します。

```
prism mock ./swagger.yml
```
これでモックサーバーが立ち上がりました。
起動すると、swagger.yamlに書いたパスが表示されると思うので
そのURLにアクセスしてみましょう！

# 終わり
以上でSwagger + Prismでの簡易的なモックサーバー作成について終わります。
今は、SwaggerHUBというものがありインターネットに公開できるものもあるので
今後そちらの方についてもお話させていただきたいです。

私と同じような初学者のエンジニアの方に役に立てれば幸いです。
これからも自身が学んだことをアウトプットしていこうと思います。
それでは、またの機会にお会いできたら幸いです！
ミツクマでした🐻
新卒エンジニアが【Pythonエンジニアデータ分析試験】の模試を受けてみたら、、、⑤

こんにちは！

前回は***Pythonエンジニアデータ分析試験***の模試を受験してみました！！！
結果は前回の記事をご覧ください。
→[前回の記事はこちら](https://qiita.com/asukitt/items/b045138dd6e7d52de503)

今回は[Python模擬試験](https://camp-lms.trainocate.co.jp/rpv/home/course/default.aspx?k=8FRrJn8tHMr5%2bqKiLf6zQWr%2fXrOAqTTnb2LtCw%2fxpaS%2b3144RchN7NjozcnZpL0Q#ctl00_cplPageContent_subject_list_dkcChapterUnit_rptChapter_ctl00_rptUnit_ctl00_liUnitListGroupItem)を受けてみました！
結果は、、、

***34点/40点満点***

安定して8割5分出せているので嬉しいです！
いよいよ今日が本番なので頑張りたいと思います。

今回も直しを備忘録として残します。

## 模試の直し
#### 不偏分散
とはすべてのデータについて平均値との差分の２条を計算しデータの個数-1で割ったもの

#### dateframeの曜日の引数
B：月～金
W：日～土

#### 白色化
すべての特徴量を0、共分散行列を単位行列とする処理

#### 標準化
特徴量１つ１つについて、分散0、標準偏差1になるように処理

#### 主成分分析
・データの共分散行列の固有値を計算し、、大きい固有値に対応する固有ベクトルが主成分として選択される
・データを標準化して主成分分析を実行する場合、主成分は相関行列の固有ベクトルとして得られる。

#### モデル評価指標
precision_score関数で適合率
F値は回帰モデルを評価するためではなく、適合率と再現率のバランスを評価する。
再現率は、陽性データのうち、実際に陽性に分類できた割合
適合率は、陽性に分類したデータのうち、実際に陽性だった割合

#### 15フォールドクロスバリデーション
１５分割交差検証
データセットを１５分割し、モデルテスト複数回行い、テスト結果の平均を汎化誤差とする

#### テストデータと学習データのデフォルト
学習:75
テスト：25

#### 多クラス分類問題
ラベルが３つ以上あるデータを分類する問題。
ソフトマックス関数
Git submoduleの使い方

# What is Git submodule
別のリポジトリをサブモジュールとして読み込みます.

## 必須コマンド
```
git submodule add https://github.com/hogehoge.git
```

## おすすめのコマンド
```
git submodule add --name 任意の名前 --branch ブランチ名 https://github.com/hogehoge.git サブモジュールを拡張するディレクトリへの相対パス
```

# まとめ

```
git submodule add 引数s URL サブモジュールを拡張するディレクトリへの相対パス
```

# 注意
サブモジュールをインポートした親リポジトリを新たにGit Cloneした場合, サブモジュールは自動的に**インポートされない**
下記の様にクローンコマンドの後に**--recursive** をつけることで解決できる
```
git clone https://github.com/parents_of_hogehoge.git --recursive
```
mswのGraphQLモックでレスポンスを定義したけど、GraphQLCodegenで自動生成したhooksで認識されなかった

- やろうとしたこと: 料理の登録
- どうやろうとしたか
    - 構成
        - フロントエンドでReactを利用
        - GraphQLでフロントとバックをつないでいる
            - フロントエンド側のクライアントはApollo
            - コンポーネントからはGraphQLCodegenで生成したApollo経由のReactHookでGraphQLと通信している
    - jestでテストする上で、mswでGraphQLをモックしてテストをしようと思っていた。

今回モックしたかったクエリの定義はこちら

```js
gql`
  query dishes($searchString: String) {
    dishes(searchString: $searchString) {
      id
      name
      mealPosition
      comment
    }
  }
`;
```

以下は成功例

```ts
  const resposeData = {
      dishes: [
        {
          __typename: 'Dish',
          id: 1,
          name: '生姜焼き',
          mealPosition: 2,
          comment: null,
        },
      ],
    }
  const queryInterceptor = jest.fn();
  const handler = graphql.query(DishesDocument, (req, res, ctx) => {
    execVariables.push(req.variables);
    queryInterceptor();
    return res(ctx.data(resposeData));
  });
  server.use(handler);
```

次に失敗例

```ts
  const resposeData = {
      dishes: [
        {
          id: 1,
          name: '生姜焼き',
        },
      ],
    }
  const queryInterceptor = jest.fn();
  const handler = graphql.query(DishesDocument, (req, res, ctx) => {
    execVariables.push(req.variables);
    queryInterceptor();
    return res(ctx.data(resposeData));
  });
  server.use(handler);
```

テストでidとnameしか使わないので、最低限の定義だけしたところ、レスポンスが以下のようになってしまった。

```js
data: { dishes: [ {} ] },
```
どうやらレスポンスに`__typename`を定義してあげないと現在の構成では認識してくれないらしい。
また、`comment`などはnullableな値だが、nullをセットするだけとかでもして未定義な状態を防がないと、警告が出て鬱陶しい
Oracleで文字列を数値に変換（変換不可な文字列は0にしたい時なども）

# やり方

```TO_NUMBER()``` で可能。

```sql:サンプル
SELECT 
-- 結果は1。
TO_NUMBER('1') AS HENKAN,
-- 結果は2。
TO_NUMBER('1') * 2 AS KAKEZAN 
FROM テーブル名
```

# 数値に変換できない文字列を渡すと例外

例えば ```TO_NUMBER('あ')``` は下記のように例外。

> ORA-01722: 数値が無効です。

# NULLや空文字は例外にならずNULL（空文字）

```TO_NUMBER(NULL)``` や ```TO_NUMBER('')``` は例外にならず、NULL（空文字）が返る。

:::note warn
ざっくり言うと、OracleはNULLと空文字を同じものとして扱うので『NULL（空文字）』という書き方をしています。
:::

# 1以上の整数でない文字列は0として扱いたい時

例えば下記のようなテーブル。

|文字列カラム|数値カラム|
|--|--|
|100|2|
||4|
|1,000円|3|
|あああ|4|
|01|5|

『文字列カラム』はユーザー入力値がそのまま入っているので、数値に変換できない値が入っている可能性がある。
だが、例外を起こさず『文字列カラム×数値カラム』を取得したい。
仮に、文字列カラムが1以上の整数でない値（イレギュラーな値）なら0としてよい。

つまり、下記のような結果を取りたい時。

|文字列カラム|数値カラム|文字列カラム×数値カラムの結果|
|--|--|--|
|100|2|200|
||4|0|
|1,000円|3|0|
|あああ|4|0|
|01|5|0|

```sql:サンプルSQL
SELECT  
CASE WHEN REGEXP_REPLACE(文字列カラム, '^[1-9][0-9]*$', '') IS NULL 
THEN TO_NUMBER(NVL(文字列カラム, 0)) * 数値カラム ELSE 0 END AS KEKKA
FROM テーブル名
```

1. ```REGEXP_REPLACE()``` で1以上の整数パターンに合致する文字列を除去。
1. 上記の結果がNULLなら 『1以上の整数の文字列』 or 『NULL（空文字）』　の **どちらか** 。
1. ```TO_NUMBER()``` へNULL（空文字）が渡される可能性があるので ```NVL()``` をかませる。

# 参考サイトさん

https://qiita.com/waokitsune/items/106ad652799f584cde15

https://docs.oracle.com/cd/E16338_01/server.112/b56299/sql_elements005.htm

# バージョン

Oracle Database 11g Release 11.2.0.1.0 - 64bit Production
Flow仕様調査

※2023/3時点
※頻繁にバージョンアップで更新がかかる。

Salesforce Flow(フロー)の比較
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/450298/2fa6d896-c702-c609-2da5-4fb47714506e.png)


＜例１＞
・注文に商品を紐づけた際に、特定の件数でなければエラーを即時に出したい。
⇒レコードトリガフロー（高速自動更新）で注文商品オブジェクトの新規作成を検知⇒他オブジェクト（特定の件数を保持）からレコードを取得⇒その値をもとに注文商品オブジェクトを更新⇒入力規則でエラー

＜例２＞
・取引先を登録したら、所有者にメールしたい。
⇒レコードトリガフロー（アクションと関連レコード）で取引先の新規作成を拾い、メールアラート（アクション）で所有者に通知

＜例３＞
・注文作成時に、クレカの有効チェックを行い、注文完了に進みたい。
⇒画面フローで注文作成画面を作り、次の要素で外部サービスを使って同期CallOut、結果を取得し問題なければ、レコード登録（注文作成）を行い、次の画面で完了を表示

＜例４＞
レコードを登録する際に、他オブジェクトにクエリをかけてチェックし、保存に進む。
⇒レコードトリガフロー（高速項目更新）＋入力規則


＜その他＞
・フローも結局一般的なガバナと同じ制約を受けるので、1回のクエリが5万件など考慮必要。
・バッチ/複雑な処理はApexが優位

＜参考＞
https://www.salesforceben.com/salesforce-flow-builder-vs-apex/
https://help.salesforce.com/s/articleView?id=sf.flow_distribute_context.htm&type=5
自分たちで会話するChatGPTボット

ChatGPTで作ったボットを SlackとDiscordに入れて使っておりました。

https://qiita.com/akiraak/items/d1d535d1d484b41be437

https://qiita.com/akiraak/items/38bb10f91590b1679953

ラスクについでホロを作って招待してみたところ２匹で会話を始めました。会話が広がりんぐ。

![2023-03-26_18h06_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33704/79358958-4cb8-687e-b735-d64624e1831e.png)

たわけ！
googlemapを利用して住所から郵便番号を自動検索する

## 背景
住所→郵便番号を知りたいとき、[ken_all csv](https://www.post.japanpost.jp/zipcode/download.html)と正規表現で頑張るのが王道？だと思うのですが、まじめにやるといろいろ苦労が多い印象。
どこかの誰かが提供しているAPIはいつ終了するかわからない＆有料なことが多い
かなりの妥協案ですが、googlemapの自動検索ならそこそこの時間＆精度で郵便番号が出せるんじゃないかと思いました。

## 環境
BeautifulSoup,seleniumインストール
```
$ pip install beautifulsoup4
$ pip install selenium
```

[chromedriver](https://chromedriver.chromium.org/downloads)のインストール(プロジェクトフォルダの下にdriverフォルダを作成してそこにchromedriver.exeを置く)

## プログラム
```python:yubin.py
# This Python file uses the following encoding: utf-8
import requests
from bs4 import BeautifulSoup as bs4
from requests.exceptions import Timeout
import re
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.common.exceptions import NoSuchElementException
import sys
import os
import time

#相対パスを絶対パスに変換
def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.dirname(__file__)
    return os.path.join(base_path, relative_path)

#chromedriverのパス
bdriverpath = resource_path('./driver/chromedriver.exe')

#住所から郵便番号を返す
def searchyubin(address):
    #住所検索URL
    url = "https://www.google.com/maps/place/"
    url += address

    options = Options()
    options.add_argument('--headless')     #chromeをヘッドレス起動
    driver=webdriver.Chrome(options=options, executable_path=bdriverpath)

    driver.get(url)
    time.sleep(1)
    resouce = driver.page_source
    driver.quit()
    soup = bs4(resouce, "html.parser")
    if soup is not None:
        #取得したページから郵便番号（数字3つ + "-" + 数字4つ)の部分を抜き出す
        obj = re.search(r"(\d{3})-(\d{4})", soup.text)
        if obj is not None:
            yubin = obj.group()
        else:
            yubin = "該当なし"

    return yubin

```

# 試してみた感想
ほとんどの場合正しい郵便番号が取れるが、相当な田舎は対応してないことが多い
一件一件web検索しているので当然時間がかかる(十分な検索間隔もとらないといけない)
新卒エンジニアが【Pythonエンジニアデータ分析試験】の模試を受けてみたら、、、④

お久しぶりです！

前回は***Pythonエンジニアデータ分析試験***の模試を受験してみました！！！
結果は前回の記事をご覧ください。
→[前回の記事はこちら](https://qiita.com/asukitt/items/b045138dd6e7d52de503)

今回は[Python模擬試験](https://diver.diveintocode.jp/dive_into_exam/2)を受けてみました！
結果は、、、

***80点/100点満点***

安定して8割出せているので嬉しいです！

今回も直しを備忘録として残します。

## 模試の直し
#### pip uninstall
pipを用いてXXXXというパッケージをアンインストールする場合に使用するコマンド

#### matplotlib
MATLABスタイルのコード
```TEST.py
import matplotlib.pyplot as plt 

df["性別"].value_counts().plot.bar()
plt.show()
```

#### histメソッドの返り値
(各ビンの度数、各ビンの範囲、パッチオブジェクトが格納されている配列)の3つの返り値を持つ

#### describeメソッド
外れ値は算出されない

#### pd.rename
df.rename(columns={“name of country”:”国名”, “area”:”面積”})

#### JupyterLabのマジックコマンド
%matplotlib tk はコードセル直下ではなく別ウィンドウにグラフを出力するためのコマンドである

#### ROC曲線のパラメータ
(横軸, 縦軸）= （偽陽性率, 真陽性率）

#### pandas count
pd.values_counts 
複数形なの注意

#### データフレーム化
df[“利用回数”].value_counts().to_frame()
列指定するとseriesで出てくるから、データフレーム化する

#### numpyの真理値
import numpy as np

a = np.array([0,1,10,100])
c = a.flatten()
c[0] = 1000
a == c
を実行すると、
array([False, True, True, True])
というように各要素ごとの真理値を出してくれる
質問する時にこれは気をつけたいよね選手権

# 初めに
春にも程がありますね。みなさんお元気でしょうか。
入社後半年間、職場の上司にこれでもかというほど質問してきたのですが、今回はその中で
「これは質問する時気をつけるべきだな、、、、」
と思ったこと選手権を開催します。というか、しましたので金賞を受賞した３匹を表彰したいと思います。パチパチパチ。

# 計画の方向性や自分の理解が合っているかを質問する
「よし、この機能を追加するぞ！！！」となった時、おそらく皆さんはある程度計画を立てるかと思います。
「DBから●●のデータを取ってくるロジックを▲▲のファイルで組んで、そのデータを■■に渡して、、、」　のように。
***この時点で質問してください。***
そうです、ここです。ここで、「こんな感じで取り掛かろうと思っているんですが問題なさそうですか？」みたいに確認するのです。
このタイミングで質問をしておくことで、以下のメリットを享受できます。
- ***膨大な手戻りをある程度防ぐことができる***
 本当にこれです。もし方向性がそもそも間違った状態で取り掛かり、結構進めた時点で間違いが判明した時、最初からリスタートせんといけんという事態に陥るかもしれません。それはもう地獄です。これが防げるだけでも、超大事な手立てであると言えますね。
- ***質問する側、される側の共通認識を形成することができる***
これは、その後の質問が格段にしやすくなるという意味で、かなり有効です。お互いの前提がすれ違っていては、質問対応そのものが時間の無駄になってしまいますからね。

# まず調べる、デバッグを試みる
質問する前に調べる。当たり前のように思えますが、これは意外とできないことが多いです。特に、調べ方がわからん！！なんてことが僕はよくありました。そこで、手立てを3つ提案します。
- ***ChatGPTを用いる***
そんな中、**ChatGPT**が彗星の如く現れましたね。こいつも活用してみてください。ググる時には、
「Ruby 配列　push」のようにワードを絞って検索する必要がありましたが、こやつを使えば、
「Rubyで配列に要素をpushしたい！」
のように文章ベースで聞くことができます。100%信頼できるわけではないですが、ググるよりも圧倒的に調べやすいのでぜひ試してみてください。
- ***公式ドキュメントを読む***
英語だったり難しい用語だらけだったりとたじろいでしまいますが、何よりも信頼できる情報がそこにはあります。共に立ち向かいましょう。
- ***デバッグを試みる***
Ruby on Railsではpp、JavaScriptではconsole.log()などを使うことが主に挙げられます。
こいつらを駆使することで、「ありゃ。エラー出てるわ」となった時、
***どこまでは自分の期待する動きをしているか***を確かめることができます。つまり、エラーの原因箇所を絞りやすくなります。

# 何がわからないのか整理する
これがぶっちゃけ一番大事かなあと思いますし、一番難しいです。
これができていない状態って、自分の頭の中が整理できていない状態なんです。これでは質問される側も困っちゃいますね。
僕もこれまで、質問をしている最中に、「結局何がわからないんだっけ？？」となり、上司を困らせてしまったことがあります。そこで最近あることを試し、「お、これ中々いいかも」となったことがあるので紹介します。それは、、
***テンプレートを使い、文章化してみる***
ということです。僕は以下のような項目に分け、つまづいた時は自分の思考を整理しています。

***1.やりたいこと***
***2.起きている問題***
***3.問題解決するために試みたこと***
***4.原因の予想***
***5.上記を踏まえた聞きたいこと***

特に、4の原因の予想を立てること、それを文章化することは個人的にめちゃくちゃ大事だなと思っています。特に新人のうちは、自分の思考とどのくらい距離感があるのか知ることができるという意味でこの方法は有意義です。ぜひやってみてください。

# 終わりに
最後まで読んでくださり、ありがとうございました。
新人にとって、上司に質問することはごく当たり前です。全員やってます。しかし、その内容にこだわることで成長スピードは格段に違ってくると思います。
僕も再度肝に銘じようと思います。共に頑張りましょう！！