OmniAICreator/Qiita-1.07M huggingface dataset:
CloudWatchAgentAdminPolicyとCloudWatchAgentServerPolicyの違いを調べた

CloudWatchへログやメトリクスを送信するため、AWSリソースに（のロールに）IAMポリシーを付与する必要がありますが、CloudWatchAgentAdminPolicyとCloudWatchAgentServerPolicyという似たようなやつがあり、その違いを調べてみた。

# 結論から言うと
・CloudWatchAgentAdminPolicyはCloudWatchAgentServerPolicyより権限が大きく、**ssm:PutParameter**が許可される
・最初の何かのセットアップ時に（何かの設定ファイルの書き込み？）、**ssm:PutParameter**が必要なので、CloudWatchAgentAdminPolicyが必要となり、
　運用時はもう**ssm:PutParameter**が要らないらしく、**cloudwatch:PutMetricData**と**logs:PutLogEvents**のあるCloudWatchAgentServerPolicyだけでOKとのことでした。

## CloudWatchAgentAdminPolicyの中身
```ruby:CloudWatchAgentAdminPolicy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:PutMetricData",
                "ec2:DescribeTags",
                "logs:PutLogEvents",
                "logs:DescribeLogStreams",
                "logs:DescribeLogGroups",
                "logs:CreateLogStream",
                "logs:CreateLogGroup"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:GetParameter",
                "ssm:PutParameter"#←ここだけ違う
            ],
            "Resource": "arn:aws:ssm:*:*:parameter/AmazonCloudWatch-*"
        }
    ]
}
```

## CloudWatchAgentServerPolicyの中身
```ruby:CloudWatchAgentServerPolicy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:PutMetricData",
                "ec2:DescribeVolumes",
                "ec2:DescribeTags",
                "logs:PutLogEvents",
                "logs:DescribeLogStreams",
                "logs:DescribeLogGroups",
                "logs:CreateLogStream",
                "logs:CreateLogGroup"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:GetParameter"
            ],
            "Resource": "arn:aws:ssm:*:*:parameter/AmazonCloudWatch-*"
        }
    ]
}
```
Next.jsでNotionAPIにアクセスしたときにauthorを読み取れずにエラーが出る問題

## 問題

![スクリーンショット 2023-10-02 11.42.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/f59bab7d-cb54-ae7c-4b37-057fbbcfd97f.png)


`npm run dev`したときにこういうエラーが出たらおそらく対象者です。

```
Error: Error serializing `.articles[0].author.name` returned from `getStaticProps` in "/".
Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
```

## 解決策

![スクリーンショット 2023-10-02 11.43.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628782/f3a2f345-451b-c6ff-a1da-e60f97caaf0e.png)

Notionのインテグレーションの設定を「メールアドレスなしでユーザー情報を読み取る」に変更すると解決します。

## 参考

https://github.com/tuanphungcz/nextjs-notion-blog-starter/issues/8
[Python] Exception継承でエラーに応じてメッセージやステータスコードを使い分ける方法

## 初めに
エラーハンドリングは、エラーの内容ごとに異なる処理を行う必要があります。例えば、ステータスコードやエラーコードは内容ごとに出力します。
今回は、デフォルトの場合とタイムアウトの場合に、特定のエラーコードとメッセージを出力させるようにしました。

## カスタムクラスの定義
```python
class CustomExceptionBase(Exception):
    STATUS_CODE: int = None
    DEFAULT_MESSAGE: str = "デフォルトメッセージです"

    def __init__(self, msg=None, *args, **kwargs):
        if not msg:
            msg = self.DEFAULT_MESSAGE_CODE

        super().__init__(msg, *args, **kwargs)


class TimeOutException(CustomExceptionBase):
    STATUS_CODE = 403
    DEFAULT_MESSAGE = "Time out error!!"
```

まず、```CustomExceptionBase```クラスに```Exception```クラスを継承させることで、カスタム例外クラスの基底クラスとして定義します。
サブクラスの```TimeOutException```クラスに基底クラスを継承して属性をオーバーライドすることで、タイムアウトエラー時にSTATUS_CODEを```403```、DEFAULT_MESSAGEが```Time out error!!```と出力することができます。

## 使用例
```python

try:
    ...

    try:
        requests.get("hoge/hoge")
    except requests.exceptions.Timeout:
        raise TimeOutException()

except CustomExceptionBase as e:
    return {
            "statusCode": e.STATUS_CODE,
            "body": "message": str(e),
            "headers": hogehoge,
        }

```
1. ```get```関数実行時のタイムアウトエラーの場合、```raise``` 
で```TimeOutException```クラスを実行します。
2. STATUS_CODEとDEFAULT_MESSAGE_CODEがオーバーライドされます
3. 最終的にraiseをexceptでキャッチします。```e.STATUS_CODE```でSTATUS_CODE属性を使用し、```str(e)```で設定したエラーメッセージを使用しています。
オブジェクトフィールドレベルセキュリティの移行について

オブジェクトフィールドレベルセキュリティの移行について簡単にまとめておく

### 移行元

１．該当するプロファイルのIDを確定する。

```
SELECT Id FROM profile WHERE name = 'システム管理者'
```

２．Dataloaderで対象プロファイルと対象オブジェクトのフィールドレベルセキュリティをエクスポートする。
```
SELECT ParentId, Parent.profileid, Parent.profile.name, SobjectType, Field, PermissionsEdit, PermissionsRead FROM FieldPermissions where SobjectType in ({対象オブジェクトリスト}) and Parent.profileid in ({プロファイルのId})
```



### 移行先

１．該当するプロファイルのIDを確定する。

```
SELECT Id FROM profile WHERE name = 'システム管理者'
```

２．該当する権限セットのIDを確定する。
```
SELECT Id FROM PermissionSet WHERE ProfileId = '{プロファイルのId}'
```

３．移行元２のエクスポートファイルをテキストエディタで開いて、ParentIdを２で取得したIdで置換する。

４．DataloaderでInsertする。
WPF ScrollViewerに配置したボタンをタッチ操作でもアニメーションさせたい

ScrollViewer内にボタンを配置しタッチ操作を行うと、何故かトリガーに設定したIsPressedを拾ってくれない。
ちょっと調べたところ、詳しい事は分からなかったがタッチイベントが途中でハンドルされてるっぽい。多分。
Stylusのイベントは発生している様なのでEventTriggerに設定して
PreviewStylusDown(StylusDownでも可)をStoryboardのアニメーション開始に設定
StylusUpとStylusLeaveをアニメーション停止に設定。
StylusUpだけだとボタンをホールドしたままスクロール移動すると
上手く停止してくれないのでStylusLeaveも設定する。
テンプレートやトリガーの中身を弄れば色んなボタンで応用可能だと思います。
```xml:PressableButtonStyle.xml
<Style x:Key="PressableButtonStyle" TargetType="Button">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="Button">
                        <Grid>
                            <Border Name="border_under" Background="{TemplateBinding BorderBrush}"/>
                            <Border Name="border_top" Background="{TemplateBinding Background}">
                                <Border.RenderTransform>
                                    <TransformGroup>
                                        <TranslateTransform X="0" Y="-3" />
                                    </TransformGroup>
                                </Border.RenderTransform>
                                <ContentPresenter Content="{TemplateBinding Content}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
                            </Border>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <EventTrigger RoutedEvent="Border.PreviewStylusDown" SourceName="border_top">
                                <BeginStoryboard  Name="beginStoryboard">
                                    <Storyboard >
                                        <DoubleAnimation 
                                                          Storyboard.TargetName="border_top" 
                                                          Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(TranslateTransform.Y)"
                                                          Duration="0:0:0:0" From="-3" To="0" />
                                    </Storyboard>
                                </BeginStoryboard>

                            </EventTrigger>
                            <EventTrigger RoutedEvent="Border.StylusUp" SourceName="border_top">
                                <RemoveStoryboard BeginStoryboardName="beginStoryboard"/>
                            </EventTrigger>
                            <EventTrigger RoutedEvent="Border.StylusLeave" SourceName="border_top">
                                <RemoveStoryboard BeginStoryboardName="beginStoryboard"/>
                            </EventTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
```
踏み台サーバーについて

業務を通して、以前踏み台サーバーについて再確認することがあったので簡単に情報共有をしていきます。

## 踏み台サーバーとは
「中継地点サーバのこと」
です。サーバーを経由してサーバーに接続するということなのですが、少し詳しく話していきます。

