OmniAICreator/Qiita-1.07M huggingface dataset:
Python3: ChatGPT Speech to text の使い方

参考ページ
[Speech to text ](https://platform.openai.com/docs/guides/speech-to-text)

## 音源
LibriVox に収録されている新見南吉の「ラッパ」を使います。

ダウンロード

```bash
wget https://ia800200.us.archive.org/21/items/yonen_dowa1_um_librivox/yonendowa1_08_rappa_niimi.mp3
```

音源の元になったテキスト
[ラツパ](https://www.aozora.gr.jp/cards/000121/files/43409_16108.html)

## プログラム

```py:to_text.py
#! /usr/bin/python
#
import openai
from dotenv import load_dotenv
import os
import sys

file_audio = sys.argv[1]
sys.stderr.write(file_audio + "\n")

dotenv_path = '.env'
load_dotenv(dotenv_path)
API_KEY = os.environ.get("API_KEY")
openai.api_key = API_KEY

audio_file= open(file_audio, "rb")
transcript = openai.Audio.transcribe("whisper-1", audio_file)
print(transcript)
```

```text:.env
API_KEY="sk-11******************************************"
```

## 実行
スクリプト

```bash:to_text.sh
./to_text.py yonendowa1_08_rappa_niimi.mp3 > out01.json
jq . out01.json
```

実行結果

```text
./to_text.sh
yonendowa1_08_rappa_niimi.mp3
{
  "text": "LibriVox.orgのために録音されました。 ラッパ おじいさんは田舎に一人ぼっちで暮らしていました。 だんだん耳が遠くなったので、 都にいる息子のところへ 耳が遠くなったから よく聞こえる機械を買って送ってくれ と言ってやりました。 息子はいろいろ探したが あまり良い機械がないので ラッパを買って これを逆さまにして耳につけて ラッパの尻から聞きなさい と言ってやりました。 おじいさんはそのラッパを喜んで使っていましたが 耳はどんどん遠くなって とうとうつんぼになりました。 それでもおじいさんは あのラッパでは聞こえなくなったから もっとよく聞こえるのを 送ってくれと息子に手紙をやりました。 息子はいろいろ考えてから また前のラッパと同じラッパを送って このラッパを耳につけると 私の声が聞こえます と言ってやりました。 おじいさんはそのラッパを耳に当ててみました。 そして ああ、聞こえる、聞こえる 懐かしい息子の声が聞こえる と言って喜びました。 けれど、それはおじいさんの耳が聞いたのではありません 心が聞いたのです。 ラッパ終わり この録音はパブリックドメインです。"
}
```
ターミナルのウィンドウ操作系ショートカット(iTerm2,Windowsターミナル）

## はじめに

ターミナルのウィンドウ操作をキーボードでパチパチとやると効率的ですし、何よりかっこいいです(自己満)。

iTerm2(MAC)やWindowsターミナル(WIN)といった高機能なターミナルを使用する場合、様々なショートカットキーが用意されていますが、全部覚えることは不可能なので、その中でも、ウィンドウ操作系の基本的なものだけをまとめたショートカットの備忘録。このくらいはバシッと操作したいものです。

## MAC: iTerm2

| 分類 | キー | 内容 |
| --- | --- | --- |
| ペイン | cmd + d | ウィンドウを縦に分割 |
| ペイン | cmd + D | ウィンドウを横に分割 |
| ペイン | cmd + [ | 次のウィンドウへの移動 |
| ペイン | cmd + ] | 前のウィンドウへの移動 |
| タブ | cmd + t | 新しいタブの作成 |
| タブ | cmd + ← | 左のタブに移動 |
| タブ | cmd + → | 右のタブに移動 |
| タブ | cmd + Number | 指定した番号のタブに移動 |
| ウィンドウ | cmd + n | 新しいウィンドウを表示 |
| ウィンドウ | cmd + w | ウィンドウを削除 |
| その他 | cmd + Enter | フルスクリーン |

## WIN: Windowsターミナル

| 分類 | キー | 内容 |
| --- | --- | --- |
| ペイン | Alt + Shift + - | 水平分割 |
| ペイン | Alt + Shift + + | 垂直分割 |
| ペイン | Alt + 矢印キー | 分割画面の移動 |
| ペイン | Alt + Shift + 矢印キー | 分割画面のリサイズ |
| タブ | Ctrl + Shift + Space | ターミナルを選択 |
| タブ | Ctrl + Shift + t | デフォルトのターミナルが追加 |
| タブ | Ctrl + Shift + d | タブの複製 |
| タブ | Ctrl + Tab | 次の移動タブ |
| タブ | Ctrl + Shift + Tab | 前の移動タブ |
| タブ | Ctrl + Shift + w | タブ/画面を閉じる |

## 参考URL

https://qiita.com/0084ken/items/8aefabef4fd3cfdf8fce
React Nativeで、Realmを使う

React Nativeで、永続的にデータを保存する方法としてRealmを使用します。
モバイルアプリだと、sqliteが主流と思いますが、React Nativeだとsqliteを使用するライブラリーが微妙だったので、Realmを選択しました。

## インストール

```
yarn add realm @realm/react react-native-get-random-values
```

```
yarn add --dev @realm/babel-plugin
```

## babelの設定

```javascript:babel.config.js
module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: ['@realm/babel-plugin'],
};
```

## モデルの実装


```typescript:models/Task.ts
import {Realm} from '@realm/react';

export class Task extends Realm.Object<Task> {
  _id: Realm.BSON.ObjectId = new Realm.BSON.ObjectId();
  description!: string;
  isComplete: boolean = false;
  createdAt: Date = new Date();
  userId!: string;

  static primaryKey = '_id';

  constructor(realm: Realm, description: string, userId?: string) {
    super(realm, {description, userId: userId || '_SYNC_DISABLED_'});
  }
}
```

```typescript:models/index.ts
import {createRealmContext} from '@realm/react';
import {Task} from './Task';

export const TaskRealmContext = createRealmContext({
  schema: [Task],
});
```

createRealmContextのschemaにモデルを渡します。

## 実際に使う

RealmProviderを囲んであげます。

```typescript:App.tsx
import React from 'react';
import {SafeAreaView, useColorScheme} from 'react-native';

import {Colors} from 'react-native/Libraries/NewAppScreen';
import {TaskRealmContext} from './models';
import Home from './Home';

function App(): JSX.Element {
  const isDarkMode = useColorScheme() === 'dark';
  const {RealmProvider} = TaskRealmContext;

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

  return (
    <SafeAreaView style={backgroundStyle}>
      <RealmProvider>
        <Home />
      </RealmProvider>
    </SafeAreaView>
  );
}

export default App;
```

* 書き込み処理

```typescript:Home.tsx
const {useRealm} = TaskRealmContext;

function Home(): JSX.Element {
  const realm = useRealm();

  return (
    <View>
      <Button
        onPress={() => {
          realm.write(() => {
            return new Task(realm, 'test');
          });
        }}
        title="Add"
      />
    </View>
  );
}
```

```typescript
realm.write(() => {
  return new Task(realm, 'test');
});
```

こちらで、realmに書き込むことが出来ます。
realmだとモデルをそのまま書き込めるので、簡単に実装が出来ると思います。
USCPA試験で撤退しないためのたった1つの秘訣

毎日たくさんのUSCPA受験生から相談をいただきます。
「不合格を繰り返し、もうUSCPAはあきらめようと思う」といった相談です。

１．USCPA試験撤退者が続出する理由No.1は？
USCPA試験撤退者が続出する理由のトップは、自分に合わない予備校を選んでしまったことです。
特に表面的な受講料の安さに目を奪われて予備校を選ぶと、撤退することになります。
撤退までしなくても、講義が理解できず受験できるまでに時間がかかり、受験しても何度も不合格になり受験料がかさむことになります。

自分に合わない予備校を選んだのが間違えで、頭が悪いとか、努力が足りないといった理由ではないことが大半です。
予備校選びが悪かっただけなのに、自分に自信がなくなって、のちのちまで引きずるのは嫌ですよね？

２．USCPAの予備校はたった４校しかない！
USCPAの予備校は、日本にはたった４校しかありません。
なので、どんなに忙しくても４校くらいなら、どんな学校なのか調べてみる時間は取れますよね？

どんなUSCPA予備校があるかというと、以下の４校です。

アビタス

プロアクティブ

TAC

大原

（１）アビタス
分かりやすく短期合格できるコンパクトな教材。
日本人講師が日本語で解説し、テキストや問題集も日本語と英語が併記されているので、英語が苦手でも理解できます。
単位取得試験も簡単なので、余計なところに時間をかけずにすみます。

少し受講料が高いのですが、忙しい社会人なら、時間をお金で買うと考えてもいいですよね。
それに、何度も不合格になることを考えたら、アビタスで確実に短期合格した方が、受験料も節約できるので、最終的な費用は抑えられるでしょう。

（２）プロアクティブ
佐々木講師の講義はクセがあるけど、頭に残りやすいと一部に人気（苦手な人も多い）。
また、事務局に何か質問をした時の対応が一番いいと言われています。

ただ、動画のみでテキストがなく、しかも英語で解説されているので、教材は圧倒的に不足しています（不親切設計です）。
合格できずに相談が来るのもプロアクティブ受講生が大半です。

プロアクティブ受講生がプロアクティブを選んだ理由は、ほとんどが受講料が安かったからなのですが、何度も不合格になったら受験料がかさむので意味がないと思いますよ。

（３）TAC
教材はボリュームがあって、社会人が働きながらこなすには、合格が難しいという印象。
Beckerという米国のCPAプロバイダーの教材も使いますが、問題演習にはいいですが、会計初心者だったり英語が苦手だと理解するのは難しいと思います。

教材の質が悪いというわけではないのですが、仕事をしていなくて専任で勉強できる環境でなければ、1科目も受験せずに撤退することになる確率が高いでしょう。
しっかり勉強したいからとTACを選んだ場合、最初は志が高いからいいのですが、途中で少し勉強のモチベーションが下がったところで勉強をやめてしまうかもしれません。

（４）大原
英語の動画講義があるので、英語で講義を受けたいという場合はおススメできます。
ただ、USCPAは色々な講座の１つという印象で、情報のアップデートや受講生のサポートが十分ではないようです。

受講料は比較的安い方なのですが、教育訓練給付金が4校の中で唯一使えないということもあり、最終的な受講料が安くなるとは限りません。
また、単位取得試験が、USCPA本試験と直接的に結びついてはいないので、時間のロスが発生します。



詳しくは「USCPAどこのブログ」のUSCPA予備校4校の比較をご覧ください。

https://www.dokoblog.com/school-uscpa/


３．一番人気のUSCPA予備校はアビタス！
どのUSCPA予備校が人気で受講生が多いかというと、圧倒的にアビタスです。
ツイッターでUSCPA合格者・USCPA受験生にアンケートを取ったところ、全527票で85％がアビタスだったのですよ！！！

https://twitter.com/dokoblog/status/1621333979722039296?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1621333979722039296%7Ctwgr%5E102ea417ba6d732376d3a5b6dd4317054f406d6f%7Ctwcon%5Es1_&ref_url=https%3A%2F%2Fnote.com%2Fdokoblog%2Fn%2Fn476ded181882

ツイッターでUSCPAの情報交換をしているのも、ほとんどがアビタス受講生です。
自分自身がアビタスのおかげでUSCPAに短期合格できましたし、アビタス受講生が一番合格率が高いと確信しているので、積極的にアビタスをおすすめしています。

（１）アビタスの紹介割引11,000円を依頼する
アビタスでUSCPAの勉強を始める場合、紹介割引11,000円が適用になるようにアビタスにご紹介しますので、「アビタスへの紹介ご依頼フォーム」からご連絡ください。

アビタスへの紹介ご依頼フォーム｜USCPAどこのブログ
https://www.dokoblog.com/contact-for-abitus/

（２）アビタスの割引完全版を読む
アビタスの割引やキャンペーンについては、「アビタスのUSCPA費用の割引完全版」にまとめていますので、ぜひ参考にして最大限お得に受講を始めてくださいね。

https://www.dokoblog.com/uscpa-abitus-cost/


まとめ：USCPA試験撤退を避けるにはアビタスで短期合格を目指す！
USCPA試験撤退を避けたいなら、アビタスへ入学することをしてください。
会計初心者や英語が得意ではなくても、安全確実に短期間でUSCPAに合格できるからです。

受講生が多いので教材についての情報交換できますし、途中で挫折せずに合格までモチベーションを保てますよ。
合格実績の高い教材のおかげで、お金も時間も無駄にしないですむので、アビタスへの入学を第一候補にしてくださいね。

アビタスが本当に一番いいか分からない場合は、アビタスの評判・口コミもご覧ください。

https://www.dokoblog.com/uscpa-abitus-review/
コマンドラインツールの jq をつかって、オブジェクトの配列をパースしたいと思っています。オブジェクトの文字列属性がjson文字列としてvalidだった場合はそれをfromjsonでパースし、そうでない場合はそのまま出力するjqスクリプトを書いてください。

以下のようなjqスクリプトを使うことで、オブジェクトの配列をパースし、文字列属性がJSON文字列としてvalidな場合はパースして、そうでない場合はそのまま出力することができます。

```jq
.[]
  | with_entries(if (.value | type == "string") and (.value | fromjson? | type) == "object" then .value |= fromjson else . end)
```

このスクリプトでは、配列の各要素について、 `with_entries` 関数を使用して、各エントリーを処理します。そして、文字列属性がJSON文字列としてvalidな場合は `fromjson` 関数を使用してパースし、そうでない場合はオブジェクトのまま出力されます。最終的に、このスクリプトは、パースされた配列を出力します。
Node.jsのバージョン切り替えに手こずった


普段案件でNode v10.12.0(古い上にLTSでもない)を使っているのですが、別件で最新版を使う必要がありv18.15.0にアップデート。

その結果、元のバージョンに戻せなくなり行き詰まったためその解決策を記録します。

結論から言うとなんてことのない、
**nodenvで管理していたことを忘れnodebrewで最新版をインストールしていたから**
でした。

## Nodeバージョン確認の流れ
まず現在のnodeバージョンとインストール済みのバージョンを確認
```
現在
$ node -v
  v18.15.0

インストール済み
$ nodebrew ls
  v18.15.0
  v16.20.0 (←いつインストールしたのかわからない)
```

使いたいバージョンが出てこない…
仕方なくもう一度インストールを試みる。
```
$ nodebrew install-binary v10.12.0

  v10.12.0is not found
  Can not fetch: https://nodejs.org/dist/v12.16.3/node-v12.16.3-darwin-arm64.tar.gz
```
v10.12.0が見つからないとのこと。
そういえばM1 Macで古いnodeをインストールできなかったからそのせい？


## M1 Macで古いNode.jsをインストール
以前v10.12.0のインストールが上手く行かなかったとき、[こちらの記事](https://zenn.dev/teppeis/articles/2022-03-m1-mac-nodenv)を参考にインストールしました。*1


```
$ arch -x86_64 nodenv install 10.12.0

nodenv: /Users/01049128/.nodenv/versions/10.12.0 already exists
```
やはりすでに存在している…とここでようやく気づきました。
`
nodenv
`
nodenvで管理していたんだった。

## nodebrewを削除

Nodeを何で管理しているのかコマンドで確認し、nodebrewはもう使用しないためアンインストール。

```
nodebrewを見ていることを確認
$ which node
/Users/~~~/.nodebrew/shims/node

nodebrewをアンインストール
$ brew uninstall nodebrew
```
もうnodebrew使用しないので、PCからnodebrew本体を削除。
ホームディレクトリ(/Users/myName)から`command + shift + .`で隠しコマンドを表示。
.nodebrewフォルダを見つけ(画像は削除後のため無し)、フォルダごと削除。
これでもう二度とnodebrewは使いません。
![nodebrew.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2884106/1ee55812-b225-c3e5-0f24-31b51a2db93e.png)

## nodenvよ、再び
無事nodebrewを削除し、nodenvのパスを通します。*2

```
zshの場合
$ echo 'export PATH="$HOME/.nodenv/bin:$PATH"' >> ~/.zshrc
$ echo 'eval "$(nodenv init -)"' >> ~/.zshrc

シェルを再起動
$ exec $SHELL -l
```

パスを通したら、もう一度向き先を確認してnodenvに向いていることを確認し、使いたいバージョンを指定。
```
向き先確認
$ which node
  /Users/~~~/.nodenv/shims/node

インストール済みのバージョン確認
$ nodenv versions
  system
* 10.12.0 (set by /Users/~~~/.node-version)
  14.18.3
  16.14.2
```

今回はすでに使いたいバージョンを指定していましたが、違うバージョンを指定してた場合は以下コマンドで対応。
```
グローバルで使うnodeのバージョンを指定
$ nodenv global 10.12.0

あるプロジェクトのディレクトリでのみ使うnodeのバージョンを指定
$ nodenv local 10.12.0

リハッシュして適用
$ nodenv rehash
```

最後にnodeのバージョンを確認し、使いたいバージョンを表示したら無事切り替え完了です。
```
$ node -v
  v10.12.0
```
以上でなんとかNodeのバージョンを切り替えることができました。


## 引用
*1 [M1 Mac で nodenv から Intel 版 Node.js をインストール](https://zenn.dev/teppeis/articles/2022-03-m1-mac-nodenv)
*2 [nodenvの環境構築](https://qiita.com/282Haniwa/items/a764cf7ef03939e4cbb1)
AWS SAA試験に合格したので合格までの道のりを書いてみた

AWS認定ソリューションアーキテクトアソシエイトを受験し合格したので、取得予定の方の参考になればと思い記載していきます。

### 0. 前提
私は異業種から転職してきたSES勤務のエンジニアです。
AWSは実務で使ったことはほぼなく、インフラ周りの知識も皆無の状態から始めました。
勉強期間としては、ダラダラと続けていたので半年程度。学習時間としては、150時間程度かなと思います。
③は最後3ヶ月のタイミングで始めたので、始めからPing-tから勉強していれば、3ヶ月ぐらいで合格できたかもしれません。。

### 1. 使用した教材について
①[【SAA-C03版】これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座](https://www.udemy.com/course/aws-associate/)

②[【SAA-C03版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）](https://www.udemy.com/course/aws-knan/)

③[Ping-t 最強WEB問題集 AWS ソリューションアーキテクト - アソシエイト(SAA-C03)](https://mondai.ping-t.com/users/sign_in)

### 2. 勉強順について
私はudemyの①を実施した後、udemyの②を実施（ここで難しさで若干の挫折）、その後ping-t③の流れで学習をしました。
上記経験から思ったのは、試験の合格点前後の点数だけであれば、ping-tだけでも十分に狙えるということです。
では次にそれぞれの使用した教材についての感想を述べたいと思います。

### 3. 教材の使用感について
**①【SAA-C03版】これだけでOK！ AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座**

こちらはハンズオンの教材と模擬試験がついています。
ハンズオンは実際にAWSを使用しながら進めていけます。
（AWSを使用にするにあたり多少のお金がかかります）
実務を視野に入れる場合は実施した方が身につきやすいかと思います。

**②【SAA-C03版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集（6回分390問）**

こちらは模擬試験のみとなっていますが、6回分の模擬試験が何度も受け放題となっています。
難易度としては本試験よりも高く、選択肢の内容も含めて理解できている方であれば合格はほぼできるようなものだと思います。
ただかなり難易度が高く、私は何度も挫折しかけました。。

**③Ping-t 最強WEB問題集 AWS ソリューションアーキテクト - アソシエイト(SAA-C03)**

こちらは問題集として知識を定着させるために利用するのがオススメです！
解説も詳しく、間違った問題や、2回連続で正解しているかどうかなどもわかるので、かなり効率的に勉強できます。
（何よりもこのクオリティを無料！ありがたやー）
難易度としても易しい問題から、本番と同程度ぐらいの問題もあるので、正直こちらだけでも合格できるクオリティーだと思っています。

### 4. これから受験される方に向けて
ある程度知識がある方でも、全くの知識ない方にも最初は③Ping-tで基礎固めをして、余裕があれば①・②を進めていければ合格できると感じました。

なのでおすすめの勉強法は、
・③Ping-tで解説含めてほぼ完璧に理解できるように実施
・②のudemy模擬試験を実施
（この時正解率が５割や６割でも合格圏内には入れるかと。高いにこしたことはありませんが。。）

です！

最後に参考まで。
Ping-tの合格体験記に何件かコメントがありますが、私も同じことを感じたので是非参考にご覧いただければ。

ご覧いただきありがとうございました。
資格取得を目指している方。応援しております！一緒に頑張りましょう！！
なにもしてないのに本番でエラーが出た

保守運用って大変よねっていう話。
気が向いたら読んでいってください。

## 前提
- 改修やリリースは一切行ってない
- 突然発生した

マジで、なにもしてないのにパソコンが壊れた感…
そうは言ってられないので、対応しました。

## 確認していく
エラー内容から、APIを実行しているところでエラーが起きていることが判明。
そういえば、ちょっと前に開発環境でもAPIがエラーになったのがあったなー…
APIは先方側に置いてあって、なんか変更しました？って聞いたけど、何も変更してないって言われたけども…
とりあえずcurlを叩いてみる。
```
[root@web ~]# curl -v --head --tlsv1.2 "https://example.com/test/v1/user" 
* About to connect() to example.com port 443 (#0)
*   Trying XX.XX.XX.XX...
* Connected to example.com (XX.XX.XX.XX) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* NSS error -5938 (PR_END_OF_FILE_ERROR)
* Encountered end of file
* Closing connection 0
curl: (35) Encountered end of file
```
開発環境で起きたのと同じエラー出てるわー…
じゃあ、開発環境と同じ対応すればよさそう。

## 対応
curlをアップデートして、apacheの再起動Done！
```
[root@web ~]# yum update curl
[root@web ~]# systemctl restart httpd.service
```
無事に表示されるようになりました。

## まとめ
引き続き調べていったところ、
APIがおいてある先方のサーバの方で更新作業があったようで、TLS1.1以下をdropしていたとのこと。
その結果、Webサーバからのアクセスがはじかれるようになってしまっていた。
きっと開発環境でのエラーも同じ原因だったんだろう。。。

## 今回の学び
- 開発環境で起きたことだから、なんでだろう？を深堀りしなかったのは良くなかった
- ↑があったおかげで対応が早かったのは不幸中の幸い
- TLS1.1以下をdropするって言われてcurlに影響あるって気づけたのだろうか…たぶん無理…
- 連携する、保守するって難しい
運用しているアプリの負荷対策した件

## 概要
開発されてからあまりメンテナンスされてないようなアプリの負荷対策をすることになった。
リリース当初に比べたらユーザ数も増えてることもあり、DBのCPU使用率があがってるのかなと思っていたが
今後もユーザ増加、運用されていくことを考えると対応する必要が出てきた。

## 行った調査
1. SlowQueryの調査
2. ボトルネックになっていそうな通信の調査

### 1. SlowQueryの調査
まず、SlowQueryがボトルネックになって使用率があがっていると想定して調べてみた。
いくつかのテーブルに有効なINDEXを追加することで改善を試みたが、　SlowQueryではなくなったもののDBの負荷は大きく変わらず高くなる状況だった。

### 2. ボトルネックになっていそうな通信の調査
今回有効だったのはこの調査。
通信の中で下記に該当するものを整理した。
- DBの負荷があがる時間帯で呼ばれているもの
- 機能的に呼ばれることが多いもの

その通信の中で実行されているQueryをすべてEXPLAINでまとめた。
```
例）
EXPLAIN SELECT * FROM users WHERE ID = 1;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
|  1 | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const |    1 | NULL  |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
1 row in set (0.00 sec)
```

その結果、あるlogテーブルに対してのゲットが多く、かつINDEXが貼られていないことが判明した。
logテーブルは基本的にinsertのみで、INDEXを貼ることで遅くなるらしい…
なので、INDEXを貼らないのが普通らしい。
logテーブルとしては正しいのだが、logテーブルとして扱っていないのが問題。
これに対してなんかしらの対応を考えた。

## 対応案
a. logテーブルに対してゲットを行わないよう改修を行う
b. logテーブルにINDEXをはる

影響範囲や工数、予算との兼ね合いで、bの案で対応を行うことにした。

## 対応
INDEXがある場合、ない場合のEXPLAINを比較。
リリースしてから何年も経っているアプリのlogで件数もそこそこにあったため、INDEXを貼ることで検索の幅を狭められることが確認できた。
実際にlogテーブルにindexを貼ってみたところ、約1秒のQueryが0.1秒になり、DBへの負荷が減り通信全体も早くなった。

## 今回の学び
- 設計や命名定義はとても大事。logってつけてるなら、logらしく扱う設計にしよう。
- INDEXってすげー。なんにでもつければいいってものではないが、こうかはばつぐんだ。
ruby 初心者必読オブジェクトとクラスとは？

# 対象者

* オブジェクト指向という前にオブジェクトはなんぞやという人.  


# この記事を読んでわかること

* オブジェクトとクラスの概念をイメージできること、またそれぞれの概念を言語化できること

# 目次

1. オブジェクトとクラスとは？.

1. クラスの要素

# 1.　オブジェクトとはクラスとは？. 
オブジェクト＝もの.  
クラス＝同じ共通点を持つ仲間を分類したもの.  

もの（目に見える）+もの（目に見えない）= オブジェクト

![スクリーンショット 2023-04-03 13.06.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602256/2dca774c-3564-dd7d-6e9c-8c27398c0ccb.png)



クラスについて. 

![スクリーンショット 2023-04-03 13.21.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2602256/675e7f16-bc48-b024-8100-97f99317054d.png)

オブジェクトのおさらいですが上の画像の全てがオブジェクト（魚類、山本、生物など）です。
ただ魚類の中のマグロと鮭には「魚」という共通点があります。なので魚類クラスが作れますし、山本さんも田中さんも「人間」という共通点があるので人間クラスが作れます。
もっというとマグロも山本さんも生物という共通点があるので生物クラスが作れます。よってクラスというのは色々な切り分け方があります。


# 1. クラスの要素

クラスの中に含まれるのは３つ.  
①名前（クラスの名前).  
②データ（要素).     
③振る舞い（動作）.  

例　人間クラスを作成するとしたときに↑に当てはめてみる
①名前:人間.  
②データ:氏名、体重.  
③振る舞い:歩く、寝る.  
```
クラス＝同じ共通点を持つ仲間を分類したもの.  
```
↑のように説明させていただいたが補足するとクラスとは、共通点をまとめてクラス内に定義したものとも言える。

次回はオブジェクト指向の三代要素についての記事を書きます。
btrfsをわざと壊してみた

# まえおき

最近RAIDを新たに構築した。せっかくなので、これまで使っていたXFSから耐障害性が高いと評判のbtrfsに切り替えた。

btrfsの特徴はこの記事が分かりやすかった。
[ぜんぜんわからない俺達は雰囲気でbtrfs使っている](https://cat-in-136.github.io/2021/09/zenzen-wakaranai-oretachi-funikide-btrfs-wo-tsukatteiru.html)

この記事ではディスクイメージの一部を破壊したらマウントもできなくなったと書いてあるが、実際にそうなると困るので追試を行った。

# 実験

10MB程度のtest.zipを作成し、ディスクイメージ内の該当データの先頭の2バイトを``CC CC``に書き換えた。

test.zipにアクセスすると、I/Oエラーが出てくれる (バッドセクタではないのにちゃんとチェックサムが働いた)

```
% cp mountpoint/test.zip .
cp: error reading 'mountpoint/test.zip': Input/output error
```

dmesgを見るとチェックサムエラーが検知されたことが分かる

```
% sudo dmesg | tail -n 50
[ 7573.785169] BTRFS warning (device loop20): csum failed root 5 ino 261 off 0 csum 0xeedc5577 expected csum 0xbe9bdcc2 mirror 1
[ 7573.785190] BTRFS error (device loop20): bdev /dev/loop20 errs: wr 0, rd 0, flush 0, corrupt 4, gen 0
[ 7573.785358] BTRFS warning (device loop20): csum failed root 5 ino 261 off 0 csum 0xeedc5577 expected csum 0xbe9bdcc2 mirror 1
[ 7573.785376] BTRFS error (device loop20): bdev /dev/loop20 errs: wr 0, rd 0, flush 0, corrupt 5, gen 0
```

`btrfs scrub`でファイルシステム全体のチェックもできる

```
% sudo btrfs scrub start -B mountpoint
scrub done for aa165469-63c6-4f62-9d04-5fa4e01f7e49
Scrub started:    Mon Apr  3 13:41:21 2023
Status:           finished
Duration:         0:00:00
Total to scrub:   91.00MiB
Rate:             0.00B/s
Error summary:    csum=1
  Corrected:      0
  Uncorrectable:  1
  Unverified:     0
ERROR: there are uncorrectable errors
```

scrub後にdmesgを見ると、破損したファイルの名前が分かる

```
% sudo dmesg | tail -n 50
[ 7446.061071] BTRFS info (device loop20): scrub: started on devid 1
[ 7446.065021] BTRFS warning (device loop20): checksum error at logical 93323264 on dev /dev/loop20, physical 122683392, root 5, inode 261, offset 0, length 4096, links 1 (path: test.zip)
[ 7446.065028] BTRFS error (device loop20): bdev /dev/loop20 errs: wr 0, rd 0, flush 0, corrupt 3, gen 0
[ 7446.065032] BTRFS error (device loop20): unable to fixup (regular) error at logical 93323264 on dev /dev/loop20
[ 7446.065446] BTRFS info (device loop20): scrub: finished on devid 1 with status: 0
```

最初のI/Oエラーの時点でinode番号が分かるので、findでも確認できる
(負荷がかかるため壊れかけているHDDでは行わない方がいいかも)

```
% find mountpoint -inum 261
test.zip
```

# 結果

実運用では、データ部分にバッドセクタが発生した程度ならこのように問題なさそうだ。原因不明のデータ化けも発見できそうで優秀。

参考記事と異なる結果になったのは、参考記事ではWorldをWORLDに書き換えたことでファイル名部分も破壊され、メタデータ領域の破損として扱われたためにマウントできなかったのかもしれない。

# 補足

チェックサムを無視してファイルを取り出したいときは、マウントオプションに`rescue=ignoredatacsums,ro`を指定する。

```
% sudo mount -t auto -o rescue=ignoredatacsums,ro crash.img mountpoint
% cp mountpoint/test.zip .
%
```

もちろんデータは壊れているので、少しでもサルベージしたいときのための方法。
LINE LIFFアプリの練習

カレー屋の一覧アプリをラインアプリで作る。

# 目的
1. ラインアプリがどんなものか調査学習。

2. 社内のSlackのカレースレッド（おすすめカレー屋や、このカレー屋行ってきた報告をするスレッド）があるのだが、そのカレー屋一覧のアプリを作りたい。Slackのスレッドだと、過去の投稿を見るのに一苦労（遡って見るのに、スクロールして見ていかなくてはいけない）。コメントとかもたくさん付いてるし。なので、一覧が欲しい！という個人的願望がある。


調べると、「LINEミニアプリ」は審査が必要とのことなので、「LIFFアプリ」について、調べる・進めることにした。
> LIFFプラットフォームで動作するWebアプリは、大きく分けて「LIFFアプリ」と「LINEミニアプリ」の2種類があり
（中略）
LIFFアプリとLINEミニアプリの違いをテーブルにまとめてみました。どちらもLIFF SDKを搭載しているのですが、LINEミニアプリを開発するにはLINEによる審査および認定が必要です。

https://logmi.jp/tech/articles/325636

# はじめに、LINE Developersの設定など。

これらの記事を見てスムーズに行えました。
https://ayaoriko.com/coding/line/liff-app/
https://goworkship.com/magazine/what-is-liff/

画面サイズが、Full、Tall、Compactのどれにするか、迷いましたが、
一旦設定しても、あとから変更OKでした（汗）

# カレー屋一覧を表示してみた。
LIFFアプリで、カレー屋一覧を表示してみました。リンクは公式ホームページに飛びます。（画面はTallサイズ。トーク画面がちょっとだけ見えるサイズです。）

<img width="550" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/4174bff8-818d-b51a-4ad9-b67686bc2538.png">

ちなみにFULLは画面いっぱいに表示、Compactが一番小さく表示（画面半分ぐらい）です。

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/24c0e827-0829-5d34-74a7-cfcdc6d06ffd.png">

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/1348ebe5-ec23-9d15-fcba-62e1aa933ca8.png">

# トークルームへメッセージを送信
一覧だけを出すだけでは面白くないので、ボタンを付けてトークルームに、なにかメッセージを送るようにしてみました。
「営業時間」ボタンをクリックすると、営業時間がトークルームに送られます。
（単にHTMLに文字列を貼り付けてあるだけなので、公式で営業時間の変更があっても更新はされませんが・・・-o-）
<<
これらの記事を参考にしました。
https://developers.line.biz/ja/docs/liff/developing-liff-apps/#sending-messages
https://developers.line.biz/ja/reference/liff/#send-messages

<img width="550" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/6229ab5e-1aa6-de39-f538-280fd8ab0956.png">

営業時間ボタンをクリックすると、トークルームに、メッセージが送られます。

<img width="550" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1284134/3a129f66-c29e-4b62-27c5-86fff0a71315.png">

メッセージ送信部分のコードはこんな感じ
```javascript
function sendMessages(text) {
	if (!liff.isInClient()) {
    	window.alert('This button is unavailable as LIFF is currently being opened in an external browser.');
	} else {
		liff.sendMessages([{
			'type': 'text',
			'text': text
		}]).then(() => {
            window.alert('Message sent');
        })
        .catch((error) => {
            window.alert('Error sending message: ' + error);
        });
	}
}

$( "#button_samurai" ).on( "click", function( event ) {
	sendMessages("11:00～21:30 （L.O. 21:00）");
});
```
# 感想
Androidだと、キャッシュが残ってしまうので、HTMLを更新したあとは、スマホの設定から、LINEアプリのキャッシュの削除をいちいちしなければならず面倒だと感じた。
それ以外は、スムーズに開発できそう。
あと、LIFFを使うとLINEのプロフィール情報（LINEのユーザーIDや名前など）を得ることができるそうです。しかし、今回のアプリでは使う機会がなさそうです・・・。

# 今後のタスク、課題
・LINE友達へのカレー屋一覧の共有
・Slackの投稿がそのままこちらに反映できれたらいいのですが（理想）。
【TypeScript】型がunknown型か判定するジェネリクス

型`T`が`unknown`型かを判定する、以下のような型を考えます。

```ts
type IsUnknown<T> = /* ここをどのように実装すればいいか？ */;

type A = IsUnknown<unknown> // true
type B = IsUnknown<any> // false
type C = IsUnknown<never> // false
type D = IsUnknown<string> // false
```

## 型がunknown型か判定するジェネリクス


以下のように書くことで、型が`unknown`型か判定することができます。

```ts
type IsAny<T> = 0 extends (1 & T) ? true : false;
type IsUnknown<T> = IsAny<T> extends true
  ? false
  : unknown extends T
  ? true
  : false;

type A = IsUnknown<unknown>; // true
type B = IsUnknown<any>; // false
type C = IsUnknown<never>; // false
type D = IsUnknown<string>; // false
```

:::note alert
以下のように、`unknown extends T`とするだけではダメです。

```ts
type IsUnknown<T> = unknown extends T ? true : false
```

このように書くと、`InUnknown<any>`が`true`になってしまいます。
:::

`unknown extends T`だけでは判定できないため、前もって`IsAny`型を使用し`any`型を条件分岐しておくところがポイントです。

こちらは[stackoverflowの回答](https://stackoverflow.com/questions/68232762/check-if-type-is-the-unknown-type)を参考にさせていただきました。

https://stackoverflow.com/questions/68232762/check-if-type-is-the-unknown-type

※ただし、このstackoverflowの回答は部分的に間違っているため、この記事で紹介したコードはそれを修正したものになります。
はじめてのWebアプリ開発！

# 最高の一冊
Webアプリケーションを作ってみたいけど作り方がわからない...
そんな全くの初心者の私が1から勉強し、Webアプリを公開しました！
サイトのURLは[こちら](https://privatediary-by-ru.com)です！

では、僕はどのようにしてWebアプリを開発したのか。

#### **ある一冊の本を参考に開発を勧めました！**

そんな最高の本がこちらです。[動かして学ぶ！Python Django 開発入門](https://www.seshop.com/product/detail/23363)
|　　　　　　　　  　　　　　　　　   　　　　　　　　　　　　　　　　　　　　　　　　　|
|:-:|
|![L.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3274897/b1e3268d-e242-3cdc-64f8-1cbe0d29a824.png)

この本は、PythonのフレームワークであるDjangoを用いたWebアプリについて初心者にもわかりやすく網羅的に書かれていて、私はこれ一冊でWebアプリを開発することができました！

めちゃくちゃオススメで皆さんにぜひ買っていただきたいのですが、
私はこの本とは一切関係がないのでどれだけ売れてもお金は入ってきません笑

# Webアプリについて
私が開発したWebアプリについて少し紹介したいと思います。

スマホで開いたときのホーム画面はこのようになっていて、おしゃれですよね笑
このデザインは[Bootstrap](https://startbootstrap.com)を利用しています。この利用方法も書籍にわかりやすく書かれています。

|　　　　　　　　  　　　　　　　　   　　　　　　　　　　　　　　　　　　　　　　　　　|
|:-:|
|<img width="400" alt="img" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3274897/c4e88a02-59fa-cea4-7566-8662d6cc8b7e.png">

## 機能
#### 自分専用の日記を保存することができる
    - 会員制なので他の人に見られるということはない
    - メールアドレスのみで無料で会員登録することができる
#### このアプリについて問い合わせをすることができる
    - なにか質問があれば質問フオームにて問い合わせをしてください

# 最後に
Webアプリ開発初心者の私が開発から公開までをすることができた素晴らしい本の紹介をさせていただきました。
ぜひ皆さんもこの本を買ってWebアプリ開発デビューをしてみましょう！！！
Apple Developerが忘れがちな4つの更新項目

# 更新項目
毎年やってくるCertificateやProfileの更新項目をまとめてみました。
うっかり更新を忘れてしまったり、更新の度に調べては1年後に忘れてしまう作業をこの記事で解決できたら何よりです。

### 1, Development/Production Certificate
開発や配信用に必要な証明書で、1年間の有効期限があります。
有効期限が切れると、アプリのビルドや配信に影響が出るため、更新が必要です。

### 2, Provisioning Profile
開発や配信用に必要なプロファイルで、1年間の有効期限があります。
証明書と同様に、有効期限が切れるとアプリのビルドや配信に影響が出るため、更新が必要です。
https://hirokuma.blog/?p=2783

### 3, Push Notification Certificate
プッシュ通知に必要な証明書で、1年間の有効期限があります。
有効期限が切れると、プッシュ通知が正常に動作しなくなるため、更新が必要です。

### 4, Apple Developer Program Membership
Appleの開発者プログラムに参加するための有料のメンバーシップです。一番重要かもしれません。
1年間の有効期限があり、有効期限が切れると、開発者プログラムへのアクセスや証明書の更新ができなくなるため、更新が必要です。
Appはダウンロードできなくなり、すでにインストールまたはダウンロードしたユーザーも、そのAppを使用できなくなります


# 更新方法

## Development/Production Certificate

#### 更新方法
1. Apple Developer Accountにログイン
2. 左側のメニューから「Certificates, IDs & Profiles」を選択
3. 「Certificates」をクリックし、更新する証明書を選択
4. 「Edit」ボタンをクリックし、新しい証明書をアップロード
5. 証明書が更新されたことを確認し、必要に応じてProvisioning Profileを更新
6. Xcodeで、新しいProvisioning Profileをダウンロードして、プロジェクトに適用

https://dev.classmethod.jp/articles/update-ios-certificate-and-provisioning-profile/

## Provisioning Profile

#### 更新方法
1. Xcodeを開く
2. 左上の「Xcode」をクリックして、「Preferences」を選択
3. 「Accounts」タブをクリックし、Provisioning Profilesの一覧から更新したいプロファイルを選択
4. 右下の「Refresh」ボタンをクリック
5. 更新されたプロファイルがリストに表示されたら、必要なものを選択して「Download」ボタンをクリック
6. ダウンロードが完了したら、Xcodeが自動的に更新される

#### 更新方法2
1. Apple Developerサイトにログイン
2. 左側のメニューから「Certificates, IDs & Profiles」を選択
3. 「Provisioning Profiles」をクリックし、更新したいプロファイルを選択
4. 「Edit」ボタンをクリックして、プロファイルを更新
5. 変更を保存し、プロファイルをダウンロード
6. ダウンロードしたファイルをXcodeにドラッグ＆ドロップして、Provisioning Profileを更新

https://qiita.com/Labi/items/d93624e4f6ec7d073e76

## Push Notification Certificate

#### 更新方法
1. Apple Developer Centerにログインし、Certificates, Identifiers & Profilesのページにアクセス
2. 既存のPush Notification Certificateの一覧が表示されますので、更新したい証明書を選択し、「Edit」をクリック
3. 有効期限の切れたPush Notification Certificateを更新する場合は、「Reset」をクリックして新しい証明書を生成
4. 証明書を再発行する場合は、「Revoke」をクリックして古い証明書を取り消し、新しい証明書を生成
5. 新しいPush Notification Certificateを生成すると、証明書ファイルが自動的にダウンロードされる
6. ファイルを開いて、Xcodeにインポートすることで、アプリケーションのPush通知を更新できる

注意点として、Push Notification CertificateはAPNs(通知サーバー)がアプリケーションを識別するための重要な証明書です。
有効期限が切れる前に更新を行うことが必要であり、期限切れ後にPush通知を送信することはできません。

https://qiita.com/takasan/items/2e9b1f7c3f78ea093195

## Apple Developer Program Membership
最近値上げしています。個人で契約するなら黒字は目指したいですね。

1. developer.apple.comにログインし、Accountをクリック
2. Membershipのタブを選択し、更新するアカウントを選択
3. Renew Membershipをクリックし、支払い情報を更新
4. 支払い情報を更新し、更新内容を確認してからRenewをクリック
5. アカウントが更新されると、新しい期間が表示される

https://sunny10side.com/apple-developer-program-membership-renewal/

## 終わりに
以上Apple Developerに必要な更新項目でした。
App store connectやメールをこまめにチェックすることやリマインダーで更新に気がつくことも重要です。
忘れずに更新しましょう。
【ポエム】SES企業で働くメリット、デメリット

7年勤めたSES企業を退職し、フリー~~無職~~になるので今までの経験をもとに、SES企業のメリットデメリットを上げていきます。
すでにこの手の話は色々とあるとは思いますが、あくまで実際に働いた実体験として残します。
こんな奴もいるんだ程度に見ていただけると幸いです。
学生の方などは就活時に参考になるかも？

# tl;dr

###### メリット(向いている人)
- IT業界未経験(新卒含む)の方でも採用されやすい
- いろんな現場を見れる
- 大手SIerなどに配属されやすい
###### デメリット
- ハズレ案件に配属される可能性がある(案件ガチャ)
- 給料が低い
- 契約面でたまにゴタゴタすることがある

# 筆者はどんな人？
学生当時は業界についてあまり知らず、また就活におけるストレスも嫌だったので学校の合同説明会に来ていたSES企業に新卒入社。
元々プログラミングは好きだったのでIT業界でプログラマとして働ければいいやという気持ちだった。

その後は大手SIerに派遣され、顧客社内WEBシステムの開発現場に配属された。
SESで働く人の中では珍しいとは思うが、最初に配属された現場から7年契約を切られなかった。
時折ヘルプで別現場に行ったりなどもありました。

自身の転居や給与、スキル向上目的などの都合から2023年3月で現職を退職。


# メリット1 採用されやすい
SES企業は、主に自社社員を契約した現場へ派遣することで利益を上げます。
その性質上 能力 < 人手 という評価基準となり、ある程度能力が低くても採用されやすいです。

またSES企業と派遣先のお客様の契約において、派遣する人の「単価」という話が出てきます。
あくまで私が働いていた現場だけかもしれませんが、テスターやコーダーなどを採る際は
単価が安い=勤続年数が低い人が好まれやすい傾向にありました。
なので新卒採用の方が会社としては積極的でした。
~~その現場のリーダーが大変になりますが。~~
逆に勤続年数が高い方は、高い単価を設定され契約を渋られたり、
契約しても微妙に契約業務外の仕事を顧客から任されたりとグレーな感じがありました。
ある程度働いていれば仕方ありませんが、派遣先の仕事以外にも自社の事務的な作業があったりなど業務量は多くなりがちです。
この辺はどの業界にもあるかもしれませんが闇ですね...

WEB系企業はSES企業よりは採用の敷居が高く、ある程度何かしらのスキルがないと厳しいです。
新卒求人を見ても、すでにある程度技術があるという募集要項になっていたり、
転職の求人に至っては業務経験2〜4年以上などの条件が多いです。

# メリット2 いろんな現場を見れる
実際働くと、人にもよりますが基本的に3〜6ヶ月の単位で契約がされる事が多く
その都度同じ現場で契約更新されるか、契約終了し別の現場にアサインされるような形になります。

その分業務で関わったシステムなどについて、深く関わることができないことが多いです。
ただ色々な人やシステムに関わることができる機会はあるのでその都度学べることはまあまあ多いです。
また仮に現場が合わないと感じた場合でも、自社側に現場を変えたいという意思表示もできるので、
技術レベルや人間関係などのミスマッチにも対応しやすい傾向にあると思います。

# メリット3 大手SIerなどに配属されやすい
配属先は大手SIerなどが多いです。
SIerへの派遣の場合、SIerさんの労働カレンダーに従って働くことになるので、
土日祝はちゃんと休めます。~~会社によっては自社に出社しろなんてところもあるみたいですが。~~

また労働時間に関しても契約内容(派遣、準委任)にもよりますが、自社との契約時間内に収めないといけないため、
無理に終電まで働かされたりということもあまりないです。
もちろん繁忙期には残業をしないと間に合わないなどはあります。

仕事内容に関しても大企業のWEBシステムの開発案件だったりで、大きなプロジェクトに関われることも多いです。

# デメリット1 ハズレ案件(案件ガチャ)
会社の営業力の都合などにもよりますが、自分がやりたいと思う案件に必ずしも就けるわけではありません。
コーダとしてプログラムを作りたいと思っても、テスターとして配属されたりということもあります。
あとは炎上案件で増員という形で放り込まれることもあります。
ある種、そういったところの現場経験ができるという意味であればメリットかもしれませんが（？）
勿論そういった配属先について会社側に意見することは可能ですが、必ずしも拒否することはできないというのが現状です。
私の身近なところの話だと監視業務に就いた方もいるようで、業務中ずっとサービス監視画面を見てアラートが上がったら連絡するだけの仕事だった人もいたとか。人によってはいいかもしれませんが、私はそうならなくてよかったなと思ったりしてます。

# デメリット2 給料が低い
私を例にしますが、入社当初初任給が月給20万円ちょっとでした。
そして退職前7年目の基本給が24万円ちょっとです。
私は上がっているだけマシ。。。かもしれませんが、私のようにアラサーあたりになると
給与面が理由で転職を考えたりという方が私の会社では多かったです。
また、任される業務量と給与が不相応と感じていました。
自社業務、顧客との調整業務、その他自身のタスクをこなさなければいけないという立場になってもそれに伴って給与が上がるということも無かったです。
これは派遣の性質上、現場を知らない人間が人事評価をすることになるので起きる事なのかなと思っています。
あとは会社自体が給与を上げたがらないといったこともあるかもしれません。

# デメリット3 契約面でゴタゴタすることがある
契約形態にもよりますが、準委任案件だとひと月の稼働時間の下限上限が決められています。
その稼働時間に満たされないとなると別途精算をしなければなりません。
明らかに稼働時間上限を超えるタスクを割り振られて上限を超えてたり、逆に年休を取らなければならず稼働時間が下限を下回ってしまった時に顧客から文句を言われたことがありました。任された時にその都度顧客にも相談はしているんですけどね。。。
（営業に言ってくれというのが本音。）
その原因を営業、労働者、顧客の3者で話し合ったりする時間とかは完全に無駄だなと感じていました。
新卒で入ったばかりでそのようなことを言われることはないと思いますが、中堅あたりになるとこのような話も度々ありストレスでした。

また、契約ではないですが勤怠連絡や日々の稼働報告も自社と顧客に別々に登録しないといけなかったりと雑多な作業は多かったなと感じてました。こればかりはどんな仕事でもあるのかもしれませんが。

# まとめ
以上、私の経験をもとに主観で見たSES企業のメリット、デメリットでした。
自分の中で感じたことを整理してみたいと思い記事にしてみました。
ここまで読んでくださった方、ありがとうございました。

もし新卒の方がいましたらSES企業はやめておけとまでは言いませんが、
やめたくなったらやめるぞというくらいの気持ちの方が、色々と病まなくて済むと思います。
3年は勤めろなんていう言葉もありますが、本当に自分に合わないなと思ったならば
行動することも大事だと思います。
20代30代の時間は貴重です。自分が思った方向に進むようにしましょう。
UiPath v2023.4 新機能紹介 (ライブストリーミング & リモートコントロール)

# はじめに
- 今回はUiPath v2023.4の新機能である「ライブストリーミング & リモートコントロール」を紹介します。
- この機能はUnattended(無人)ロボット実行時のデスクトップ状態をAutomation Cloud管理画面からリアルタイムで表示することができます。さらに画面操作を行いたい場合にはリモートで制御することも可能です。
- 従来Unattendedロボットによるジョブエラーをデバッグするには実行ログの出力結果によって被疑箇所を推測したり、[メディア記録機能](https://docs.uipath.com/ja/orchestrator/standalone/2022.10/user-guide/about-recording)などを利用して実行状態を確認していましたが、いずれにしてもリアルタイムで実行状態を参照することはできませんでした。また管理者によって介入するためにはUnattendedロボットのマシンに実行アカウントでログインして操作する必要があり、マシン台数が多い時には運用が煩雑になっていました。
- 今回の新機能を利用することによって、このようなデバッグ作業が容易に実行できるようになりました。

# デモ動画
- このデモでは [RPA Challenge](https://www.rpachallenge.com/?lang=ja) のサイトからExcelファイルをダウンロードし、各行よりデータを読み込み、対応するWebフォームに自動入力しますが、途中で邪魔なポップアップが表示されます。
- ライブストリーミング機能によって実行状態をリアルタイムで監視し、ポップアップが出たタイミングでリモート制御を行います。
![LiveStreamingDemo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/381f284b-fa95-9984-4041-62f9702ebf3e.gif)

# この機能を利用するには？
- まずAutomation Cloud > Orchestrator > プロセス > その他の設定画面にて「ライブストリーミングを許可」のトグルをオンにし、リモート制御の許可を「有効」に設定します。
![LiveStreamingSetting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/a9ea60c8-b24a-4d00-8b9b-e807edc19475.png)
- 次にUnattendedロボットのマシンにてUiPath Studio/Robot v2023.4以降をインストールします。Community Editionの最新版は[こちらからダウンロード](https://download.uipath.com/UiPathStudioCommunity.msi)します。インストーラーの途中で「Unattendedロボット」を選択し、Orchestratorにマシンキーまたはクライアント資格情報にて接続します。
- さらにUnattendedロボットのマシンにてTightVNCをインストールします。TightVNCは[こちらからダウンロード](https://www.tightvnc.com/download/2.8.75/tightvnc-2.8.75-gpl-setup-64bit.msi)します。インストール時には **Register TightVNC Server as a system service のオプションをオフ** にします。
![TightVNC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/1d65c28f-7904-e015-07b7-77652e76e62c.png)
- Unattendedロボットのジョブ実行時にデスクトップの状態を確認するには対象ジョブの縦三点リーダー > **ライブストリーミングを開く** をクリックします。
![LaunchLiveStreamingView.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178312/6ea3d35f-ed24-c164-8c2a-649e8d274817.jpeg)
- しばらくすると別タブでデスクトップ画面が表示され、ジョブ実行の状態をリアルタイムで確認することができます。
- リモート制御を行いたい場合には、画面上部の「リモート制御」をクリックすると画面操作が行えるようになります。ただし管理者権限昇格時のUACダイアログが表示される場合などには画面表示が切断されてしまうため要注意です。

# 参照リンク
- [Automation Cloud リリースノート](https://docs.uipath.com/ja/orchestrator/automation-cloud/latest/release-notes/march-2023) > 2023年3月22日 参照
- [ライブストリーミング & リモートコントロール Webガイド](https://docs.uipath.com/ja/orchestrator/automation-cloud/latest/user-guide/live-streaming-and-remote-control)

# 最後に
- 「Unattendedロボットは画面操作が見えないから扱いが難しい」と思われがちですが、この機能を活用することによってデバッグが格段に効率的に実施できるようになります。
- 今後ともUiPath製品の活用に役立つ情報発信を行っていきますので、どうぞ宜しくお願いします！
VSCodeでROS 2のC++ノードをデバッグする方法


# 環境

Ubuntu 22.04 with ROS2 humbleの環境を想定しています。
おそらくWindowsやMacの場合はgdbの設定などで差異があると思います。

多くの操作は参考文献[[1]](https://rinatz.github.io/cpp-book/debug-vscode/#_1)と共通なのでまずはそちらを参照すれば良いかと。

# VSCode側の準備

## ROS 拡張機能のインストール

ROSの拡張機能をインストールします。

## launch.jsonの編集

参考文献[[1]](https://rinatz.github.io/cpp-book/debug-vscode/)や[[2]](https://jp.community.intersystems.com/post/vscode%EF%BC%9A%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%AB%E3%82%A2%E3%82%BF%E3%83%83%E3%83%81%E3%81%97%E3%81%A6%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)に倣ってデバッグの設定ファイルlaunch.jsonを編集します。

```json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "ROS: Attach",
            "type": "ros",
            "request": "attach"
        }
    ]
}
```

めちゃくちゃ短いですが多分ROSの拡張機能がいろいろよろしくやってくれてるっぽいのでこれでも動きます。

# プログラム側の準備

Debugビルドを行います。


```
colcon build --symlink-install --cmake-args -DCMAKE_BUILD_TYPE=Debug --packages-select <your package>
```

`--packages-select` は`--packages-up-to` でも良いです。（たまに依存パッケージも一緒にビルドしないと行けなかったりするケースもあるはず）

# 実行しているプログラムへのAttach

プログラムを実行後、VSCodeの左のツールバーから再生ボタンをおしたり、コマンドパレットに Debug: Start Debuggingを打ち込んだりF5を推したりしてAttachを実行します。

c++かPythonか聞かれた後にAttachするプロセスを聞かれるのでデバッグしたいファイル名 or 実行ファイル名で検索して選択します。（この時、管理者権限での実行のためにsudoのパスワードを求められます。）


Attach成功！→ BreakPoint作るなり変数をWatchするなり何なりしてしまいましょう。




# 参照

- [1] [ゼロから学ぶ C++
Visual Studio Code による GUI デバッグ](https://rinatz.github.io/cpp-book/debug-vscode/)
- [2] [VSCode：プロセスにアタッチしてデバッグする方法](https://jp.community.intersystems.com/post/vscode%EF%BC%9A%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%AB%E3%82%A2%E3%82%BF%E3%83%83%E3%83%81%E3%81%97%E3%81%A6%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95)
- [3] https://code.visualstudio.com/docs/cpp/cpp-debug
無理に「車輪の再発明」をする必要は無い

「車輪の再発明」とは
既にあるものを、無駄な時間を費やして作ってしまうことを皮肉る言葉

発展途上国で算数が好きな男性が、何十年もかけて渾身の思いで「世紀の大発見」だと思ってアメリカの大学の数学会に提出したところ
既に100年以上前に考案された「2次方程式」の解の公式だったという話もどこかで呼んだことがあるように

まずはGoogle検索が第一です

それと最近のCPUは高性能なので

* SeleniumIDE
のXPathを学習

* MicrosoftOffice
のマクロ(VBA)を学習

* PHPフレームワーク
に素直に従う

ほうが

*  CPUやメモリのアドレスの「ビッグエンディアン」「リトルエンディアン」
*  厳密な型宣言「int」「char」のある旧JavaやC言語
*  基本情報技術者試験にも出てくる「2進数の暗算」

から始めてしまったばっかりに、挫折してしまうよりも
よっぽどハードルが低くて、実用的とまで言えます

老害おじさんたちが楽な方法を嘲笑ったり、批判したりするのは「自分の仕事が奪われるのが怖いから」でもあります

※ただし著作権には気をつけましょう
迫真Twitter部「プロモーションする」気が無い人のための裏技

ツイッターがイーロン・マスク氏に買収されてから
無料会員に「お前も有名になりたいよな？金払って♡（提案）」が鬱陶しくなったのでまとめました

既に書いている人がいたらごめんなさい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275132/d79cfa78-fe77-52fc-f881-28561fa07bde.png)

※いや、普通に居たわ

で、最終的に私はこの記述で落ち着きました

”プロモーションする”

がいちいち自分のツイートの下部に表示されて鬱陶しい人はABPアドオン(拡張機能)入れて
```
https://twitter.com/##a[href*="/quick_promote_web/intro"]'
```
と登録すればOK

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/275132/e01f8b22-3351-fd83-8da9-46643bb5fbf2.png)

Twiter Webでも「お金出して♡（提案）」が表示されなくなりました 

以上

参考サイト

https://amekujira.seesaa.net/article/270984656.html
Djangoで作成したアプリケーションにあらかじめユーザーを登録する

# はじめに

Djangoで作成したアプリケーションはEmailアドレスをIDとして、LDAPで認証する仕組みにしています。

ユーザーIDはあらかじめ分かっているわけですが、アプリケーション内部で許可権限をグループなどのユーザーリストで管理したい場合、デフォルトの管理画面(django.contrib.admin)で追加できるのはDjangoにログインして、DB上に登録されているユーザーIDのみです。

つまり最初にログインしてもらってから、権限付与の作業をしていたのですが、まぁUXの観点からはログイン時点で適切な権限が付与されていないと当然まずいわけで、あらかじめユーザーIDを登録しておきたいと思いました。

アプリケーション上で利用したい主なユーザーグループはLDAPのGroupで管理しているメンバーと同一なので、バルクでユーザーIDの登録だけを行う機能を追加しました。

# 環境

* Django 3.2.18
* requests 2.28.2

requestsはLDAPクライアントではなくREST APIを通してグループリストがダウンロードできるようになっているので、そのAPIにアクセスするためのクライアントライブラリです。

# 実装のポイント

個別のケースで再利用ができる、できないなどがあると思うので、ユーザーIDをDjangoのDBに登録するための考慮点をまとめておきます。

## ユーザー追加用のUIを追加

superuserだけが利用するためのAppを追加しました。

* ./myapp/admintool/ ディレクトリ
* ./templates/admintool/list.html テンプレートファイル

myadpp/admintool/ 以下には、空の __init__.py ファイルの他は、views.py と urls.py だけを配置しています。

```python:myapp/admintool/urls.py
from django.urls import path, include
from .views import AdminToolView

urlpatterns = [
    path('', AdminToolView.as_view(), name='admintool_usermanage')
]
```


## ユーザーID登録の基本的なコード

views.pyに追加するコードは次のような内容です。

```python:myapp/admintool/views.pyからの抜粋
from myapp.authenticate.models import AuthUser
from django.views.generic import ListView

from django.contrib.auth import get_user_model
from django.urls import reverse_lazy

class AdminToolView(ListView):
  model = GroupMembers
  template_name = "admintool/list.html"
  def dispatch(self, request, *args, **kwargs):
    if request.user.is_superuser:
        ...
        pass
    else:
        return redirect(reverse_lazy('top'))

    if request.method == "POST" and ... :
      glname = request.POST.getlist('glname')
      r = requests.get("https://....somewhere..../..rest_api../%s" % (glname))
      ...
      User = get_user_model()
      User.objects.create_user(email="%s" % (member_info['email']),
        family_name=member_info['name'].split(' ',1)[0],
        first_name=member_info['name'].split(' ',1)[1],gender='na',
        phone="%s" % (member_info['phone'].split(' ')[0]),
        password='hogehoge')
      ...
      return redirect(reverse_lazy('admintool_usermanage')) ## returning by GET method
    return super().dispatch(request, *args, **kwargs) ## when method != "POST"

```

テンプレート上のformからPOSTで渡されたグループ名を元にユーザーリストを抽出し、その数だけユーザーIDを追加するループを回します。

ここでget_user_model()から返されるのは、この環境では カスタム・ユーザー・モデルを定義しているmyapp.authenticate.models.AuthUser です。

直接呼び出してもget_user_model()経由で間接的に呼び出しても、最終的にはobjects.create_user()によってIDを登録することができます。

User.objects.filter()を利用すれば、登録済みかどうか判定することができます。

create_user()では自分がAuthUserに定義した要素を指定しますが、passwordがNoneオブジェクトのままだとエラーになります。LDAPで認証する際にはpasswordはハッシュ化された文字列が保存されるものの利用されませんのでダミーの文字列(実際には適切な乱数から生成した文字列)を指定します。

適当なユーザーIDのリストをrequests経由で取得してコードを反映しています。

これぐらいの操作で、あらかじめDBに登録しておきたいユーザーを追加することができました。
Pyodbcを使用したデータベースへの接続方法

# PythonでSQLServerへ接続

業務の中でPythonを使用してSQLServerへ接続し、データを操作する簡単なスクリプトを作成する必要があったため、その備忘録として接続方法を残します。

## 1. Pyodbcのインストール

```
pip install pyodbc
```
## 2. データベースに接続

Pyodbcを使用してデータベースに接続するには、以下の手順を実行します。

### インポート
Pyodbcモジュールをインポートします。

```python
import pyodbc
```
### 接続文字列の作成

データベースに接続するために必要な接続文字列を作成します。接続文字列は、データベースの種類、サーバー名、データベース名、認証情報などの情報を含みます。


```python
# 例（SQL Serverの場合）
connection_string = (
    r"DRIVER={ODBC Driver 17 for SQL Server};"
    r"SERVER=your_server_name;"
    r"DATABASE=your_database_name;"
    r"UID=your_username;"
    r"PWD=your_password;"
)
```
### データベースへの接続

接続文字列を使用してデータベースに接続します。

```python
connection = pyodbc.connect(connection_string)
```
## 3. データベース操作

データベースに接続した後、SQLクエリを実行してデータベースを操作できます。例として、データの取得や挿入を示します。

### データの取得

```python
cursor = connection.cursor()
cursor.execute("SELECT * FROM your_table_name")

rows = cursor.fetchall()
for row in rows:
    print(row)
```
### データの挿入

```python
cursor = connection.cursor()
cursor.execute("INSERT INTO your_table_name ('column1', 'column2') VALUES ('value1', 'value2')")
```

### 変更を確定させる
```python
connection.commit()
```
## 4. データベース接続の終了

データベース操作が完了したら、接続を閉じます。

```python
connection.close()
```
AOS.jsをサイズ変更に対応させる

# 画面幅に応じて発火タイミングを変えたい。
webページのスクロールをトリガーとしてふわっと表示させる演出が簡単に実装できて便利なAOS.jsですが、レスポンシブ対応などでレイアウトが変わると、意図していないタイミングで発火してしまう事がありました。
# data-aos-offsetを使う。
発火タイミングのコントロールには対象要素にdata-aos-offsetを設置します。（デフォルトは120）
このパラメータで該当する要素が、画面の下端からどれくらいスクロールしたら発火するかを変更できます。
```html
<h1 id="target" data-aos="fade-up" data-aos-offset="300">ふわっと表示したい！</h1>
```
この値をjavascriptなどで制御すればいいのですが、パラメータを変更するだけでは反映されずに、いちどAOS.jsを初期化する必要があります。
```javascript
//リサイズ時にaos.jsの発火タイミング変更
$(window).on('road', 'resize') function (){
  if($(window).width() < 1200){
    $('#target').attr('data-aos-offset', '-500');
  }else{
    $('#target').attr('data-aos-offset', '120');
}
AOS.init(); //ここでdataの変更を読み込ませます
}
```
ターミナルから手軽にSSH接続！ssh,sftpコマンドのチートシート

# 動作確認環境

macOS Ventura
macOS Monteray

macbook air M2
iMac late2017 Intel Core i7

# 接続

```
$ ssh ユーザ名@ホスト名
```

すると、パスワードを聞かれるので、入力。コピペでもいける。

```
ユーザ名@ホスト名's password:パスワードを入力
```

接続できると、接続できたよーというメッセージが表示される。


# ディレクトリ移動などの基本コマンド

`cd , ls , pwd , chmod`のような基本のコマンドはそのまま使える。
これらのコマンドをそのまま打つと、**接続先(サーバ)上で**`cd , ls , pwd , chmod`などのコマンドとして動く。

# ダウンロード/アップロードはscpかsftpを使う
sshコマンドではscpを使うか、sftpを使う。
このコマンドが使えない場合は、sftpコマンドで繋ぎ直す必要があるので、このscpコマンドの見出しは飛ばして、次のsftpで繋ぐ方法を見よう。

## scpコマンドでダウンロード/アップロードする

scpコマンドでダウンロード

```
scp -r [ユーザー名]@[ホストアドレス]:[リモートのファイルパス] [ローカルの保存先パス]
```

scpコマンドでアップロード

```
scp [ローカルのファイルパス] [ユーザー名]@[ホストアドレス]:[リモートの保存先パス]
```



## sftpでファイル、フォルダをダウンロード
scpコマンドで、もしNo such file or directory エラーが出る場合、scpコマンドにリモートサーバーが対応していないためにこのエラーが出るということもある。
その場合はsshコマンドでの接続を一度exitで切って、sftpコマンドで繋いで作業する。

以下のように一度sftpコマンドで接続する。

```
$ sftp ユーザ名@ホスト名
```

一度、sshの時同様にパスワードを聞かれるので、入力すると接続が完了する。

接続ができたら、そのあとにダウンロードコマンドやアップロードコマンドを実行する。

ファイルをダウンロードする場合は以下の通り。

```
sftp > get ファイル名
```

フォルダをダウンロードするときは`-r`のオプションが必要。

```
sftp > get -r フォルダ名
```

※ダウンロードしたフォルダやファイルはユーザディレクトリ直下にダウンロードされるので注意。
(なぜかダウンロードフォルダにはダウンロードされない。。。)

sftpでファイルをアップロードする場合は以下の通り。

```
sftp > put ファイル名
```

フォルダをアップロードするときは`-r`のオプションが必要。

```
sftp > put -r フォルダ名
```

※ローカルのカレントディレクトリからの相対パスでも、フルパスでもどちらでもいける。
※ローカルのカレントディレクトリがどこかわからない時は、`lpwd`コマンドで確認できる

# ファイル、フォルダを削除
ファイルを削除する場合は以下の通り。

```
rm ファイルパス
```

フォルダを削除するときは`dir`が必要。

```
rmdir フォルダのパス
``` 


# フォルダを作る

```
mkdir ディレクトリ名
```

# ファイルのコピーを作る

```
cp コピー元 コピー先
```

# zipフォルダの作成

zipファイルを作成
 
```
zip -r 作成したいzipフォルダ名 zip化対象フォルダのパス
```
※基本的にはフォルダは複数のファイルを含むので、-rオプションをつける。

# zipファイル解凍

```
unzip -r 解凍フォルダパス
```
Rakeのspecはシンプルに書きたい

# 概要
Rakeのspecについて実行方法をググると後述する冗長な書き方（×今までのよくある書き方）しか出てこず、自社のサービスも全て同じ書き方がされていてムズムズしたので
Rakeのspecをシンプルに書くためのヘルパークラス`rake_helper.rb`を作成しました。
このヘルパークラスを使用することにより、Rakeタスクのspecのコードがシンプルになり、テストスイートの実行時間が多少短縮されるかも。

# ヘルパークラス
```ruby:spec/rake_helper.rb
# frozen_string_literal: true

RSpec.configure do |config|
  # Load all Rake task files once into your Rails application
  config.before(:suite) do
    Rails.application.load_tasks
  end

  # Reset all Rake tasks so that the same task can be run multiple times
  config.before do
    Rake.application.tasks.each(&:reenable)
  end
end
```

# 使い方
## ×今までのよくある書き方
```ruby
require 'rails_helper'

# rubocop:disable RSpec/BeforeAfterAll, RSpec/InstanceVariable
describe 'ClassName' do
  before(:all) do
    @rake = Rake::Application.new
    Rake.application = @rake
    Rake.application.rake_require('task_name', [Rails.root.join('lib/tasks').to_s], [])
    Rake::Task.define_task(:environment)
  end

  before(:each) { @rake['task_name'].reenable }
  # test
  # Run with something like "@rake['task_name'].invoke"
end
# rubocop:enable RSpec/BeforeAfterAll, RSpec/InstanceVariable
```

## ○ヘルパークラスを用いた書き方
```ruby
require 'rails_helper'
require 'rake_helper' # Just add this line!

  # test
  # Run with something like "Rake.application['task_name'].invoke"
```

# つまずいたところ
ローカル環境に`rake_helper.rb`を用意し、一部の既存specで利用するよう変更したら正常に実行完了しましたが、
GitHub Actionsのspecではランタイムエラー（Don't know how to build task）が発生しました。
原因は、`rake_helper.rb`の`:suite`で`Rails.application`に全タスクをロードした後、
既存specがタスクを1件だけロードして上書き（[×今までのよくある書き方](https://qiita.com/melty_go/items/31366ecca00e8e1d0667#%E4%BB%8A%E3%81%BE%E3%81%A7%E3%81%AE%E3%82%88%E3%81%8F%E3%81%82%E3%82%8B%E6%9B%B8%E3%81%8D%E6%96%B9)）していたからです。
対応として、上記に該当する全ての既存specで`rake_helper.rb`を利用するよう変更したら解決しました。

# ぼやき
- `rake_helper.rb`内で`require 'rails_helper'`をしたら呼出し側Rakeのspecの`require 'rails_helper'`を減らせる（1行にできる）かと思いましたが、ヘルパークラスはそれぞれ1つの責任を持っているので分割した。が、今思えばやっても大きな問題にはならなさそう。
- 逆に`rails_helper.rb`内で`require 'rake_helper'`することもできましたが、それをするとRake以外のspecでも`rake_helper`が無駄に実行されてしまう。その場合、`type: :rake`のときだけ`before`を実行するようなロジックが必要となり、呼出し側Rakeのspecの全実行箇所に`(type: :rake)`を追記することになるので冗長かなと思ってやめました。
【ドラゴンボール風解説】リージョンについて

# はじめに
この記事はAWSの説明は今ひとつよくわからないのでドラゴンボールに例えて説明してほしい。
というAWS初学者向けの記事です。
※投稿記事作成にはchatGPTを使用しております

# リージョン 
オッス、オラ悟空！AWSのリージョンはな、ドラゴンボールの世界にある様々な惑星にたとえられるんだ！**北米のリージョンは地球みたいな惑星で、アジアのリージョンはナメック星みたいな感じだ**。それぞれの惑星には、いろんな人や生き物がいて、それぞれに個性や特徴があるんだぜ。

そしてな、**リージョン内にはAZ（Availability Zone）ってのがあって**、これは惑星の地域にたとえられるんだ。例えば、地球みたいな惑星には西の都、北の都、東の都とかいろんな町があるだろ？同じように、**リージョン内にも複数のAZがあって、それぞれに独自の電源やネットワークが用意されてるんだ**。

これらのリージョンやAZが、AWSのサービスが世界中で利用されるための基盤となっているんだぜ！オラもAWSを使って、いろんな惑星に飛んで冒険しているんだ！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3304054/5d84b061-1fb9-bd80-39fa-7be12d4c8e86.png)
WBC休暇届Generator

昨日、WBC決勝戦の為、MLBJapanが休暇届を発信したことが、話題になっていました。
```
https://twitter.com/MLBJapan/status/1638099375657476096
```
名前欄が「＿＿＿＿（あなたの名前）様」となっていて、そのままでは使えないので、名前を埋め込むページを作ってみました。

ページは下記
https://aeha.github.io/dayoff4wbs/
Javascriptなので、ブラウザで動作します。

ソース (base.jpegは、MLBJapan公開の画像)
```HTML
<!DOCTYPE html>
<html>
<head>
	<title>WBC休暇届を生成</title>
</head>
<body>
	<label for="text-input">テキスト:</label>
	<input type="text" id="text-input" placeholder="お名前を入れて">
	<button onclick="draw()">描画する</button><br>
	<canvas id="canvas" width="1350" height="1080"></canvas>
	<script>
		// 画像のパス
		var imagePath = "base.jpeg";

		// 画像の読み込み
		var image = new Image();
		image.src = imagePath;

		// canvas要素の取得
		var canvas = document.getElementById("canvas");
		var context = canvas.getContext("2d");

		// 描画関数
		function draw() {
			// 画像を描画
			context.drawImage(image, 0, 0);

			// テキストを取得
			var text = document.getElementById("text-input").value;

			// 文字列を描画
			context.font = "30px sans-serif";
			context.fillStyle = "white";
			context.fillText(text, 100, 640); // X座標: 100, Y座標: 100

			// 画像と文字列を合成
			var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
			context.putImageData(imageData, 0, 0);
		}
	</script>
</body>
</html>
```
Railsで個人的に参考になった記事をただ纏めた記事


## 其ノ壱
RailsにとってActiveRecordは強力なライブラリだが、その付き合い方を学べる記事。

https://tech.smarthr.jp/entry/2021/11/11/151444

## 其ノ弐
`ActiveSupport::Callback`について学べる。CallbackはModelが汚れたりテストがしにくくなったりするので是非読んでおきたい。


https://qiita.com/mitanih/items/a0f2a41766ad61ab491f

https://qiita.com/luccafort/items/69e943b26ef6890b8a30

## 其ノ参
Railsでの大量データの取り扱いについて学べる。

https://techblog.lclco.com/entry/2019/07/31/180000

## 番外編
### API
https://rooter.jp/programming/rails-activemodelserializers/

https://tech.timee.co.jp/entry/2020/07/05/150312

https://qiita.com/nakazawaken1/items/1cf12756a9e00f1a8fc4

### SOLID原則
https://techracho.bpsinc.jp/hachi8833/2018_03_27/54130
local環境をスマホ実機で確認する方法

こんにちは。
開発しててlocal環境をスマホ実機でちゃんと確認したいなぁってとき、ありますよね。
やっとそれを実現したので、自分の備忘録も兼ねて残します。

## 実現したいこと
開発中のlocal環境を手元にあるスマホ実機で確認する

## 手順
### １｜スマホとPCを同じwifiに接続
なんかわかんないけど、必要

### ２｜PCに接続しているネットワークのIPアドレスを確認する
MacOSの場合、``システム環境設定 > ネットワーク`` から接続済みのネットワーク情報を確認できる
設定されているIPアドレスをコピー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2940151/849e7fe1-d540-c478-6ea6-e04e163cd524.png)

### ３｜スマホのブラウザのURL欄に先ほどのIPアドレスを入力する
必要に応じて、```192.xxx.xx.xx:3000```など、末尾にポート情報を追記

以上。
えーこれだけかってびっくりです。
前にやった別の方法ができなくて一旦諦めてたのですが、
こんなシンプルな方法でできました。

ただこれ、手元にある同じwifiに接続しているスマホだけですよね
開発メンバーに一旦local環境で共有したい...ってときは無理なので、その辺もうちょっと調べます。
（前にやったけどこれもできなくて一旦断念した...）