## 踏み台サーバーについて
そもそもサーバーとは、「サービスや機能を提供する側のコンピュータ」です。
  
そこに対して色んな作業をする（アクセスする）訳なのですが、他のサーバにアクセスする際の中継地点になるサーバーが「踏み台サーバ」です。
ジャンプサーバーとも呼ばれています。

Aサーバー、Bサーバー、Cサーバーという3つのサーバーがあったとします。
CサーバーへのアクセスはBサーバーからしか許されていないとします。
あなたのサーバーがAサーバーだとして、Cサーバーにアクセスするには、一度Bサーバーにアクセスし、そこからCサーバーにアクセスする必要があるわけです。

このとき、Bサーバーは踏み台サーバーと言えます。

## 踏み台サーバーを使う理由
踏み台サーバを使う主な理由はセキュリティです。

目的のサーバにアクセスするには踏み台サーバを経由する必要があります。
踏み台サーバに接続できる人を制限することで、目的のサーバにアクセスできる人も制限できます。
踏み台サーバを止めてしまえば、目的のサーバに誰もアクセスできなくなるわけです。
上記のようなメリットのほかにも、環境を統一することができるというメリットもあります。

踏み台サーバーを利用するときはそれぞれのサーバーへのポートの割り当てを行って扱うため、ポートがごちゃごちゃにならないように気をつけましょう。
Vagrant `config.ssh.insert_key` 関連の備忘録

Windows上でVagrantfileの `config.ssh.insert_key` を変更した時の挙動を整理しました。

## 一覧まとめ

| 設定値 | `.\.vagrant\machines\default\virtualbox\private_key` | Git BashからのSSH | Git Bash以外からのSSH |
|:---:| --- | --- | --- |
| true(規定値) | 作られる | できる | できない（[※1](#補足)） |
| false | 作られない | できる | できる |

## 補足

### `config.ssh.insert_key=true` の場合に、Git Bash以外からSSH接続できない問題の回避方法

`.\.vagrant\machines\default\virtualbox\private_key` について、以下のように権限をログインユーザーの読取可のみに限定することで、Git Bash以外からのSSHもできるようになりました。

1. エクスプローラーからファイルのプロパティを選択
1. 「セキュリティ」タブの「詳細設定」を選択
1. 「継承の無効化」をクリック → 「このオブジェクトから継承されたアクセス許可をすべて削除します。」を選択
1. 「追加」をクリック
1. 「プリンシパルの選択」をクリック
1. 「選択するオブジェクト名を入力してください」にログインユーザー名を入力 → 「名前の確認」をクリック →  「OK」をクリック
1. 「基本のアクセス許可」で「読み取り」のみOnにして「OK」をクリック
1. 「private_key のセキュリティの詳細設定」で「OK」をクリック
1. 「private_key のプロパティ」で「OK」をクリック

- コマンドプロンプト（管理者権限）からコマンドで実行する場合

```bat
icacls .\.vagrant\machines\default\virtualbox\private_key /inheritancelevel:r /grant:r <ユーザー名>:R
```

別のパスに秘密鍵をコピー等し、そちらを参照する場合は、`vagrant ssh -- -i "<秘密鍵のパス>"` でログインできます。
vitest-openapiというjest-openapiのマッチャーをVitestで利用可能にするライブラリを公開した話

## はじめに

Jestがテストライブラリとしてはメジャーで多く利用されていると思うが、ES Moduleとの相性でいうとゼロコンフィグとはいかず歯がゆい感じもある。そこで私個人としてはVitestを利用してテストを実行することが多くなってきた。

ただ、[jest-openapi](https://github.com/openapi-library/OpenAPIValidators/tree/master/packages/jest-openapi)という便利な機能は利用したいなと思った。今回はVitestの[Extending Matchers](https://vitest.dev/guide/extending-matchers.html)の機能を利用してVitesにおいてjest-openapiのマッチャーを利用できるようにするライブラリを作成する事にした。

今回はその実装時に少しハマったことなどを備忘録として残す。

※ちなみに、JestにおけるESMサポートは実験的である

https://jestjs.io/ja/docs/ecmascript-modules

## 作成したライブラリについて

https://www.npmjs.com/package/vitest-openapi

上記のライブラリ自体はかなり単純で、`expect.extend()`を利用してjest-openapiのマッチャーをVitestでも利用可能にしたもの。

以降の章で、実装するあたっていくつかハマったポイントがあったので1つずつ取り上げたいと思う。

## ハマったポイント
### This expression is not callable.

TypeScriptでライブラリを実装した際に、以下のようなエラーが出てコンパイルエラーになった。

```
This expression is not callable.
  Type 'typeof import("/home/study/workspace/vitest-openapi/node_modules/jest-openapi/dist/matchers/toSatisfyApiSpec")' has no call signatures.ts(2349)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1372684/00787d42-32ba-792d-68f8-5f2a5855feeb.png)

このエラー自体は以前の[記事](https://qiita.com/yuta-katayama-23/items/0a2e6df10ce139d1b647)で取り上げた内容と同じで、CommonJSのTypeScriptプロジェクトでdefault exportでモジュールをexportしているものを、ES ModuleのTypeScriptプロジェクトでimportしようとすると起こるエラー。このコンパイルエラーは`"esModuleInterop": true`を利用しても解消できない。

詰んだか…と思ったが、冷静になってdefault exportしているライブラリの実装を確認してみると、以下のように`exports.default`になっていることが確認できる。

```js:jest-openapi/dist/matchers/toSatisfyApiSpec.js
function default_1(received, openApiSpec) {
    ...
}
exports.default = default_1;
```

つまり、`default`という名前で関数がexportされているだけなので、以下のように`.default()`という実装をすればコンパイルエラーにはならなくなる（若干実装として気持ち悪い感じはするが）。

```ts
import jestToSatisfyApiSpec from 'jest-openapi/dist/matchers/toSatisfyApiSpec.js';

...

export default (filepathOrObject: string | OpenAPISpecObject): void => {
	const openApiSpec = openapiValidator.makeApiSpec(filepathOrObject);

	expect.extend({
		toSatisfyApiSpec: (received: unknown) => jestToSatisfyApiSpec.default(received, openApiSpec)
	});
};
```

### VitestはCommonJSに対応していない

当初、ドュアルパッケージ（ES ModuleとCommonJSの両方で利用できるパッケージ）を開発しようと考えていたが、VitestはCommonJSには対応していないようなので中止した。理由は、Vitestの`index.cjs`は中身のない空ファイルになっており、`const { expect } = require("vitest");`のようなことができない状態だったため。

https://github.com/vitest-dev/vitest/blob/main/packages/vitest/index.cjs

どうしてこんな事になっているのか？の詳細は不明だが、どうやらTypeScriptに対応させる上での対応でこうなっているらしい。
VeeValidateを使ってカスタムルールを作成する方法（v3系）

## 初めに
カスタムルールはコンポーネントごとに定義するのではなく、使い回すことで工数やメンテナンス性を向上させることが基本です。
今回はバリデーションを特定のファイルで宣言し、各コンポーネントで使用できるようにしました。

## ルールの適用
main.jsでvee-validateから```defineRule```と```configure```をimportします

```js
import { defineRule, configure } from "vee-validate";

```
インポートしたすべてのルールを使用可能にするためにdefineRuleを定義
```js

Object.keys(AllRules).forEach((rule) => {
  if (rule === "default") {
    return;
  }
  defineRule(rule, AllRules[rule]);
});
```
AllRulesオブジェクトの各キー（各バリデーションルールの名前）を取得し、それぞれのルールをdefineRule関数を使って定義しています。ただし、"default"という名前のルールを定義するとエラーになるので除く必要がある。なぜエラーになるのか引き続き調査する

## defineRuleにカスタムルールを定義
特定のルールの作成はオブジェクトとして変数に格納する。複数作成の場合はカンマ区切り
```js
const customRules = {
  fullWidthChar: (value) =>
    !/^([\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana}]|\u30FC)*$/u.test(
      value
    )
      ? "全角の漢字、ひらがな、カタカナ、ハイフンを入力してください"
      : true,
  alphaNumOrFullWidthChar: (value) =>
    !/^([A-Za-z0-9-\x20]|\u30FC|\p{Script=Han}|\p{Script=Hiragana}|\p{Script=Katakana})*$/u.test(
      value
    )
      ? "半角英数字（ハイフン・スペース含む）または全角（漢字・ひらがな・カタカナ・ハイフン）を入力してください"
      : true,
}
```
defineRuleを使って、vee-validateにカスタムルールを定義
```js
for (const [ruleName, callback] of Object.entries(customRules)) {
  defineRule(ruleName, callback);
}
```
この関数は、第一引数としてルールの名前、第二引数として実際のバリデーションロジックを持った関数を受け取り、そのルールを定義している。
customRulesオブジェクトのエントリ（[key, value]のペア）を取得し、後続のfor-ofループで、オブジェクトの各エントリに対して処理をしている

## メッセージのローカライズ
importしたconfigureで各ルールのメッセージを設定できる
```js
configure({
  generateMessage: localize("ja", {
    fields: {
      inputAmount: {
        max_value: "残高が不足しています",
      },
    },
    names: {
      inputNumber: "クレジットカード番号",
      inputExp: "有効期限",
    },
    messages: {
      required: "{field}は必須項目です",
      min_value: "0:{min}より大きい値を入力してください",
    },
  }),
});
```
### names
keyにname属性、valueに項目の説明を記述することでlabelを設定できる

### messages
vee-validateのデフォルトルールを上書きしたい場合は、keyにルール名、valueにエラーメッセージを記述する

### fields
エラーメッセージは定義済だか、特定のフィールドにだけ異なるエラーメッセージを表示したい場合に必要。
inputAmountはnamee属性、max_valueはルール名でvalueにメッセージを記述して上書きしている

## 使用方法
```rule```に定義したバリデーションnameを設定するだけです。
customRulesに定義した```alphaNumOrFullWidthChar```の使用例は以下になります。

```javascript
 <Field
      ...
      name="inputAddress"
      rules="alphaNumOrFullWidthChar"
    />
```

<Form> コンポーネントには```errors```オブジェクトが含まれており、これにはフォーム内の各フィールドから発生したバリデーションエラーがすべて含まれています。
例えば、フィールドがその属性で指定されたバリデーション基準を満たさない場合、そのフィールド名をキーとするエラーメッセージが```errors```オブジェクトに追加されます。

また、フィールドに関連するエラーメッセージは```errors.フィールド名```を使用してアクセスできます。
例えば、フィールド名が "username" の場合、対応するエラーメッセージには ```errors.username```でアクセスします。


以下のように <Form> コンポーネント内で```v-slot```を使用し、```errors```オブジェクトを受け取ることで、これらのエラーメッセージにアクセスし、表示することができます：
```javascript
<Form ... v-slot="{ errors }">
  <!-- フォームの内容 -->
  <span>{{ errors.username }}</span> <!-- 例：'username' フィールドのエラーメッセージ -->
```

Formで@invalid-submitを使えばバリデーションに失敗したときにトリガーされます。
@submit イベントは、フォームが正常にバリデーションを通過したとき（すべてのフィールドが検証ルールを満たしたとき）にトリガーされます
EclipseのCheckStyleで【cannot initialize module JavadocMethod - Property 'scope' does not exist】

# 環境
| 名称 | バージョン |
|:-:|:-:|
| Eclipse | 4.28.0 |
| Checkstyleプラグイン | 10.7.0 |
| Spring Boot | 3.1.4 |

# 問題
Eclipseで、数年前に作成されたCheckstyleを適用したら、エラーになった。
```
Checkstyle execution failed due to an internal error.
```

Eclipseのログ※には以下が出力されていた。
※{ワークスペースのディレクトリ}/.metadata/.log
```.log
cannot initialize module TreeWalker - cannot initialize module JavadocMethod - Property 'scope' does not exist, please check the documentation
```


# 解決方法
`cannot initialize module JavadocMethod - Property 'scope' does not exist`と言われているのだから、scopeを定義している箇所を削除。
```sampleCheckstyle.xml
<module name="JavadocMethod">
  <!-- <property name="scope" value="public"/> -->
  <property name="allowMissingParamTags" value="true"/>
  <property name="allowMissingReturnTag" value="true"/>
  <property name="allowedAnnotations" value="Override, Test"/>
  <property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF"/></module>
```
# 参考
https://qiita.com/ponsuke0531/items/02d3761c83ec2e0681d0
シングルでもステレオCAMっぽくできないか

# ステレオカメラ使(わせてもら)えない

<details><summary>愚痴</summary>

:::note info
外様は触れないんですよ
- 自腹で買えと言う雰囲気をプンプンと感じる。
- 他のこともわりと自分でやっている・・・
- やれることやりますよ
:::

</details>

- さて大人の事情でつかえないので、どうしましょう

# 子供なりに考えてみる

1. 普通に買う
1. それっぽいカメラを買う
1. 一眼でやってみる

## 普通に買う

https://www.intel.co.jp/content/www/jp/ja/architecture-and-technology/realsense-overview.html

これは５万円ぐらいする・・・アリエクスプレスだとRMBかもしれん。こぇ・・・


## それっぽいカメラを買ってみる

https://store.opencv.ai/products/oak-d

- なんかよくわからないのだともうちょっと安くはなる。
- 完全に自分が使うという範疇を出ていないので投資に見合うだけのリターンが怪しい
- 大人の事情を超越するならありな選択肢

## 一眼でやってみる

- 有名なPINTO ZOOなどから引っ張ってくる・・・王道だけど、今回はパスかな
- 勉強中の身なので、それっぽい情報を集めて少し試してみる


# ちょっと試してみる

## Depth処理をしてみる

![Depth Heatmap_screenshot_30.09.2023.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155177/1e991518-5193-7b7c-073f-789d81050144.png)

- 距離などは測れないものの、何があるかわかるので障害物検知はできそう
- 障害物検知だとセンサーというのありですよね。（こちらもピン切りですが）


## Heat map を再現してみる
![Heatmap_screenshot_30.09.2023.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155177/a9fb7898-d88f-88db-1781-10e90faba75a.png)

- なんか一昔前のアートっぽいのになった。
- 実用性はともかくおもしろいかもしれないと思いました。

## 手持ちのカメラ２台同時使用 

![Matchingsokyo30.09.new.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2155177/1985c90b-836b-24f8-b598-b5eaf183e200.png)

:::note warn
注意
- 正確な結果を得るなら同じカメラが２台必要です。
- 本例ではターゲットの測距をやってみようとしています
- ２台のカメラの位置が一致するところが結ばれています（片方の露出が怪しいですが）
:::

https://github.com/mi-kaneyon/man-yard/tree/main/Utils

# ちゃんとしたものが一番ですね。やっぱり

:::note info
目標
- それでもローコスト、あわよくば手持ちの道具で測定したい
- 研究は続ける
:::

以上です
【学習メモ】CloudWatchの各機能について

こんにちは、以前AWS-SAAを取るときは暗記で勝負したため、多くの機能の理解は実はあやふやでした。。。

八月上旬弊社BeeXに入ったらいろいろ勉強機会を下さって本当にありがたく思います！

自分のためでもありますが、ほかのAWS初心者にも役に立てたらと思って勉強メモをどんどん投稿したいと思います！！！

# CloudWatchとは
・統合的な運用管理サービス
・AWSが提供するサービス（EC2やLambdaなど）だけじゃなく、外部サービスの監視（外形監視）もできる
・その下に、**CloudWatch-logs**、**CloudWatch-Metrics**、**CloudWatch-Alarm**、**CloudWatch-event**がある

## CloudWatch-logsとは
・ログの一元管理、検索。フィルタリングができる
・オンプレミスのサービスのログも取得できる

### CloudWatch-ロググループとは
・**ロググループ**で各ログを分けられる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/8de3d2c1-ec3e-bc3d-2de6-8ecb20a63d11.png)
・タイムスタンプで文字列でログを出力する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/5e9537eb-ec51-c87b-418e-35708a815333.png)

・保存期間は1日～10年、または失効しない（無制限）で指定できる


### CloudWatch-ログのインサイトとは
・英字の通り、「ログのインサイト」→「ログの中身」
・CloudWatch-logsをSQLみたいな言語でクエリし、必要な情報のみフィルタリングできる（全ログの中から目視で探すのは大変だたら。。。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/21d8395c-1b3f-6eab-763a-cbc966d52274.png)

## CloudWatch-Metricsとは
・特定の時期、特定のサービスを指定し、ヘルス情報やパフォーマンス情報、測定値を可視化・図視化する
・AWSの各サービスから自動取得するメトリクスがある（S3とか）
・オンプレミスのサービスのメトリクスも取得できる

## CloudWatch-Alarmとは
・メトリクスを選択し、アラームを作成できる
・事前に閾値を設定することで（以上・以下・より大きい・より小さい）、異常が発生するときにアラームを出すサービス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/9dc65f9c-675a-d57b-80fc-2c46de0fcc7f.png)

・アラームをトリガーにインシデント対応ができる（SNSで通知とか、EC2を終了とか）
　トリガーは三種類：ALARM（アラーム状態）、OK、INSUFFICIENT_DATA（データ不足）
　アクションは三種類：Auto Scaling アクション、EC2 アクション、Systems Manager アクション

例：ある期間内に（１分間）、あるメトリクス（HTTPリクエストの回数）が、）閾値（平均100回）を超過したことが、実行ポイント（5回）に達した場合、アラームを出す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/0017e85f-9198-d4ae-cfce-2b48eb5cc220.png)

## CloudWatch-Eventとは
・事前にイベントルールを設定することで、AWSリソースの状況を検知し、ルールにマッチングした場合（イベント発生）、アクションを実行する（lambda実行とか）
・**スケジュール式**（Rate式・Cron式）と**イベント式**（ルールにマッチしてイベント発火）がある
・AWS-EventBridgeとは上位互換、いまは基本CloudWatch-Eventをクリックすると**AWS-EventBridge**へ遷移するとなる（今後徐々にAWS-EventBridgeに移行するかな。。。）

:::note info
CloudWatch-EventとCloudWatch-Alarm actionの違い
stackoverflowからの引用ですが、、、
> Events can self-trigger based on a schedule; alarms don't do this.
Alarms invoke actions only for sustained changes.
Alarms watch a single metric and respond to changes in that metric; events can respond to actions (such as a lambda being created or some other change in your AWS environment).
Alarms can be added to CloudWatch dashboards, but events cannot.
Events are processed by targets, with many more options than the actions an alarm can trigger.
:::

# 参照サイト
https://qiita.com/MetricFire/items/f09157d114e74a04c92c#:~:text=%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%AF%E6%99%82%E9%96%93%E3%81%AE%E7%B5%8C%E9%81%8E,%E5%A0%B4%E5%90%88%E3%81%AB%E4%BD%BF%E7%94%A8%E3%81%95%E3%82%8C%E3%81%BE%E3%81%99%E3%80%82

https://stackoverflow.com/questions/58205306/what-is-the-difference-between-a-cloudwatch-alarm-and-a-cloudwatch-event
【Entra ID】サインインがないユーザーをリストで見える化

Microsoft Entra ID 上の使用されていないユーザーを棚卸できるように、Graph API 経由でサインイン日時を取得して SharePoint リストに出力する Logic Apps を作成しました 。


## Microsoft Graph API でサインイン日時を取得
Graph API でユーザーを取得する際、signInActivity を明示的に指定することでユーザーのサインイン日時を取得できます。


https://learn.microsoft.com/ja-jp/azure/active-directory/reports-monitoring/howto-manage-inactive-user-accounts#detect-inactive-user-accounts-with-microsoft-graph


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540101/239e3bb4-4807-b8f9-399f-4ae7d75e2c93.png)

lastSignInDateTime（対話型サインイン）と lastNonInteractiveSignInDateTime（非対話型サインイン）の情報が取得できます。
正確なサインイン日時を知るためには両方のサインイン日時を考慮する必要がありますね。

サインイン日時以外にも、ユーザーの属性を取得してみます。
下記 URI では「表示名」「メール」「割り当て済みライセンス」「有効なアカウント」の情報を取得しています。

```
https://graph.microsoft.com/v1.0/users?$select=displayname,mail,signInActivity,assignedLicenses,accountEnabled
```

リストに出力する値を決めたところで、SharePoint リストを作成しておきます。


## Logic Apps で Microsoft Graph API を実行

Logic Apps の HTTP コネクタで実行します。
認証の種類はマネージド ID を利用すると管理が楽ですし最もセキュアです。

<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540101/2a8fe8f6-b87a-bd86-cd6a-f245dbbc91d8.png">


## SharePoint リストに出力する

続いて、取得した情報を SharePoint リストに出力する処理です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540101/56793454-6722-6c92-accd-eb4a405bb241.png)





### リストにあるアイテムを取得

まず、「複数の項目の取得」アクションで SharePoint リストのアイテムを取得します。フィルタークエリで Graph API で取得した UserPrincipalName の値が入力されている行を取得します。
これは、このあとの処理の "リストに UserPrincipalName がない場合は行を追加、すでにある場合は項目を更新" するフローで使います。 


<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540101/8d1a6daa-fa32-3f7f-4df9-6e2094e4bc3a.png">


### ライセンスの GUID をライセンス名に変換
 assignedLicenses で取得したライセンス名は GUID で取得されるので、replace 関数を使って GUID をライセンス名に変換する処理を加えました。


<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540101/49ef5d9d-1f08-45ba-7dd1-62eb1b3c32d8.png">

```
replace(replace(replace(string(item()?['skuId']),'05e9a617-0261-4cee-bb44-138d3ef5d965','M365 E3'),'06ebc4ee-1bb5-47dd-8120-11324bc54e06','M365 E5'),'f30db892-07e9-47e9-837c-80727f46fd3d','PowerBI Pro')
```

https://learn.microsoft.com/ja-jp/azure/active-directory/enterprise-users/licensing-service-plan-reference


### リストに UserPrincipalName がない場合は行を追加、すでにある場合は項目を更新
ユーザーのサインイン日時やライセンス、アカウントの有効状態の情報は更新されます。
そのため、リストに Graph API で取得したユーザー情報を新規で作成する処理とすでにある情報を更新する処理が必要です。



<img width="500" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540101/fd3ef159-2a2f-2a75-0973-17e884565c83.png">




この例では、「条件」アクションで下記の length 関数を使って「複数の項目の取得」アクションの実行結果がない場合は、True（項目を作成）、ある場合は False（項目の更新）をする条件としています。



```
length(body('複数の項目の取得')?['value'])
```
Logic Apps を実行すると以下のようなリストが出来上がります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3540101/62c2c634-a83c-0242-7f48-ecbd423524ab.png)





## リストができたら
たとえば長期間サインインがないユーザーの項目に色を付けて視覚的にわかりやすくしたり、リストからユーザーを削除するフローを取り入れることもできます。このような手法で、ユーザー棚卸作業を効率的に行うのはいかがでしょうか。
Arduino上でIRremote4.2を使用した市販リモコンの解析について

Arduino上で赤外線リモコンなどの信号送受信をコントロールするIRremoteという[ライブラリ]("https://github.com/Arduino-IRremote/Arduino-IRremote"IRremoteライブラリ")が公開されている。

先人の記事を参考にして、テレビのリモコンの赤外信号を受信→シリアルモニタに受信した信号の情報を表示するスケッチを書いたが、どんなリモコンのボタンでも**0xFFFFFFFF**で返ってきてしまう。


この問題はライブラリのIRremote2.xから3.xに変わり仕様が変更となったことが原因のようだ。

https://github.com/Arduino-IRremote/Arduino-IRremote#converting-your-2x-program-to-the-4x-version

IRRemote4.2を使用して、赤外線受信したシグナルを、Arduinoのシリアルモニタに表示するスケッチを書いてみたら問題なく受信ができた。

~~~arduino.c

include <IRremote.h>
int receiverPin = 2; //赤外線受信するPinを2番に設定 

IRrecv receiver(receiverPin);
decode_results results;
 
void setup() {
    Serial.begin(9600);
    IrReceiver.begin(receiverPin,ENABLE_LED_FEEDBACK);

 
void loop() {
    if(IrReceiver.decode() ) {
        Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX);
        IrReceiver.printIRResultShort(&Serial);
        IrReceiver.resume();
        }
 
}
~~~

【参考】
https://www.reddit.com/r/arduino/comments/11sub8n/irremote_only_returns_ffffffff_value/?rdt=42930
Android Studio: 画像を表示

Ubuntu 23.04 にインストールした Android Studio Giraffe | 2022.3.1 で確認しました。

## プロジェクトの作成
Empty Views Activity を選ぶ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/a0fac0c1-aa71-48aa-71a4-7e68925c4369.png)

## 画像の取り込み
Tools -> Resource Manager
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/623c5dc9-a66c-1b49-00d3-bcb19fac7ec8.png)

画像の追加
'+' をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/0d7042b2-d2dd-8a66-3a17-ec3c2fa39432.png)

Import Drawables
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/a363fc36-0dee-c030-e940-069722e19d4f.png)

ファイルを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/68566b84-fdc5-d19c-f176-7e09b6b81a6a.png)

Next
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/4ee4f0b1-35bf-9fc7-9f50-15d479ea7065.png)

Import
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/dcdc38af-5d92-c03b-4f1d-952ab5ac1a5c.png)

##　画像の配置
activity_main.xml　を選ぶ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/91a08b1e-4048-9fb7-2738-f0f33b3591b4.png)

画像をドラッグ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/1bb05cd1-926f-b376-ba5d-4a53e7c07748.png)

この時の、activity_main.xml をコードで表示
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/ec104ee6-9be5-b1f8-f2ff-83709aa18b63.png)


```xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:srcCompat="@drawable/fuji_a"
        tools:layout_editor_absoluteX="0dp"
        tools:layout_editor_absoluteY="259dp" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

## 実行:　Run
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/2997dc2c-0340-9aec-82a1-d3e3c34209b2.png)

## インストーラーの作成:　Build APK(s)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/933b71c5-b7e9-da49-9bea-2b1cae3bb57f.png)

## apk-debug.apk を実機に持って行ってテスト
![Screenshot_20231002-095148.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/8b7620fa-f5d9-e7ae-d415-c1f6d6c98eea.png)
2025年まであと少し！〜昭和100年問題について〜

おはようございます。
今日は2023年10月2日月曜日です。

ですが

もしあらゆるコンピュータが今日を日曜日だといえば、
本当に今日は日曜日になってしまうのではないか。

そう本気で思ってしまう昨今です。

秋風を感じて年の瀬を考える時間が増えてきたので、
少し気が早いですが、2025年を見据えて
今回は昭和100年問題と2000年問題についてまとめたいと思います。

## 2000年問題
2000年問題とは、西暦が2000年1月1日にコンピュータに何らかの異常が起こるのではないかと取り沙汰された問題です。
2000年当時、メモリ容量やディスプレイの大きさの関係で、
年号を下2桁で1987年を「87年」のように管理しているプログラムが多くあったようです。
そんな中で2000年を迎えると、コンピュータが一斉に機能停止や誤作動を起こすのではないかと懸念されていました。
特にライフラインに関するシステムや飛行機、銀行に関しては手厚い管理体制が敷かれていたようです。

こちらは厚生労働省の文書ですが、2000年問題は当時大きく取り上げられたのだなぁということが伺えます。

>コンピュータ・プログラムが２０００年以降の日付に対応していない場合にシステムが正常に機能しないという西暦２０００年問題（以下「２０００年問題」という。）については、平成１０年９月１１日付けで高度情報通信社会推進本部（本部長：小渕恵三内閣総理大臣）にて決定された「コンピュータ西暦２０００年問題に関する行動計画」により、対応が必要な各領域において官民を挙げた具体的な行動の徹底が求められており、厚生省においては、これを受けて平成１０年１０月２２日に「厚生省コンピュータ西暦２０００年問題対策実施要領」を定め、その対応を進めているところである。
　水道分野は、ライフラインとして国民生活に不可欠の分野であり、万一２０００年問題により給水に障害が発生した場合の影響は極めて大きいことから、金融、エネルギー、情報通信、交通及び医療の民間重要５分野等の一つとしての認識が高まっている。
　厚生省が平成１０年１２月に行った水道事業者等の２０００年問題への対応状況の抽出調査の結果では、コンピュータが停止した場合の給水への支障については、一時的に停止をしても手作業で復旧可能であり、給水が停止することはないとの結果であったが、２０００年問題の有無について未だ４％の水道事業者は未確認であり、模擬テストを実施しているものが２０％弱、危機管理計画を策定しているものは３％と低率であった。
　水道の給水に支障を生じた場合の国民生活及び経済社会への影響の重大性にかんがみ、２０００年問題に関し、万一の事態を想定した、徹底した対応を講じることが求められている。

https://www.mhlw.go.jp/www1/topics/c2000/tp0305-1_14.html

https://www.mlit.go.jp/sogoseisaku/policy/y2k/2000.html

結局大規模なトラブルは起こらなかったようですが、未経験の事態に緊張感は高かったことでしょう。
私は当時幼かったので、全く記憶の欠片にもありませんが、社会の温度感がどんなものだったのか、どんな報道がされて、どんなリアクションがとられたのか見てみたかったなぁ。

## 昭和100年問題
似たようなコンピュータシステムの年問題が、2025年に日本で起こります。
それが昭和100年問題です。
UI上令和5年となっていても、システム上昭和が現在まで継続しているものとして扱った際、2025年に昭和100年を迎え、2桁を超えてしまいます。
2000年問題の懸念と同じように、システムの異常が起こるかもしれません。


2000年問題での経験があることや、日本という限られた範囲での問題であることなどから、
昭和100年問題は大きくは取り上げられないかもしれませんが、

あなたのシステムは大丈夫ですか？
AZ-104試験対策のまとめ「Storage Account編①」

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

## 試験対策のまとめ
### **Azure Date Lake Storage Gen2**
Azure Blob Storageをベースに構築された、ビッグデータ分析専用の一連の機能  
任意の形式の大量のデータを格納し、ビッグデータ分析ワークロードとしに対応するように設計  

※データレイクとは？
構造化データや非構造化データに関わらず全てのデータを一元的に格納できるリポジトリ  
様々なデータを一か所に格納して、素早く簡単にアクセス/分析が可能  

* **Azure Data Lake Storage Gen2をサポートするStorage Accountの条件**
    - BLOBストレージをサポートしていること  
        - Standard 汎用 v2 アカウント
        - Premium ブロック BLOB アカウント
    - 階層型名前空間を有効にしていること

<br>

* 参考
    - https://learn.microsoft.com/ja-jp/azure/storage/blobs/data-lake-storage-introduction

<br>

### **Storage Accountとレプリケーションの制限**
一部のStorage Accountの種類では、特定の冗長構成のみをサポート
| Accountの種類 | LRS | ZRS | (RA-)GRS | (RA-)GZRS | ライブマイグレーション※ |
| --- | :---: | :---: | :---: | :---: | :---: |
| Standard 汎用 v2 | 〇 | 〇 | 〇 | 〇 | 〇 |
| Premium ファイル共有 | 〇 | 〇 | × | × | 〇 |
| Premium ブロック BLOB | 〇 | 〇 | × | × | × |
| Premium ページ BLOB | 〇 | × | × | × | × |

<br>

* 参考  
    - https://learn.microsoft.com/ja-jp/azure/storage/common/storage-redundancy
    - https://github.com/MicrosoftDocs/azure-docs.ja-jp/blob/master/articles/storage/common/redundancy-migration.md

<br>

#### **※ライブマイグレーション**
再起動を必要とせず、VMのメモリを保持する操作  
一時停止や凍結が発生するが、その継続時間は通常は5秒未満  

* シナリオ例
    - Azureの計画メンテナンス
    - ハードウェア障害
    - 割り当ての最適化

<br>

* 参考
    - https://learn.microsoft.com/ja-jp/azure/virtual-machines/maintenance-and-updates

<br>

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

https://qiita.com/fsd-jume/items/7368e342a637ed8162a7

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

https://qiita.com/fsd-jume/items/6eda24728c9c03c36bc3
【Docker】Error starting userland proxy: listen tcp4 0.0.0.0:xx: bind: address already in use.というエラーが出た話

Dockerコンテナ構築時以下のようなポートエラーが発生し、原因がはっきりとわからず時間を費やしてしまった経験があるので、そちらの解決方法を紹介いたします。
```
Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use.
```

## エラーの原因と対処法
下記のようにDoccker コンテナを起動しようとするとエラーが発生したわけです。
```
# docker run -p 80:80 -d atmoz/sftp foo:pass:::upload
8886d0f9740a1988d6da27dc8f62abdc7d556aba8cdef7e39fc2f7779c152268
docker: Error response from daemon: driver failed programming external connectivity on endpoint romantic_haslett (f4f9d55d8c947ff55486ab89a564abd2992c83c969f522ab0a5c0fd05c3bba00): Error starting userland proxy: listen tcp4 0.0.0.0:80: bind: address already in use.
```
　　
このエラーの原因はポートがすでに使われていることが原因となっています。
こちらは、以下のようなコマンドで実際にそのポートが使用されているか確認することができます。
```
# lsof -i -P | grep "LISTEN"
```

　　
このエラーの回避方法はポートを使用しているサービスを停止すれば解決しますので、以下のコマンドでサービスを停止してあげましょう。
```
# systemctl stop 対象サービス名
```

以上の手順を踏んでいただければ、コンテナが正常に起動するはずです。
matplotlibリアルタイムチャート更新とアニメーションGIF作成  ※ ArtistAnimation不使用

# 内容
![download.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629684/de558582-e0ed-b653-477e-33ab81274f6e.gif)

1. matplotlib チャートをリアルタイム更新する方法
1. matplotlib チャートをアニメーション表示し GIFファイルに保存する方法

PIL(Python Imaging Library)を使います。matplotlib.ArtistAnimation は使いません。 Colab で動作確認しました。


# matplotlib チャートをリアルタイム更新
IPython.display を使います。早すぎると表示にコマ落ちが発生します。リピート再生はできません。

```py
import time
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display  # 試した環境では省略しても動作しました

x = np.linspace(0, 2 * np.pi, 24)

fig = plt.figure(figsize=(2, 1.5))
display_handle = display(fig, display_id=True)
for i in range(12):
    y = np.sin(x + 0.5 * i)
    plt.clf()  # Axes の場合は ax.cla()
    plt.plot(x, y, '-')
    display_handle.update(fig)
    time.sleep(0.5)

plt.close()
```

# matplotlib チャートをアニメーション表示し GIFファイルに保存
matplotlibチャートをコマ画像としてメモリ上に蓄積し最後に PIL でGIF画像に変換します。そのGIF画像を IPython.display で表示します。


```py
import io
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from IPython.display import display  # 試した環境では省略しても動作しました
from IPython.display import Image as DisplayImage

def fig2im(fig):
    """PyPlotのFIgureをPIL画像に変換"""
    with io.BytesIO() as fp:
        fig.savefig(fp, bbox_inches='tight', format='png')
        im = Image.open(fp)
        im.load()
    return im


def draw_animation(imgs):
    """PIL画像リスト imgs をアニメーションGIFで表示"""
    with io.BytesIO() as fp:
        # 先頭のPIL画像に後続するPIL画像を追加しGIF画像として保存
        imgs[0].save(fp, format='gif', append_images=imgs[1:], save_all=True, loop=0, duration=120)
        # ファイルの先頭へ移動
        fp.seek(0)
        # GIF画像表示
        display(DisplayImage(data=fp.read(), format='gif'))
    return

# アニメーションのコマ画像を作成
imgs = []
x = np.linspace(0, 2 * np.pi, 24)
fig = plt.figure(figsize=(2, 1.5))
for i in range(12):
    y = np.sin(x + 0.5 * i)
    plt.clf()  # Axes の場合は ax.cla()
    plt.plot(x, y, '-')
    imgs.append(fig2im(fig))

plt.close()

# アニメーション表示
draw_animation(imgs)
```


表示されたアニメーション上で[右クリック]-[名前を付けて画像を保存] でGIFファイルとして保存できます。

![qiita-2310.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629684/4b42b942-78c9-6eb1-a755-302366b04ab0.png)
Androidアプリ「TaskPriority」をリリースしました【個人開発】

## はじめに

個人開発したAndroidアプリ「TaskPriority」をリリースしました！

Androidアプリのリリースは今回で3回目です！

この記事ではリリースしたアプリの紹介や制作した経緯などについてを自分のための備忘録として書いていきます。

## 目次
1. リリースしたアプリ「TaskPriority」について
2. 制作した経緯
3. 使用した言語・データベース
4. 今までのアプリ開発で感じた課題とその解決策
5. 今後修正したいところ
6. おわりに

## リリースしたアプリ「TaskPriority」について
#### リリースしたアプリ

* <a href='https://play.google.com/store/apps/details?id=com.ruriboshi.taskpriority'>TaskPriority:優先順位がわかるタスク管理アプリ</a>

#### アプリの概要

タスクの優先順位を確認することができるタスク管理アプリです。
優先順位が高いタスクは濃い色で、低いタスクは薄い色で表示されるので、下記画像のようにグラデーションになり、見た目も楽しいタスク管理アプリになっています。

<p>

<img width="40%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354509/3f046697-2ee7-6961-06d9-b5c5be19182d.png">

<img width="20%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354509/be6d7065-7ffa-74f2-d973-05eb4694b1c9.jpeg">

<img width="20%" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354509/70dd9a01-69e5-f547-d6b8-47f3aa0fa200.jpeg">


</p>


## 制作した経緯

少し前に「緊急度と重要度のマトリクス」というタスクの優先順位を判断するためのフレームワークを知り、これをアプリにできないかと考えたからです。
タスク管理アプリは以前に「<a href = "https://play.google.com/store/apps/details?id=com.ruriboshi.clask_app">Clask</a>」を制作したことがあったので、その知識を生かして制作しました。

## 使用した言語・データベース

* Kotlin
今までにリリースしたアプリと同様にKotlinで開発しました。

* Realm
以前にタスク管理アプリを制作した際にも使用したので今回もデータベースRealmを使用しました。
（そろそろFirebaseの使い方も勉強しようと思っています）

## 今までのアプリ開発で感じた課題とその解決策

#### アプリのダウンロード数が増えない

今までに２つアプリをリリースしましたが、ほとんどダウンロードされず結構ショックでした(笑)これを解決するにはダウンロードしてもらえる機会を増やすことが大切だと思い、今回のアプリ開発では以下の行動を取りました。

* ASO対策
* スクリーンショット画像の改善
* SNSでの宣伝

#### ASO対策
Googleキーワードプランナーでキーワードを選定してアプリの説明文に組み込むようにしました。今までにリリースしたアプリの説明文もGoogleキーワードプランナーを参考にして変更しましたが今のところ効果があるのかはよく分かりません。方法があっているかも分からないので、何度か説明文を変えてみて様子を見ようと思います。

#### スクリーンショット画像の改善
今までよりもスクリーンショット画像の作成に力を入れました。アプリの魅力を伝えることを意識して、他のアプリのスクリーンショット画像も参考にしながら作成しました。

（1枚目：前に作ったアプリのスクリーンショット　2枚目：今回のアプリのスクリーンショット）
<p>
<img width=45% src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354509/48eec9eb-238a-9f42-6e44-1816cfddcb87.jpeg">
<img width=45% src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2354509/56b3e2b1-0df3-23d5-d75e-36d4795fd6f1.jpeg">
</p>
（2枚目の画像のスマホが微妙にずれてるところは後で修正します…）

#### SNSでの宣伝
X(旧Twitter)を使ってアプリの紹介をしました。最近作成したアカウントなのでそこまで宣伝効果はありませんが、こつこつと続けて少しでも多くの人に自分が開発したアプリを知ってもらえたらいいなと思います。

Xアカウント：<a href="https://twitter.com/app_ruriboshi_">ruriboshi@Androidアプリ開発</a>


## 今後修正したいところ

#### アプリの再読み込みの挙動
今はタスクの並び替えをする際にActivityの再読み込みをするのですが、挙動が気に入りません。そもそも並べ替えるたびに再読み込みをするので本当にいいのか分からないので他の方法がないか考えています。

#### タスクの並び替え
今はアプリの初回起動時にRealmのモデルを作成してその数値を書き換えてタスクの並び替えをしていますが、もっといい方法があるのではないかと思っています。

## おわりに
3回目のリリースも無事にできて良かったです。
次はもう少し開発期間を長く設けてさらにクオリティが高くて使いやすいアプリを開発したいです。また、Flutterにも興味があるので挑戦してみたいと思います。
アプリ開発は大変なことや難しいことが多いですが、それ以上に楽しいと感じているので、これからも沢山アプリを開発して沢山リリースしたいです。

↓もし興味があればアプリをダウンロードしていただけると嬉しいです。

<a href='https://play.google.com/store/apps/details?id=com.ruriboshi.taskpriority'>TaskPriority:優先順位がわかるタスク管理アプリ</a>

最後までお読みいただきありがとうございました。
【Mac】ショートカットキーを使ってウィンドウ操作・タブ操作を効率化する

## はじめに
プログラミングをしていると、様々なアプリケーションを開いたり、ブラウザで沢山タブを開いたりというように、ウィンドウ操作やタブ操作を頻繁にしますよね。
そこで、自分が使ってみてウィンドウ操作を効率化できたと感じたショートカットを紹介していきたいと思います。使っているPCは、MacBook Pro M1 Maxです。

## ショートカットキー

### ウィンドウ操作
| ショートカットキー | コマンド |
| :--- | :--- |
| command + tab | ウィンドウ切り替え(順方向) |
| command + shift + tab | ウィンドウ切り替え(逆方向) |
| command + control + f | ウィンドウの最大化 |
| command + m | ウィンドウの最小化 |
| command + n | 新しいウィンドウを開く |

- `command + tab` / `command + shift + tab`
`command + tab`の場合、`command`キーを押したまま`tab`キーを押すことで、ウィンドウを切り替えることができる。
`command + shift + tab`の場合、`command`キーと`shift`キーを押したまま`tab`キーを押すことで、ウィンドウを切り替えることができる。

- `command + control + f` / `command + m`
`command + control + f`は、最大化とその解除の切り替えをすることができるので便利です。一方、`command + m`は、一度最小化すると同じショートカットキーを実行しても切り替えることができないので少し不便です。




### タブ操作
ブラウザのタブ操作を想定しています。
| ショートカットキー | コマンド |
| :--- | :--- |
| control + tab | タブ移動(右方向) |
| control + shift + tab | タブ移動(左方向) |
| command + t | 新しいタブを開く |
| command + w | 現在開いているタブを閉じる |
| command + option + w | 現在開いているウィンドウの全てのタブを閉じる |
| command + shift + w | 全てのタブを閉じる(アプリの終了まではしない) |


- `control + tab`, `control + shift + tab`
タブ移動は非常によく使う操作で、カーソルをタブまで持っていくのが面倒くさいのでとても便利です！
- `command + t`, `command + w`
新しいタブを開く際の「＋」ボタンや、タブの閉じる際の「×」ボタンは小さいので、カーソルを持っていってカーソル位置を微調整する操作はかなりストレスのかかる操作だと思うので、ショートカットキーを思い出すのに多少の時間がかかったとしても一発で操作できた時の爽快感もあるのでよく使っています！



## おわりに
今回は、自分が便利だと思ったショートカットキーを紹介しました。
タブ操作やウィンドウ操作は頻繁に行うものなので、効率的かつストレスのないやり方を見つけていきたいですよね。
他にも便利なショートカットキーがあれば、ぜひコメントをください！

## 参考記事
https://shiraberu.tech/2020/09/02/mac%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%82%AB%E3%83%83%E3%83%88%E9%9B%86%E3%82%A6%E3%82%A3%E3%83%B3%E3%83%89%E3%82%A6%E6%93%8D%E4%BD%9C%E7%B7%A8/

https://www.sejuku.net/blog/124447

https://smbiz.asahi.com/article/14608208
【Ruby】知っておくといつか役立ちそうな配列・ハッシュのメソッド6選

## はじめに
もうすぐ入社１年になるエンジニアです。
最近、開発チームの勉強会で「良いコード悪いコード」という本を読みました。その本の、コレクション(※)に関する説明で、車輪の再発明をせずに標準ライブラリに用意されているメソッドを使おうと書かれていて、確かにあるものは使うべきだなと思いました。
そこで、配列とハッシュのメソッドを調べてみたので、知らなかったけど使う機会がありそうなメソッドや知らない人は車輪の再発明してしまいそうなメソッドを6つ紹介しようと思います。

※ コレクション：複数のデータをまとめて格納するためのデータ構造。rubyでは、配列やハッシュなどがある。

## 配列・ハッシュ共通のメソッド

### `select`メソッド
selectメソッドは、配列の各要素に対して条件式を満たす要素のみで構成される新しい配列を返します。ハッシュの場合も同様です（例を参照）。

```
# 基本構文
配列.select { |変数| 条件式 }
ハッシュ.select { |変数1, 変数2| 条件式 }

# 配列の例
array = [1, 2, 3, 4, 5]
new_array = array.select { |elem| elem  < 3 }
pp new_array # => [1, 2]

# ハッシュの例
hash = { a: 1, b: 2, c: 3 }
new_hash = hash.select { |key, value| value >= 2 }
pp new_hash # => { b: 2, c: 3 }
```


### `reject`メソッド
rejectメソッドは、selectメソッドとは逆に、配列の各要素に対して条件式を満たさない要素のみで構成される新しい配列を返します。ハッシュの場合も同様です（例を参照）。

```
# 基本構文
配列.reject { |変数| 条件式 }
ハッシュ.reject { |変数1, 変数2| 条件式 }

# 配列の例
array = [1, 2, 3, 4, 5]
new_array = array.reject { |elem| elem >= 2 && elem <= 4  }
pp new_array # => [1, 5]

# ハッシュの例
hash = { a: 1, b: 2, c: 3 }
new_hash = hash.reject { |key, value| value == 2 }
pp new_hash # => { a: 1, c: 3 }
```


### `find`メソッド
findメソッドは、ブロック内で指定した条件式を満たす最初の要素を取得します。ただし、条件式を満たす要素が存在しない場合は、nilを返します。
自分は、コードレビューの時に`~.select { ~ }[0]`という処理を見たのですが、その時はまだfindメソッドを知らなかったのでスルーしてしまった記憶があります。

```
# 基本構文
配列.find { |変数| 条件式 }
ハッシュ.find { |変数1, 変数2| 条件式 }

# 配列の例
array = [1, 2, 3, 4]
elem = array.find { |elem| elem % 2 == 0 }
pp elem # => 2

# ハッシュの例
hash = { a: 1, b: 2, c: 3 }
elem = hash.find { |key, value| value % 2 == 0 }
pp elem # => [:b, 2] ハッシュの場合は、キーとバリューの配列を返すので注意
pp elem.to_h # => { b: 2 } 
```


## 配列のメソッド

### `grep`メソッド
grepメソッドは、引数に正規表現を指定してパターンにマッチした要素を配列として返します。

```
# 基本構文
配列.grep(/正規表現/)

array = ['aaa', 'aab', 'abb', 'bbb']
new_array = array.grep(/^a.b$/) # aで始まるbで終わるパターンのみ取得
pp new_array # => ['aab', 'abb']
```


## ハッシュのメソッド
### `key?`メソッド
ハッシュが引数で指定した値をキーとして持つ場合はtrue、持たないはfalseを返します。
同様のメソッドに`value?`メソッドもあります。
全く同じ挙動ではありませんが`key?`メソッド知らないと、`hash[:key] != nil`のように書いてしまいそうですね。

```
# 例
hash = { a: 1, b: 2, c: 3 }
pp hash.key?(:a) # => true
pp hash.key?(:z) # => false
```


### `transform_values`メソッド
全てのバリューに対してブロック内の処理を実行した結果で置き換えたハッシュを返します。キーは変化しません。
このメソッドを知らないで、`hash.map { |key, value| keyを使わない処理 }`のように書いてしまいそうですね。
同様のメソッドに、`transform_keys`メソッドもあります。

```
# 基本構文
ハッシュ.transform_values { |変数| 実行する処理 }

# 例
hash = { a: 1, b: 2, c: 3 }
new_hash = hash.transform_values { |value| value * 2 }
pp new_hash # => { a: 2, b: 4, c: 6 }
```


## おわりに
配列やハッシュのメソッドを6つ紹介しましたが、知らないメソッドはあったでしょうか？
使えそうだなと思ったメソッドがあれば、頭の片隅にでもいれておくと、実装を考える際に役立つことがあると思います。

## 参考記事
https://qiita.com/yyykms123/items/e1bcb838f56100d12025

https://qiita.com/jnchito/items/02ba8aad634a6bd8a2f6

https://hataworakuni.net/search-method-of-array-in-ruby
コミュニケーションの拡散と収束: 成功への鍵

コミュニケーションは、我々の日常生活、仕事、関係の中核をなす要素です。しかし、一般的なコミュニケーションの考え方だけでは十分ではありません。成功へと導くためには、場面や目的に応じてコミュニケーションの「拡散」と「収束」を適切に使い分けることが必要です。

### 1. 拡散させるコミュニケーションとは？

拡散させるコミュニケーションとは、思考やアイディアを広げるためのコミュニケーション手法です。これは、新しいアイディアの発想やクリエイティブな解決策の模索、多様な視点や意見を取り入れることを目的としています。

例えば、新しいプロジェクトや企画の初期段階では、多くの可能性やアイディアを模索する必要があります。この時、拡散させるコミュニケーションを活用することで、参加者の意見や視点を最大限に活かし、多様な選択肢を考え出すことができます。

### 2. 収束させるコミュニケーションとは？

一方、収束させるコミュニケーションは、特定の目的やゴールに向かって意識や行動を集中させるためのコミュニケーション手法です。具体的なタスクの実行や、プロジェクトのゴール達成を目指す際には、この収束させるコミュニケーションが不可欠となります。

例として、PM（プロジェクトマネージャー）がプロジェクトの進行を管理する際、明確なゴールやスケジュールを設定し、チームメンバーにそれを伝えることは非常に重要です。このような場面では、収束させるコミュニケーションを用いて、必要なタスクや期限を明確にし、チーム全体の意識を一致させることが求められます。

### 3. 使い分けの重要性

拡散と収束、両方のコミュニケーション手法にはそれぞれのメリットと適切な場面があります。適切な使い分けができないと、企画の初期段階で収束させすぎてしまいアイディアが足りなくなったり、逆に実行段階で拡散させすぎてしまい方向性が失われるリスクがあります。

成功へと導くためには、時と場合に応じてこれらのコミュニケーション手法を適切に使い分けることが重要です。特に、チームのリーダーやマネージャーは、これらの使い分けを意識し、チームのコミュニケーションを適切にガイドする役割が求められます。

### 4. まとめ

コミュニケーションの拡散と収束は、それぞれ異なる目的と場面での活用が求められる手法です。成功へと導くためには、これらを適切に使い分けることが必要です。特に、チームのリーダーやマネージャーは、この使い分けを意識し、チーム全体のコミュニケーションを適切にガイドする役割があります。
正規表現 ^[0-9a-zA-z]+$ がマッチする文字列は？

# はじめに

半角英数字のみを許容する入力フォームのバリデーション等で以下の正規表現を用いることがあるかもしれません。

```plaintext
^[0-9a-zA-Z]+$
```

これを間違えて標題のように記載するとどうなるでしょう？
この正規表現であれば、言語を問わず同じだと思うのですが、念の為、javascriptとします。
というのが、この記事のお話です。

なにが違うかを念のため記載すると、アルファベットの**最後のzが小文字**になっています。

```plaintext
^[0-9a-zA-z]+$
```

## 答え


以下を1文字以上含む文字列にマッチします。
つまり、本来除外したかった記号も含んでしまいます。

|分類|内容|
|-|-|
|数字|0123456789|
|アルファベット小文字|abcdefghijklmnopqrstuvwxyz|
|アルファベット大文字|ABCDEFGHIJKLMNOPQRSTUVWXYZ|
|一部の記号|[\\]^_`|

## 解説

`A-z`をASCIIコード（10進数）で表すと`65-122`となります。
`A`から`z`までには、アルファベット大文字小文字の52個以外に何かが6個追加されているのです。
以下がその何かです。

|10進数|...|65|...|90|91|92|93|94|95|96|97|...|122|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|文字|...|A|...|Z|[|\ |]|^|_|`|a|...|z|

※ 並び順はUnicodeも同じです。

## おまけ

javascriptにはありませんが、[ruby](https://docs.ruby-lang.org/ja/latest/doc/spec=2fregexp.html#string)やj[ava](https://docs.oracle.com/javase/jp/8/docs/api/java/util/regex/Pattern.html), kotlinでは、正規表現の減算表現があります。

例えば、正規表現で、`\w`は`[A-Za-z0-9_]`に相当し、半角英数字とアンダースコアにマッチします。半角英数字のみにマッチさせたい場合は使用できないわけです。
ところが、以下のように記載すると[^1]アンダースコアを抜くこと(減算)ができ、半角英数字のみにマッチします。

```plaintext
^[\w&&[^_]]+$
```

## 追記
javascriptでもアンダースコアを抜く方法があることを@ss8826 さんに教えていただきました。`\w`の否定の`\W`を用いる方法です。
こちらの方が短くて他の言語でも使用できますね。
詳細は[コメント](https://qiita.com/fumta/items/e90dbe61f5c921b4d177#comment-55d83e386a73e28c9c18)を参照下さい。

```plaintext
^[^\W_]+$
```

## 参考

正規表現のチェックには、以下のサイトを使用しました。

* [regular expressions101](https://regex101.com/)
* [Rubular](https://rubular.com/)


[^1]: java, kotlinでは、バックスラッシュに対しエスケープが必要です。
FileWriter 

```java
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class FileOutputExample {
    public static void main(String[] args) {
        // 出力先のファイルパス
        String filePath = "output.txt";

        try {
            // FileWriterとBufferedWriterを使用してファイルに書き込み
            FileWriter fileWriter = new FileWriter(filePath);
            BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);

            // 書き込むテキスト
            String textToWrite = "Java 1.4のバージョンでのファイル出力の例です。";

            // ファイルにテキストを書き込む
            bufferedWriter.write(textToWrite);

            // ファイルを閉じる
            bufferedWriter.close();

            System.out.println("ファイルに書き込みが完了しました。");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
```
何もしないことで...サービス・パフォーマンスを常に向上させる方法

<font color="Grey">*[How to constantly improve your service performance... by doing nothing](https://aiven.io/blog/constantly-improving-your-service-performance)の翻訳です。*</font>

2022年11月24日

# 何もしないことで...サービス・パフォーマンスを常に向上させる方法

ハードウェアが進化し、より優れたインスタンスタイプが利用できるようになりました。Aivenがどのように御社のサービスを時代に対応させるのか、リラックスしてくつろぎながらご覧ください。

**Aivenのサービスには、お客様にはあまり見えていない重要な部分があります。それは新しいサービスだけではありません。ライフサイクルを通じて、すべてのサービスのパフォーマンスを常に微調整しています。


ムーアの法則は60年以上前のものですが、今でも真実であり、コンピュータ・ハードウェアは非常に速いスピードで進化し続けています。クラウド・コンピューティングも例外ではありません。


クラウド・プラットフォーム・プロバイダーは、新しい仮想マシン・インスタンス・タイプを導入し続けている。この開発作業の結果、クラウドごとに異なるインスタンス・タイプが進化し、それぞれ異なるユースケースに最適化されている。このため、常に最新の情報を入手し、どのインスタンス・タイプが特定のデータ・インフラ・サービスに最適なのかを知ることは非常に難しくなっている。


Aivenでは、お客様からこの負担を取り除きます。


Aivenのサービスプランは、仮想マシンの数、仮想マシン上のCPUとメモリ、プランに含まれるストレージを定義していることにお気づきでしょうか。しかし、特定のインスタンスタイプに固定されていないことにお気づきでしょうか。


私たちのチームはハードウェアの進化を追跡しています。どのインスタンスタイプが特定のサービスに最適な価格性能比をもたらすか、継続的に評価しています。より良いインスタンス・タイプが見つかったら、それを本番稼動させ、新しいサービスはすべてこの新しいインスタンス・タイプをベースにします。古いサービスも、次にメンテナンス（バージョンの更新、セキュリティパッチの適用、サービスのスケールアップやスケールダウンなど）を受けると、新しいインスタンスタイプに切り替わります。お客様には、余分なコストや時間をかけることなく、サービスのパフォーマンスが向上したことだけを実感していただけます！


このプロセスの一例として、私たちは最近、AWSのRedisサービスの新しいARMベースのインスタンスタイプに移行しました。ベンチマークによると、これによりRedisのパフォーマンスが最大10%向上しました。もしあなたがRedisの顧客の一人であれば、すでにこのパフォーマンスの向上を目にしているかもしれません。もしそうでなければ、もうすぐです。


#### まだマネージド・データ・プラットフォームをお探しですか？

見つかりました！

[今すぐ無料トライアルにお申し込みください](https://console.aiven.io/signup)
Aivenと当社のサービスに関する最新ニュースや、オープンソースに関するちょっとした情報を入手するには、[月刊ニュースレターを購読](https://aiven.io/newsletter)してください！Aivenに関する日々のニュースは、[LinkedIn](https://www.linkedin.com/company/aiven/)と[Twitter](https://twitter.com/aiven_io)のフィードでご覧いただけます。


サービスのアップデート情報を知りたい方は、[変更履歴](https://aiven.io/changelog)をご覧ください。