OmniAICreator/Qiita-1.07M huggingface dataset:
#PowerApps Patch 関数の結果を取得する

以前、Form コントロールで SubmitForm と LastSubmit を利用した例を紹介しました。

https://qiita.com/yamad365/items/e572e471a3dd2ca4d69c

「Patch 関数でも同じようなコトできないの？」という質問をいただいたので、今回は Patch 関数で同様の効果を得たいネタです。

# Patch 関数
念のため、公式情報は下記です。

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-patch?WT.mc_id=DX-MVP-5004053

公式に記載されていますが、Patch 関数の戻り値は変更または作成したレコードになります。

※念のため該当箇所のスクショ（2023/09/19 時点）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215892/fb7a33b8-f734-7ff6-50aa-ffb0f166f67d.png)

> Patch の戻り値は、変更または作成したレコードです。 レコードを作成した場合、戻り値にはデータ ソースによって自動的に生成されたプロパティが含まれる場合があります。 ただし、戻り値は、関連するテーブルのフィールドの値を提供しません。

つまり、Patch 関数の戻り値を得れば Form コントロールの LastSubmit と同様なコトが実現できる、ですね。

# 実際に試してみた
簡単なアプリで動作を確認してみました。

## 検証用アプリの概要
左上の［データ生成］ボタンで Collection を作成しています。
右側のテキストインプットコントロールに入力した値で、ギャラリーコントロールで選択されている対象の名前（Name）を Patch 関数で更新します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215892/4039dc62-9b8e-71fb-75b8-5f11de150830.png)
既に、コレクションを作成した後の画面です。

Collection はこちら。

```
ClearCollect(
    colWk,
    {Name: "愛知 家康", Age: 26, Gender: "男"},
    {Name: "岐阜 千姫", Age: 24, Gender: "女"},
    {Name: "三重 忠勝", Age: 32, Gender: "男"}
)
```

［Patch で Name 変更］ボタンの関数はこんな感じです。

```
UpdateContext( {locRet: Patch(colWk, Gallery.Selected, {Name:TextInput.Text}) } )
```

locRet コンテキスト変数へ Patch 関数の結果を保持しています。右下の青色背景の箇所には locRet の結果をラベルで 1 つずつ表示するようにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215892/2b7129f8-f536-88c4-7b89-8f1432284c6f.png)
その他、詳細は割愛です。気になる方はご自身で試行錯誤してください。こういう検証は思った以上に良い訓練になりますよ。

## 検証してみる
ギャラリーコントロールの 3 番目を変更してみます。

1) ギャラリー 3番目を選択して Name を変更する準備
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215892/a16ab335-4dcc-2bd9-8de7-4745c3c45eee.png)
右下の青い背景の箇所は、まだブランクです。

2) Patch 関数を実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215892/10f864ee-8e4b-3aac-e593-52687d4444df.png)
Patch 関数の戻り値である locRet.Name などが反映されたので、青い背景の箇所に値が反映されました。

編集画面に戻り、デバッグ表示で Patch 関数の戻り値を保持している変数を確認してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/215892/d3278911-a7f0-0552-7850-f366717bad82.png)
変数に続けて "."（ドット）でレコードの要素へアプローチできていますね。
公式情報どおり、戻り値で更新結果が確認可能です。

# まとめ
Patch 関数の結果は ***戻り値*** で判別可能です！

SharePoint Online のリストで疑似的なリレーションを実現したい際などで活躍すると思います。そんな必要性に迫られた際には思い出してあげてください。

普段は気にしなくても不都合無い話題かもしれない、とも言えますｗ

それでは、皆さま、素晴らしい Power Platform Life を！
React Next.jsで子コンポーネントから親コンポーネントへデータを渡す方法

# 実装したいこと
親から子へデータを渡すときはpropsという非常に便利な機能を使用するだけでデータを受け渡しができます。
しかし、子から親へデータを渡すことは当然ながらpropsで渡すことはできません。
そこで子から親へデータを受け渡すいくつかの方法を紹介します。



# useStateを使用
useStateを用いて子から親コンポーネントにデータを受け渡します。
これはシンプルに親コンポーネントでuseStateを宣言し、propsで更新する関数の方を渡すだけです。
```tsx:親コンポーネント
import { useState } from "react";
import Child from "./child";

const Parent = () => {
  const [inputState, setInputState] = useState("");
  return (
    <>
      <Child inputState={setInputState} />
      <p>{inputState}</p>
    </>
  );
};

export default Parent;
```

```tsx:子コンポーネント
import { Dispatch, SetStateAction } from "react";

const Child = (props: { inputState: Dispatch<SetStateAction<string>> }) => {
  const { inputState } = props;
  return <input type="text" onChange={(e) => inputState(e.target.value)} />;
};

export default Child;
```
注意点は型が`Dispatch<SetStateAction<T>>`であることに注意してください。

上が子コンポーネントで下が親コンポーネントです。
![test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2729508/31b2a303-53a0-fbd3-5303-b944a3367fba.gif)



## localStorageを使用
localStorageを用いて実装してみます。
ここで、更新ボタンを追加しました。更新ボタンなしに動的に行うには自分ではすこし長いプログラムになってしまいました。
おそらくlocalStorageを使用する場合、そこまで動的に処理することが無いと思います。
参考程度でお願いします。

```tsx:親コンポーネント
import { useState } from "react";
import Child from "./child2";

const Parent = () => {
  const [inputString, setInputString] = useState<string | null>(null);

  const handleInputButton = () => {
    setInputString(localStorage.getItem("inputString"));
  }

  return (
    <>
      <Child />
      <p>{inputString}</p>
      <input type="button" value="更新" onClick={handleInputButton}/>
    </>
  );
};


export default Parent;
```

```tsx:子コンポーネント
const Child = () => {
  return (
    <input
      type="text"
      onChange={(e) => localStorage.setItem("inputString", e.target.value)}
    />
  );
};

export default Child;
```


## 値もセットでコンポーネントから返す
私が個人的に気に入っている方法です。関数のreturnの際に値ごと返してあげれば良いです。

```tsx:親コンポーネント
import Child from "./child3";

const Parent = () => {
  const [inputString, ChildComponent] = Child();
  return (
    <>
      <ChildComponent />
      <p>{inputString}</p>
    </>
  );
};


export default Parent;
```

```tsx:子コンポーネント
import { useState } from "react";

const Child3 = (): [string, () => JSX.Element] => {
  const [inputString, setInputString] = useState("");

  const handleInput = () => {
    setInputString("テスト");
  };

  return [inputString, () => (<input type="button" value="" onClick={handleInput}/>)]
};

export default Child3;
```

## その他
他にもRecoilを使ったりcontextを使用する方法もあります。
【Java・学習ログ⑨】継承について（抽象クラスとインタフェース）

教本に沿ってやってみたシリーズ、『第10・11章』。
≪使用教材≫スッキリわかるJava入門　第3版　スッキリわかるシリーズ

## 継承の基本
以前作ったクラスと似通ったクラスを作りたい時、類似したクラスを作成することができる**継承**という機能がある。
▼Heroクラスを元にしたSuperHeroクラスを定義したいとき
```java
public class SuperHero extends Hero {
```
このように、<font color = red>extends</font>を用いれば、元のクラスの差分だけを記述する形で新たなクラスを宣言できる。

元となるクラス（親クラス）、新たに定義されるクラス（子クラス）の関係を**継承関係**という。

子クラス直下に定義されていないメソッドも、インスタンス化すれば呼び出しが可能。
```java
SuperHero sh = new SuperHero(); // インスタンス化
sh.run();
```
#### オーバーライド
親クラスに定義したrunのメソッドを、SuperHeroクラスに限って動きを書き換えたいは、子クラスで再定義できる。

※Stringクラスは継承不可能。
※オーバーライドを禁止したいメソッドには宣言時に<font color = red>finel</font>を付ける。

## 継承とコンストラクタ
Javaでは、**すべてのコンストラクタは、その先頭で必ず内部インスタンス部分のコンストラクタを呼び出さなければならない**というルールがある。
よって、SuperHero（子クラス）は本来、下記のような書き方をしなければならない。
```java
public SuperHero(){
    super();
```
コンストラクタ1行目でHeroを呼び出していない場合、コンパイラによって「super();」という行が自動的に挿入される。

#### 内部インスタンスの初期化を行う

親クラスコンストラクタに引数が指定されている場合、上記のような引数なしの「super();」で親元を呼び出しを行うことは不可能。内部インスタンスの初期化を行うには引数を明示的に渡す必要がある。

#### is-aの関係
継承はis-aの関係であるかどうかに基づいて利用すべきである。
<font color=red>子クラスは親クラスの一種である</font>というルールに則ってこそ正しい継承である。

## 抽象クラスとインターフェース

* abstract
詳細未定のメソッドを抽象メソッドとして宣言できる。

※抽象クラスはnewによるインスタンス化が禁止されている。
　継承元のクラスに詳細未定のメソッドが存在している場合、どこかで**オーバーライド**し、中身を実装する必要がある。

```java
public abstract void attack(Matango m) {
}
```

* interface
より抽象度の高い、基本的にフィールドを持たないクラスを「インタフェース」として特別扱いすることができる。
```java
public interface Creature {
   void run (); // public abstract は省略可能

}
```
インタフェースに宣言されたメソッドは、自動的にpublicかつabstractになる。

* implements
interfaceクラスを継承して子クラスを実装する。
```java
public class hill implements Creature {…}
```
* 多重構造
複数の親クラスを継承すること。
Javaでは基本的に多重構造が禁止されているが、インタフェースでは特別に多重構造が許されている。基本的に中身のないインタフェースの継承ならば、定義の重複により混乱を招くことがなく、オーバーライドによって継承先クラス独自の定義の実装が可能なため。

```java
public class PrincessHero implements Hero,Princess,Character{…}
```
* extends(拡張)
既存のインタフェースを継承して、機能を拡張する。
```java
public interfacde Human extends Creature {
// 追加メソッド
 void talk();
 void watch();
 void hear();
// ～　親クラスからの継承メソッドを実装する
```
<br>
<br>

以上、雑記メモ。次回『多能性』。
DHCPとは

# はじめに
毎週1本様々な技術に関する記事を投稿しています。
もし興味のある方は下のリンクから他の記事も見ていただければ幸いです。
 [@7_inai_](https://qiita.com/7_inai_)

# DHCPとは
`DHCP(Dynamic Host Configuration Protocol)は、ネットワーク上のデバイスにIPアドレスやその他のネットワーク設定情報を自動的に割り当てるためのプロトコル`です。この記事では、DHCPの基本的な概念、機能、およびその重要性について記載します。

# DHCPの役割
### ◾️IPアドレスの自動割り当て
DHCPの主要な役割の1つは、__ネットワーク上のデバイスに自動的にIPアドレスを割り当てる__ ことです。IPアドレスは、ネットワークデバイスが通信を行うために必要な重要な識別子であり、DHCPはこれを効率的に管理します。デバイスがネットワークに接続されると、DHCPサーバーは利用可能なIPアドレスのプールからデバイスにアドレスを割り当てます。

### ◾️サブネットマスク・デフォルトゲートウェイ・DNS設定
IPアドレスだけでなく、DHCPはサブネットマスク、デフォルトゲートウェイ、DNSサーバーのアドレスなど、__ネットワーク設定情報全般も提供__ します。これにより、デバイスは __正確なネットワーク設定を手動で入力する必要がなく、ネットワークの構成を容易__ にします。

# DHCPの動作原理
### ◾️DHCPクライアント
ネットワークデバイスは、DHCPクライアントとして動作します。これは通常、コンピュータ、スマートフォン、ルーター、ネットワークプリンターなど、ネットワークに接続されたデバイスです。DHCPクライアントはネットワークに参加する際に、DHCPサーバーから設定情報を要求します。

### ◾️DHCPサーバー
DHCPサーバーは、ネットワーク上のデバイスにIPアドレスなどの設定情報を提供する役割を果たします。ネットワーク内には通常、1つ以上のDHCPサーバーが存在し、それぞれが異なるIPアドレスの範囲を管理することができます。DHCPサーバーは、クライアントからのリクエストを受け取り、適切な設定情報を提供します。

### ◾️DHCPリース
DHCPはリースベースで動作します。これは、デバイスが一時的にIPアドレスを使用することを意味します。リース期間が終了すると、デバイスは新しいリースを要求する必要があります。この仕組みにより、ネットワーク管理者はリソースを効率的に管理できます。

# DHCPの利点
### ◾️ネットワーク管理の簡素化
DHCPを使用することで、ネットワーク管理者は手動でIPアドレスを割り当てる手間を省くことができます。これにより、__ネットワークの拡張や変更が容易になり、エラーの可能性が低減__ します。

### ◾️IPアドレスの効率的な利用
DHCPはIPアドレスの効率的な利用をサポートします。__デバイスがネットワークから切断された場合、そのIPアドレスはプールに戻り、他のデバイスに再利用__ されます。

### ◾️ミス構成の防止
手動でネットワーク設定を構成する場合、ミス構成が発生する可能性が高まります。DHCPを使用することで、正確な設定情報が提供され、ミス構成が減少します。

# 終わりに
DHCPは、ネットワークの効率性と管理性を向上させるために不可欠なプロトコルです。自動的にIPアドレスを割り当て、ネットワーク設定情報を提供することで、ネットワーク管理者は作業を簡素化し、ミス構成を減少させることができます。DHCPは、現代のコンピューターネットワークにおいて重要な役割を果たしており、その機能と利点はますます重要性を増しています。



最後までお付き合いくださりありがとうございました。
モノリシックアーキテクチャとマイクロサービスアーキテクチャ

# モノリシックアーキテクチャとマイクロサービスアーキテクチャ

## 目次
1. [はじめに](#はじめに)
2. [モノリシックアーキテクチャ](#モノリシックアーキテクチャ)
3. [マイクロサービスアーキテクチャ](#マイクロサービスアーキテクチャ)
4. [モノリシックとマイクロサービスの比較](#モノリシックとマイクロサービスの比較)
5. [Kubernetesとの組み合わせ](#Kubernetesとの組み合わせ)
6. [結論](#結論)
7. [付録: 他のアーキテクチャのリスト](#付録)

---
もちろん、詳細に加筆します。

---

# モノリシックアーキテクチャとマイクロサービスアーキテクチャ

## 目次
1. [はじめに](#はじめに)
2. [モノリシックアーキテクチャ](#モノリシックアーキテクチャ)
3. [マイクロサービスアーキテクチャ](#マイクロサービスアーキテクチャ)
4. [モノリシックとマイクロサービスの比較](#モノリシックとマイクロサービスの比較)
5. [Kubernetesとの組み合わせ](#Kubernetesとの組み合わせ)
6. [結論](#結論)
7. [付録: 他のアーキテクチャのリスト](#付録)

---

## はじめに
ソフトウェア開発のフィールドでは、アーキテクチャの選択が重要です。この記事では、モノリシックアーキテクチャとマイクロサービスアーキテクチャに焦点を当て、それぞれの特徴、利点、課題を詳しく検討します。

---

## モノリシックアーキテクチャ
### 定義
モノリシックアーキテクチャでは、一つのコードベースとして全ての機能が統合されます。これは伝統的なアーキテクチャとしてよく採用されてきました。

### 主な特徴と利点
- **簡単な開始**: 小さなチームや初期段階のプロジェクトには適しています。
- **統一性**: 全ての機能が一つの場所にあります。
- **デプロイの簡易性**: 一つのアプリケーションとしてデプロイできます。

### 限界と課題
- **スケーラビリティ**: 大規模化すると、一部をスケールすることが難しくなります。
- **長期的な保守性**: 一つの箇所でのエラーが全体のエラーとなる可能性があります。

---

## マイクロサービスアーキテクチャ
### 定義
個々の機能を独立したサービスとして分割し、これらが連携して全体のアプリケーションを構築します。

### 主な特徴と利点
- **スケーラビリティ**: 特定のサービスだけをスケールすることが容易です。
- **柔軟性**: 新しい技術や手法を特定のサービスに導入することが容易です。
- **障害の局所性**: 一つのサービスがダウンしても、他のサービスには影響しない。

### 実践例: Netflixのケーススタディ
Netflixは、2000年代初頭にはDVDのレンタルサービスとしてスタートしましたが、その後のオンラインストリーミングサービスへのシフトとともに、技術的な挑戦が増大しました。特に、多数のエンドユーザーへの配信、大量のコンテンツの取り扱い、そしてさまざまなデバイスへの対応が求められました。

### モノリシックからの移行
当初、Netflixはモノリシックなシステムを使用していました。しかし、ビジネスが急成長するにつれて、このモノリシックアーキテクチャは彼らのスケーラビリティや速度の要件を満たすことができなくなりました。この課題に対応するため、Netflixはマイクロサービスへと移行を始めました。

### マイクロサービスの導入
Netflixは、特定のビジネス機能ごとに独立したマイクロサービスを開発しました。これにより、各サービスがそれぞれのペースで進化し、デプロイされるようになりました。また、障害が発生した場合でも、他のサービスへの影響を最小限に抑えることができました。

### Netflix OSSとクラウドの活用
Netflixは、この変革を支える多くのオープンソースツールを開発し、公開しました。これには、サービスディスカバリ、回路ブレーカー、トラフィック管理などの機能が含まれています。また、全体のインフラをAWS（Amazon Web Services）上に構築することで、必要に応じてリソースをスケーリングする柔軟性を獲得しました。

### 結果とインパクト
マイクロサービスへの移行は、Netflixが迅速にイノベーションを行い、グローバルな規模でサービスを提供する能力を強化する上で、極めて重要な役割を果たしました。今日、Netflixは世界中で数億のユーザーにサービスを提供しており、その背後には効果的に運用されるマイクロサービスのエコシステムが存在しています。


---

## モノリシックとマイクロサービスの比較
- **開始のしやすさ**: モノリスはスタートアップや小規模なプロジェクトに適していますが、大規模なプロジェクトにはマイクロサービスが適しています。
- **スケーラビリティ**: マイクロサービスの方が高いスケーラビリティを持っています。
- **保守性**: モノリスは短期的には簡単に保守できますが、長期的にはマイクロサービスが有利です。

---

## Kubernetesとの組み合わせ
### マイクロサービスとKubernetesの相性
Kubernetesはコンテナオーケストレーションツールとして知られており、マイクロサービスの運用を大いに助けることができます。特に、自動スケーリングやサービスディスカバリなどの機能が役立ちます。

### デプロイメント戦略
リポジトリを分けることで、各サービスのライフサイクルを独立させることができます。一方、単一のリポジトリにまとめることで、一貫したデプロイプロセスを確保することができます。

---

## 結論
アーキテクチャの選択はソフトウェアの寿命や成功に直接的な影響を与えます。プロジェクトのニーズに応じて適切なアーキテクチャを選択し、その利点と制約を理解することが重要です。

---

## 付録: 他のアーキテクチャのリスト
- **サービス指向アーキテクチャ (SOA)**: 大規模なシステム間の連携を促進します。
- **サーバーレスアーキテクチャ**: リソースの動的な割り当てに基づきます。
- **イベント駆動アーキテクチャ**: イベントの発生によって処理を行います。
- **レイヤード（層状）アーキテクチャ**: 機能を層に分割して構築します。
- **ピアツーピアアーキテクチャ**: ノード間の直接通信に依存します。
基本情報：小学生でもわかる？アナグラム判定アルゴリズム

試験対策で新しい問題集を進めたところ、コードの意味と動きがわからない問題がありました。

アナグラム判定プログラムですが、簡単ながらハマリました。

## 問題：問題文は実行できません。

```python
class map:
    def __init__(self):
        self.dict = {}

    def put(self, key, value):
        self.dict[key] = value

    def get(self, key):
        return self.dict.get(key, None)

    def equals(self, other_map):
        return self.dict == other_map.dict

def chkanag(str1, str2):
    map1 = map()
    map2 = map()
    if len(str1) != len(str2):
        return False
    for i in range(len(str1)):
        # for str1
        if map1.get(str1[i]) is None:
            map1.put(str1[i], 1)
        else:
            map1.put(**問題A**)

        # for str2
        if map2.get(str2[i]) is None:
            map2.put(str2[i], 1)
        else:
            map2.put(**問題B**)
            
    return map1.equals(map2)

# テスト
print(chkanag("free", "reef"))  # True 
print(chkanag("hello", "world"))    # False
```

## 答え

問題A：str1[i], map1.get(str1[i]) + 1

問題B：str2[i], map2.get(str2[i]) + 1

答えを見てもいまいち意味が分かりません。

このmap1.put(str1[i], map1.get(str1[i]) + 1)は何をしているのでしょうか。

# アナグラムのアルゴリズムの開設

アナグラムのアルゴリズムは複数ありますが、こちらはClassを使いmapという配列に文字を格納して行うアルゴリズムです。

引数Aと引数Bの文字数が同じ場合に限り、各mapに文字の種類とその文字の出現回数を格納していき、引数Aと引数Bの文字の種類と出現回数が同じだった場合、アナグラムが成立します。

よくよく問題文を読めば回答できる問題でしたね(';')

## コード解説：

map1,map2ってなに？

文字と文字の出現回数を格納する配列です。

a,b,cだったら、

a:1 b:1 c:1

というキー文字と文字の発生数が格納されます。

### getとputって？

- putは引数をもとに配列に格納する
- getはその文字の発生回数を加算していくために使われます。

```python
if map1.get(str1[i]) is None:
            map1.put(str1[i], 1)
```

というif文では、そのi番目の文字がmapに格納されていないか確認して、

格納されていない場合、文字を入れます。

今回は最初の**a:1**をmap1に格納します。

```python
else:
   map1.put(str1[i], map1.get(str1[i]) + 1)
```

map1.get(str1[i]) + 1

という処理では、mapにすでに文字が格納されている場合、その文字のmapに格納されている数字を１する！という単純な処理になります。

a,a,bを呼び出した場合、

a:2 b1になるということです。

```python
if map2.get(str2[i]) is None:
            map2.put(str2[i], 1)
        else:
            map2.put(str2[i], map2.get(str2[i]) + 1)
```

でもstr2で同じ処理を繰り返します。

この関数をstr1[a,a,b] str2[b,a,b]で処理するとします。

上記の処理で、map1[a:2,b:1] map2[a:2,b:1]になります。

### equalsの処理

こちらの関数は、文字の数さえ合っていれば、配列の順番にかかわらず、「true」を吐き出します。

# 動くコード

```python
class map:
    def __init__(self):
        self.dict = {}

    def put(self, key, value):
        self.dict[key] = value

    def get(self, key):
        return self.dict.get(key, None)

    def equals(self, other_map):
        return self.dict == other_map.dict

def chkanag(str1, str2):
    map1 = map()
    map2 = map()
    if len(str1) != len(str2):
        return False
    for i in range(len(str1)):
        # for str1
        if map1.get(str1[i]) is None:
            map1.put(str1[i], 1)
        else:
            map1.put(str1[i], map1.get(str1[i]) + 1)

        # for str2
        if map2.get(str2[i]) is None:
            map2.put(str2[i], 1)
        else:
            map2.put(str2[i], map2.get(str2[i]) + 1)
            
    return map1.equals(map2)

# テスト
print(chkanag("reef", "free"))  # True (anagram)
print(chkanag("hell", "leff"))#false
```

以上参考になれば幸いです。
GNU nano  基本の操作

# 背景 
systemd, systemctlの操作をGUN nanoで編集したいが、操作方法がよくわからない
# GNU nanoの基本の操作
  
| 操作 | 操作手順 |
| ---- | ---- |
| 保存して終了 |  Ctrl + X のあとに Y |
| 保存せずに終了 | Ctrl + X のあとに Y |
| キャンセル | Ctrl + C |
| 上書き保存　| Crel + S |
| 名前をつけて保存 | Ctrl + O or F3|
| インデント | Tab | 
    
# 参考

https://ichiri.biz/tech/linux-nano-how-to/
1日のスケジュール管理Webアプリ「daydule」 - システム概要

## はじめに
みなさん、こんにちは！
最近はキャンプにハマっているエンジニアの@mellbrotherです。

今回は私たちが作成したスケジュール管理アプリdayduleの「システム概要」と「利用しているホスティングサービス」について紹介していきます。
ホスティングサービスについては、候補に上がった他サービスの情報も簡単に取り上げます。

（アプリ概要を知りたい方は、 前回の [1日のスケジュール管理Webアプリ「daydule」 - 概要](https://qiita.com/tom-takeru/items/edb78d86374fae24354b) を見てみてください）

## システム概要
![スクリーンショット 2023-10-04 19.13.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518208/d17c9452-3d35-fb0c-5b0f-18fa99135498.png)
dayduleはフロントエンドとバックエンドが分かれた構成となっています
システムを分けて開発している理由は大きく3つです
1. システムを分けることで責任範囲を分離できるため
色々なサイトで紹介されていることですが、考えることが減るのは良いことですね
フロントエンドはUI/UXに、バックエンドは業務ロジックやデータ操作に専念することができます
2. それぞれのシステムの変更が容易にできるため
システムが分かれているため、一方の変更が他方に影響を及ぼすことが少ないです（API定義などの変更の場合は、例外ですが...）
3. フロントエンドのReact、Next.jsを使ってみたかったため

お互いのシステムのことを考えなくて良い、ということはかなりメリットに感じています
フロントエンド側の変更がかなり多かったのですが、バックエンドのことをあまり気にしなくて良かったので、スムーズに改修が行えました

デメリットはあまりなかったのですが、強いて言えばホスティングサービスを2つ用意する必要があり、選定することに苦労したことですかね

この後、ホスティングサービスで何を使用しているか、他の候補などはあったのか、などについて書いていこうと思います

## ホスティングサービス
dayduleではフロントエンドに[Vercel](https://vercel.com/)を、バックエンドに[render](https://render.com/)を使用しています
DBはrender内で構築しており、postgreSQLを使用しています

フロントエンドはNext.jsを利用すると決まっていたので、デプロイの容易さなどを考えるとVercel一択でした
フリープランだと商業用に使えないので、そこはちょっと残念ですが、使いやすさはかなりよかったです（広告を貼っていきたいなと思っているので、現在は他のホスティングサービスに乗り換えることを検討しています）

バックエンドはrenderを利用していますが、こちらは消去法で選んだ形です
node.jsとpostgresSQLの組み合わせが無料で使えそうなのが、ここくらいしかありませんでした
無料なので仕方がない部分ですが、3ヶ月でDBがリセットされる仕様となっていました
3ヶ月ごとにバックアップ→DBを新規立ち上げ→バックアップしたデータを投入する、という作業が必要で、なかなか面倒です...

バックエンドのホスティングサービスで他に候補に上がったものも簡単に紹介します（今回はマイナーなものに絞って）
私たちのシステムには利用できませんでしたが、この記事を読んでいる方が作成するシステムにはマッチしているかもしれませんので、ご参考までに

- [Fly.io](https://fly.io/)
    - Rails/Laravel/Django/Javascriptが使える
    - MySQLやMariaDBを使える
    - 従課金制
- [Glitch](https://glitch.com/)
    - Node/Reactが使える
    - SQLiteが使える
    - 無料プランあり
    - 5分アクセスがないとスリープしてしまう
- [Qoddi](https://qoddi.com/)
    - Node.js/PHP/Python/Docker/Ruby/Django/Laravel/Java/Rust/Goが使える（かなり多い）
    - 無料プランあり
    - スリープなし
    - RDB周りが使えなそう

## 苦労したこと
ローカルでフロントエンドとバックエンドがうまく連携できないということは、あまりなかったのですが、本番環境にデプロイした瞬間、API呼び出しがうまくいかなくなりました

CORS関連のエラーでドメインが異なっているために失敗していたのですが、それになかなか気づけず、コード修正→デプロイ→動かない→コード修正→...の負のループを繰り返していました

ドメインを揃えると今までのことが嘘のようにすんなり動いていて、かなり時間を無駄にしたなぁと落ち込んでいたのが懐かしいです



## さいごに
今後もdayduleについて情報発信していきますので、もしよかったら「いいね」をお願いします。
意見・質問・感想があれば、コメントまで。

では、またの機会に！！
visibilityよりもisVisibleを積極的に使った方がいい理由

# こいつらは一体何
`visibility`も`isVisible`もAndroidにおいて**Viewの表示・非表示を制御するプロパティ**です。

たとえばidが`hoge_button`というボタン要素があったとしてこれを**表示状態**にしたい時、次のように書けます。

```kotlin
binding.hogeButton.visibility = View.VISIBLE
```

しかし、以下のようにも書けます。 :eyes:

```kotlin
binding.hogeButton.isVisible = true
```

**「え、じゃあどっちのほうがいいの？！」**

ってなりませんか。（私はなりました。）

# 結論
**`isVisible`を積極的に使おう！** です

## Booleanなので関係のない値が入ってしまう可能性が低い

たとえば`visibility`を使う場合、値はIntが入ります。

```kotlin
binding.hogeButton.visibility = View.VISIBLE
```

この`View.VISIBLE`の状態が実はIntなのです。

内部実装を覗いてみましょう。

```kotlin
public static final int VISIBLE = 0x00000000;

public static final int INVISIBLE = 0x00000004;

public static final int GONE = 0x00000008;
```

Int型の値ならなんでも入ってしまうことになります。

一方で、`isVisible`に入るのはBooleanの値です。

```kotlin
binding.hogeButton.isVisible = true
```

Booleanには`true`と`false`しかないので、いろんな値が入るわけではなく、安全性が一定担保されています。

## コードをきれいにまとめやすい

たとえば次のようなコードがあったとして

```kotlin
MatchButtonsLayout.visibility = if(it == UpperSwipeTab.Match) View.VISIBLE else View.GONE
SwipeButtonsLayout.visibility = if(it == UpperSwipeTab.Swipe) View.VISIBLE else View.GONE
```

これを`isVisible`を使って書き直すとこうなります。

```kotlin
MatchButtonsLayout.isVisible = it == UpperSwipeTab.Match
SwipeButtonsLayout.isVisible = it == UpperSwipeTab.Swipe
```

かなり見やすくなりましたね！

# 注意

ここまで読んだあなたは、`isVisible`最強ダネ〜となるかもしれませんが、`isVisible`にも若干の注意事項があります。

## isVisibleとisInvisible

実はViewの表示・非表示には次のような３つの状態があります。

- `VISIBLE`
    - 表示させる
- `INVISIBLE`
    - Viewが見えなくなる
    - しかし、そのViewが占めているスペースは保持されてる
    - つまり、Viewが**透明になる**感じ
- `GONE`
    - Viewが見えなくなる
    - Viewがレイアウト内から**完全に消える**
    - つまりそのViewが占めているスペースも消失する

`visibility`ではこの３つの状態を全て表現できますが、`isVisible`にはtrueかfalseしかないので`VISIBLE`と`GONE`までしか状態を表現できません。

実際に`isVisible`の内部実装を見てみましょう。

```kotlin
public inline var View.isVisible: Boolean
    get() = visibility == View.VISIBLE
    set(value) {
        visibility = if (value) View.VISIBLE else View.GONE
    }
```

`true`なら`VISIBLE`で`false`なら`GONE`になることがわかります。

`INVISIBLE`は表現できないのかと心配になりますが、そういう時は`isInvisible`を使います。

`isInvisible`の内部実装を覗いてみましょう。

```kotlin
public inline var View.isInvisible: Boolean
    get() = visibility == View.INVISIBLE
    set(value) {
        visibility = if (value) View.INVISIBLE else View.VISIBLE
    }

```

`true`の場合に`INVISIBLE`、falseの場合には`VISIBLE`になります。
従って、**`isVisible`だけでなく`isInvisible`も使う** と３つの状態全てが表現可能になります。
Djangoでカバレッジの取得と, Codecovでカバレッジ確認をできるようしてバッジを付ける

# django でカバレッジを確認する方法

coverage.py を使用することで確認できる.
とりあえずインストール

```bash
pip install coverage
```

djangoでテストを書いている前提で進める.
テストの書き方は下記を参考にすると良い

https://zenn.dev/tmasuyama1114/articles/django-how-to-unit-test

通常は以下のように実行してテストを行うと思う
```bash
python3 manage.py test
```

カバレッジを取得するときは,
```bash
coverage run --source='.' manage.py test
```

これでカレントディレクトリ以下のファイルはすべて対象にしてカバレッジを取得する.
カバレッジを確認するには,

```bash
coverage report
```

で確認可能
ここでCodecov の連携は以下のサイトから GitHub と連携しておく必要がある.

https://github.com/apps/codecov

これを登録すると settings から "CODECOV_TOKEN" という鍵が取得できるため
予めGitHubのSecretに登録しておく.

使用するdocker comopose ファイルは以下のようになる.
今回coverage サービスしか記載していないが本来はいくつかサービスがある.
自分の環境に合わせて追加してください.
(DBとか)



```yaml:docker-compose.yaml
version: "3.3"

services:
  coverage:
    build:
      context: .
      dockerfile: Dockerfile
    environment:
      - REDIS_URL=redis://redis:6379
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - DATABASE_URL=postgres://user:password@db:5432/db
      - DJANGO_DEBUG=True
      - CODECOV_TOKEN=${CODECOV_TOKEN}
    command: bash -c "coverage run --source='.' manage.py test &&
      coverage xml && 
      codecov -f coverage.xml"
    volumes:
      - .:/code
```

上記で参照している Dockerfile
```dockerfile:Dockerfile
# DockerのPython公式イメージをベースにする
FROM python:3.10

# 環境変数の設定
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# 作業ディレクトリを作成
WORKDIR /code

# requirements.txtをコピー
COPY requirements.txt /code/

# 必要なパッケージのインストール
RUN pip install --upgrade pip && pip  install -r requirements.txt
```


そして.github/workflowsディレクトリにcoverageを取得するワークフローファイルを置く

```yaml:coverage.yaml
name: Coverage

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
env:
  OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1


      - name: Cache Docker layers
        uses: actions/cache@v2
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ github.sha }}
          restore-keys: |
            ${{ runner.os }}-buildx-

      - name: run coverage
        run: |
          docker-compose -f docker-compose-coverage.yaml up --build --exit-code-from coverage

```

これで自動的にcodecovにカバレッジがアップロードされる.

# バッジの追加
これが一番やりたかったこと
Codecovの対象となるリポジトリページに有る Settings -> Badges & Graphs からCodecov Badgeの部分を自分のリポジトリの README.md に貼り付けるとカバレッジ率が確認できる.


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141779/4d07f223-a742-c7b8-cb63-9d6b2047379a.png)

# 参考文献


https://zenn.dev/ikemo/articles/pytest-parallel-coverage

https://docs.codecov.com/docs/github-2-getting-a-codecov-account-and-uploading-coverage

https://hiroronn.hatenablog.jp/entry/20200919/1600510512

https://qiita.com/nasum/items/aff9bf09d49b136bbf94
ピボットのステップでの順番 / Tableau Prep

# ピボットの手順で想定しない結果が起きる！

## 現象

### ピボットしてから不要な項目を削除
グルーピングの項目（最終選択項目）ごと集計がされていない！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531257/583bd003-cb41-1ce2-13ff-af94fb37efb0.png)

### 不要な項目を削除してからピボット
グルーピングの項目（最終選択項目）ごと集計がされている！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3531257/38d8f171-74f7-5595-fb56-108a5990b885.png)

## 当たり前だけど、注意点

ピボットをやるときに、不要な項目を削除してから、ピボットしないと値が集計されない現象になります。
変更は、変更履歴の上からの順番から実行されます。順番を意識した構築が必要であり、あれ！？集計がおかしいと思ったら、変更のタブを見て手順が想定どおりかチェックしてみてください。
メッセージ認証とディジタル署名

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457284/64493a8e-13fa-d922-e8de-873029c4b700.png)
　メッセージ認証は、文書の内容が改ざんされていないかどうかを検証する技術です。メッセージ認証の一つにディジタル署名があります。ディジタル署名は、印鑑と同じ役割を担っています。文章にディジタル署名を付与すると、内容が第三者によって改ざんされていないことや、文書の作成者が署名を行った本人であることを確認できます。ディジタル署名は、メッセージダイジェスト関数（ハッシュ関数）と暗号技術を利用して付与します。

１：メッセージダイジェスト関数
　文書が改ざんされていないかどうかを確認するためにメッセージダイジェスト関数を利用します。メッセージダイジェスト関数はハッシュ関数の一種です。メッセージダイジェスト関数は、次の特徴を持っています。
・入力値のサイズと関係なく一定サイズの出力値が得られる。
・入力が異なれば、同じ出力値が出る可能性は極めて少ない。
・出力から入力を求められることが極めて難しい。
・すでに与えられている出力値と同じ出力値となる別の入力を見つけることが極めて難しい。
これらの特徴から、メッセージダイジェスト関数が利用する場合は、
出力値が同じであれば入力は同じとして扱います。出力値は、文書の「指紋」の役割をするのです。なお、出力値をメッセージダイジェストと呼ばれます。

２：ディジタル署名
　ディジタル署名は、メッセージダイジェストを署名者の秘密鍵で暗号化したものです。
　ディジタル署名の付与の手順として、
①メッセージをメッセージダイジェスト関数（ハッシュ関数）に入力して、メッセージダイジェストを作る。
②メッセージダイジェストを署名者Aの秘密鍵で暗号化し、ディジタル署名とする。
③メッセージとディジタル署名をまとめて１ファイルとして送信する。

　ディジタル署名が付与さえrている文書を受けった人は、ディジタル署名を検証して、
・内容が第三者によって改ざんされていないか
・文書の細工性者が署名を行なった本人であるか
を確認します。

ディジタル署名は次の手順で検証します。
④ファイルからメッセージ部分を取り出して、メッセージダイジェスト関数（ハッシュ値）に入力してメッセージダイジェストを作る。
⑤ファイルからディジタル署名を取り出して、署名者A
の公開鍵で復号し、メッセージダイジェストを入手する。
⑥④のメッセージダイジェストと⑤のメッセージダイジェストを比較し、一致することを確認する。

⑥で、２つのメッセージダイジェストが一致しない場合は、文書が改ざんされているか、文書の作成者が署名を行なった本人でないかのどちらかです。どちらにしても、そのような文書は不要ですから破棄してしまい、再度Aに送り直してもらうよう要求すれば良いです。
　なお、ディジタル署名を付与してもメッセージが暗号化されるわけではありません。メッセージを第三者に読まれないようにするには、ディジタル署名を付与した上で、さらに、メッセージを暗号化する必要があります。

３：公開鍵署名書と認証局（CA）
　Aのディジタル署名を検証するには、Aの公開鍵が必要です。公開鍵ですrから必要に応じてAからもらうことができます。しかし、BはAに成り済ました何者かに騙され、Aの偽公開鍵を取得してしまうかもしれません。Aの偽公開鍵では、署名の認証は正しく行えません。
　このような場合、中立的な第三者が、「確かにこの公開鍵はAの公開鍵である」という保証をすれば、騙される危険性は減ります。公開鍵が確かなものであるということを保証する文書が公開鍵証明書（ディジタル証明書）です。公開鍵証明書を発行する組織を認証局（CA）と言います。CAは、Aの公開鍵情報にCAの秘密鍵を使って署名を付与し、公開鍵証明書を発行します。公開鍵証明書はX.509という形式を使います。また、執行した公開鍵証明書の一覧（CRL）も発行します。

　Bは、はじめにAの公開鍵署名書を受け取り、公開鍵証明書の内容を検証します。その後、公開鍵証明書に記載されていたAの公開鍵を利用して、Aのディジタル証明書を検証すれば良いのです。

　CAによって公開鍵の正当性を確保し、セキュリティレベルを高めた環境のことを、PKI（公開鍵基盤）と言います。
【Swift】FirebaseStorageを使う

# はじめに
FirebaseStorageを初めて使ったので、アップロードと削除の基本的な実装を記録しておきます。

# アップロード
```swift
let folderName = "folder"
let imageName = "image"
let storage = Storage.storage()
let storageRef = storage.reference()
let imageRef = storageRef.child("\(folderName)/\(imageName).jpg")
imageRef.downloadURL { url, error in
    if let error {
        print("error:", error)
    } else if let url {
        print("url:", url)
    }
}
```

# 削除
```swift
let folderName = "folder"
let imageName = "image"
let storage = Storage.storage()
let storageRef = storage.reference()
let imageRef = storageRef.child("\(folderName)/\(imageName).jpg")
imageRef.delete { error in
    if let error {
        print("error:", error)
    }
}
```

# おわり
次回実装する時用に記事に出来て良かったです：）
【Rails6】 既存AppにTailwindCSSを導入しよう!

# はじめに
👋こんにちは！Web系の企業に勤務し、地方からフルリモート勤務をしている@takakouと申します🏙️✨

今回は、「**【Rails6】 既存AppにTailwindCSSを導入しよう!**」というテーマで記事をシェアします！📝

この記事を執筆しようと思った経緯としては、私自身が **既存のRails6のアプリケーション**に**TailwindCSS**を導入する記事があまり見つからなかったからです 。💡

そこで、同じ疑問を持つ方の解消ができればと思い執筆しました🌟

記事執筆は未熟者で、至らない点もあるかと思いますが、皆さんのコメントやフィードバックをお待ちしています！🚀💬
# 目次
<!-- タイトルとアンカー名を編集 -->
1. [対象者 👨](#1-対象者)
1. [動作環境 🖥️](#2-動作環境)
1. [導入手順 🚀](#4-導入手順)
1. [参考文献 📚](#5-参考文献)
1. [おわりに 🎉](#6-おわりに)
<!-- 各チャプター -->

# 1. 対象者

* **Rails6**の既存AppにTailwindCSSを導入したい方

# 2. 動作環境

## 端末
- PC : **MacBook Air(M1,2020)**

- RAM : **8GB**

- OS : **macOS Monterey(ver12.1)**

## バージョンなど

- Ruby : **3.1.2**

- Rails : **6.1.7**


# 3. 導入手順
こちらの章では導入手順について説明していきます。

:::note info
**前提**
公式のリファレンスを参考にしています。
:::

https://tailwindcss.com/docs/guides/ruby-on-rails

## Gemfileに記述を追加
```diff_ruby:Gemfile
+ gem 'tailwindcss-rails'
```

## bundle install
```shell:Terminal
bundle install
```

## TailwindCSSをinstall
```shell:Terminal
rails tailwindcss:install
```

## ファイルの確認
基本的にファイルが自動生成、記述が追加されますが、念のため確認をします。

```diff_javascript:config/tailwind.config.js
+ const defaultTheme = require('tailwindcss/defaultTheme')
+
+ module.exports = {
+  content: [
+    './public/*.html',
+    './app/helpers/**/*.rb',
+    './app/javascript/**/*.js',
+    './app/views/**/*.{erb,haml,html,slim}'
+  ],
+  theme: {
+    extend: {
+      fontFamily: {
+        sans: ['Inter var', ...defaultTheme.fontFamily.sans],
+      },
+    },
+  },
+  plugins: [
+    require('@tailwindcss/forms'),
+    require('@tailwindcss/aspect-ratio'),
+    require('@tailwindcss/typography'),
+    require('@tailwindcss/container-queries'),
+  ]
+ }

```
```diff_scss:assets/stylesheets/application.tailwind.css
+ @tailwind base;
+ @tailwind components;
+ @tailwind utilities;
```
```diff_erb:views/layouts/application.html.erb
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

+    <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>

```

## アプリケーション再起動
:::note warn
**注意点**
アプリケーション起動中のままの人は再起動してください。buildされるのでアプリケーション再起動後にはTailwindCSSが問題なく適用できるはずです。
:::
```shell:Terminal
bin/dev
```

## classを適用
あとは自分が使いたい**TailwindCSS**のclassを適用させていくだけです。公式リファレンスで自分が適用したいclassを調べて実装してみましょう

https://tailwindcss.com



# 4. 参考文献

https://tailwindcss.com/docs/guides/ruby-on-rails


https://kt-tech.blog/blogs


<a id="#end"></a>
# 5. おわりに

今回の記事では、 **既存のアプリケーションににTailwindCSSを導入** をする方法について説明しました💾

もし興味を持っていただけましたら、ぜひ他の記事も読んでいただけるとうれしいです！ 📖🌟

(最後に、気づいた方もいらっしゃると思うのですが、絵文字を入れるのは苦手なので、文章に沿った絵文字を挿入するのに、ChatGPTを利用させていただきましたが、いかがでしたでしょうか。不快に思われた方がいたら申し訳ございません 🙇‍♂️)
TypeScript登場の背景

## TypeScript登場の背景
:::note
TypeScriptが多くの人に利用されるようになった主な要因
・開発生産性の高い静的型付け言語であること
・JavaScriptの文法をそのまま拡張する上方互換言語であること
:::

TypeScriptはMicrosoftが中心となって開発を進めている、オープンソースのプログラミング言語。JavaScriptを拡張する形で設計されており、静的な型付け機能を搭載した、いわゆるAltJSで、モダンフロントエンド開発のスタンダードになっています。

#### JavaScriptの爆発的人気
年々Webアプリケーションの世界は進化している。その中で世界中で稼働するWebアプリケーションのフロントエンドはJavaScriptで記述されています。Webブラウザが直接解釈し実行できる唯一のプログラミング言語がJavaScriptであったため、JavaScriptは爆発的に人気を集めました。


一方で、進化を続けるWebアプリケーションの進化に対して、JavaScriptには機能的に不完全・未成熟なところも多くあり、ECMAに代表される標準化団体により、仕様の策定が繰り返されていました。

#### AltJSの登場
ただしJavaScriptは後方互換性を重視する言語で、仕様の抜本的な変更は難しい。例えば、JavaScriptの言語仕様に直ぐに型を取り込むことは現実的に難しい。
こういった課題を解決するために、**AltJS**と呼ばれる、JavaScriptに変換できるプログラミング言語が人気を集めました。AltJSはある言語をJavaScriptに変換する仕組みをもち、JavaScriptの文法上の制約を一部回避できる。


#### MicrosoftによるTypeScriptの開発
大規模なWebアプリケーション開発において、型のないJavaScriptは課題が多い言語になる。コンパイル時にエラーを検出できず、実行時にバグが生みやすい状況だった。特に大人数での開発では型のないことで意図しないバグが生じることが多かった。

そこで、静的型付け言語を支持するMicrosoftにより、2014年ごろ、JavaやC＃などを参考に、JavaScriptの機能を拡張した静的型付け言語TypeScriptが考案された。

TypeScriptによるコンパイル時のエラー早期発見により、開発コストを下げることも実現。またコードの安全性の向上も期待されます。


#### 上方互換言語
上方互換とは、TypeScriptにおいてJavaScriptはそのまま動作するように作られている。このため、これまでJavaScriptで作られていた大規模なアプリケーションを徐々に移行する形で導入が可能。この親和性の高さから、Javascriptの知識があれば、TypeScriptをそこまで導入コストをかけずに使い始めることができます。




----

参考文献
・TypeScriptとReact/Next.jsで作る実践Webアプリケーション開発

https://www.amazon.co.jp/TypeScript%E3%81%A8React-Next-js%E3%81%A7%E3%81%A4%E3%81%8F%E3%82%8B%E5%AE%9F%E8%B7%B5Web%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA-%E6%89%8B%E5%B3%B6-%E6%8B%93%E4%B9%9F/dp/4297129167
Rのterra::viewshed()で可視領域分析

terraパッケージで可視領域分析を行います。DEMは国土地理院基盤地図情報数値標高モデル（10mメッシュ）による北海道南西部厚沢部町周辺の領域です。


```R
# パッケージ読み込み
library(terra)
library(tidyverse)
```
読み込んだDEMは下記のようなものです。
```R
plot(dem)
```
![01取り込んだDEM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/6ec21a4a-e202-f451-73fa-0ec4b151f43d.png)


視点場を設定します。視点場は厚沢部町名物、山頂で飛び跳ねると太鼓の音がする太鼓山山頂です。
```R
# 視点場座標をデータフレーム化
x <- 436777
y <- 4641166
locPoint <- 
  bind_cols(x, y) %>% 
  rename(x = ...1, y = ...2) 
# 視点場座標データフレームをSpatVector形式に変換
location_point <- 
  vect(locPoint, 
       geom = c("x", "y"),
       crs = "epsg:3100")
```

可視領域分析の実行

```R
view <-
  terra::viewshed(dem,
           loc = c(locPoint$x, locPoint$y),  # 視点場の位置
           observer = 1.7)    # 地上高
plot(view)
```
![02可視領域ラスタ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/c759cfb1-470e-c530-dada-b0d3e115f5cb.png)

ラスタで出力された可視領域をポリゴン化します。
```R
# 可視領域ラスタをポリゴン化
view_poly <- as.polygons(view)
# 可視領域部分のみを抽出
view_poly_true <- view_poly[view_poly$viewshed==1]
```
```R
# プロット
terra::plot(dem)
terra::polys(view_poly_true,
      col = "grey40",
      border = NA)
terra::points(location_point,
              col = "#771122",
              cex = 1)
```
![03DEMに重ね書きした可視領域ベクタ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60438/97cc1111-6c6c-0597-28a8-350c0d34111c.png)
毎日AtCoder~B埋め～


##### ABC320 25min 解答参照
```python
s = input()
count = 1

# 外側のループ (i)
for i in range(len(s) - 1):
    # 内側のループ (j)
    for j in range(i + 1, len(s)):
        # 部分文字列を取得
        sub_str = s[i:j+1]

        # 部分文字列が回文であるかチェック
        if sub_str == sub_str[::-1]:
            count = max(count, j - i + 1)

# 最長の回文部分文字列の長さを表示
print(count)
```
1. 前から順に部分文字列を取得
2. 部分文字列が回文であるかをチェック
3. iは回文の開始地点、jは回文の終了地点を表している。j-i+1で回文の長さがわかる
4. max関数を使うことで、それまでのcountと現在の回文の長さを比較してより長いほうをcountに保持できる

###### ABC319 53min 自力
```py
N=int(input())
j_list = []
ans_list=[1]#0を割ったときの結果を先に入れておく
#10以下の約数リストの作成
for i in range(1,10):
  if N%i == 0:
    j_list.append(i)
#0を除く１～Nについて
for i in range(1,N+1):
  for j in j_list:
    L=i*j
    if L%N == 0:
      ans_list.append(j)
      break
 #if内条件を満たさなかった場合
  else:
      ans_list.append('-')
print("".join(map(str, ans_list)))
```
##### ABC318 9/20 解説参照 50min
```python
n=int(input())
num_set = set()
for _ in range(n):
  x1,x2,y1,y2 = map(int,input().split())
  for a in range(x1,x2+1):
    for b in range(y1,y2+1):
      num=str(a)+str(b)
      num_set.add(int(num))
print(len(num_set))
```
最初はこれでやろうとした。
三重ループになる点、(1,120)と(11,20)が同じとみなされてしまう点などが問題となり断念。
```python
N = int(input())
ss = []
for _ in range(N):
    A, B, C, D = map(int, input().split())
    ss.append((A, B, C, D))
c = set()
for s in ss:
    for x in range(s[0], s[1]):
        for y in range(s[2], s[3]):
            c.add((x, y))
S = len(c)
print(S)
```
これならいけた。数字として見るなんてめんどくさいことせずにタプルとして扱えばよかった。
```python
N = int(input())
sheets = []

for _ in range(N):
    A, B, C, D = map(int, input().split())
    sheets.append((A, B, C, D))


grid = [[0] * 101 for _ in range(101)]


for A, B, C, D in sheets:
    for x in range(A, B):
        for y in range(C, D):
            grid[x][y] = 1


total_area = sum(sum(row) for row in grid)
```
シートを作ってやるならこのやり方も。

###### ABC317 20min 自力
```python
n=int(input())
num=list(map(int,input().split()))
num.sort()

for i in range(num[0],num[0]+n+1):
  if not i in num:
    print(i)
    exit()
print(num[0])
```
BizRobo!でWebページ上のポップアップ画面から処理を分岐させる方法は

就業先では、RPAツールのBizRobo!を使用しています。BizRobo!開発で悩み、BizRobo!ナレッジベースで日々検索しています。

Webシステムから毎日ファイルをダウンロードすることは、RPAではよくあるケースです。
もし該当データが存在しない場合に、画面上で【データがありません】のようなポップアップが表示されるWebシステムがあります。そのようなポップアップ画面が発生した場合でも、エラーにせず処理を進める方法を調べる必要がありました。

今回はWebページ上のポップアップ画面から処理を分岐させる方法について、共有いたします。

※時間が経過して変わってしまった箇所や間違っている箇所、不適切な表現があるかもしれませんが、その時は都度修正いたしますので、ご指摘いただけると幸いです。

## BizRobo!のDSで行うには
:::note info
株式会社TBOは、企業情報や株価情報、競合店の商品情報など様々なレポートをダウンロードしている。毎日ダウンロードし、年月日ごとにフォルダを用意し、分析のデータとして活用している。
レポートをダウンロードするWebシステムの中には、データが存在しない場合に、画面上で【データがありません】のようなポップアップが表示されるものがある。
上記のようにWebシステムからレポートをダウンロードする際、ポップアップ画面が表示されたとしても、RPAがエラーにならないようにする。
:::
上記のようなケースを想定しています。BizRobo!のDSでどうやって対処するのか調べました。

まずBizRobo! eラーニングサイトで学んだことを思い出しました。
eラーニングサイトで、BizRobo! GYMの「ファイルのダウンロード」というページからPDFファイルをダウンロードし、ファイルに記載された「BizRobo GYM!」という文字列を取得するという演習があるため、役に立つと考えました。

※BizRobo! GYMとは、eラーニングサイトの演習のために用意されたサイトです。
[BizRobo! GYM](https://community.bizrobo.com/gym/file-download)

しかしこの演習の場合、PDFボタンを押下すると、別タブでPDFを開くため、ポップアップ画面は出ません。この演習問題のロジックは使えませんでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955689/9436f081-c097-a32f-fe0c-57214201f3e5.png)

そのため、別の方法を探す必要がありました。色々と調べたりした結果、下記ポイントで作るのがいいと考えました。
```
ポイントは、下記3点です。
・トライステップ（エラーのときの分岐用ステップ）
・ポップアップ画面上にあるテキストを変数に格納
・該当のテキストが抽出されているか変数判定
```
ポップアップ画面とは、あるボタンをクリックすると自動的に起動される、メイン画面とは別の画面を指します。
※Googleの設定を変えると、下記のような「名前を付けて保存」という画面を出すことができます。下記は正確にはポップアップ画面と違いますが、イメージは一緒のため例として取り上げてました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2955689/5c46865d-539a-c144-3f57-02b4798383a6.png)

上記の画面をBizRobo!でクリックすることができませんでしたが、Webサイトの中にはポップアップ画面をクリックすることができる時もあります。その時は、ポップアップ画面からテキストを抽出し、変数に格納できます。変数に格納できれば、テストステップで判定できるので、エラー処理を組み込むことができます。


## BizRobo!のDAで行うには
私の就業先では、DAを用いた開発は行っていないため、DAでのやり方がわかりませんでした。
しかし、調べてみたところ、下記サイトが参考になると思うため、共有します。
[【Bizrobo !】GuardedChoiceの利用方法を知ろう](https://kyosopras.biz/blog/1119/)
[Guarded Choice（ガードチョイス）の特徴](https://knowledge.bizrobo.com/hc/ja/articles/360032997271)

ポイントは、GuardedChoiceを利用して、ウィンドウが消えるのを確認することや何秒か待機することを設定することだと推測しています。


## 最後に
BizRobo!でWebページ上のポップアップ画面から処理を分岐させる方法について、ご紹介しました。

BizRobo!でWebページからファイルをダウンロードすることがあるならば、ポップアップ画面の処理で悩むケースもあると思います。今回調べたことを活かしていきたいと考えています。

今回のことをBizRobo! LAND Communityで調べた際、とても有意義な情報が見つかりました。こういう時にBizRobo! LAND Communityは役立つなと実感しました。

今回の記事が、RPA開発で悩んでいる人の為になれば幸いです。
Dr.SumのSQLクエリとビュー

# はじめに
Dr.SumのデータベースではSQL文を使用してデータを抽出・集計したり、登録・更新・削除などを行うこともできます。
また、Dr.SumにはDr.Sumビューというものがあり、SQLのViewのようにDr.Sumテーブル同士を結合したりWhere条件を付けてデータをselectし表示することが可能で、Dr.SumレポートやMotionBoardのボードでDr.Sumビューを参照することも可能です。
ここでは、Dr.SumビューやEnterprise ManagerからSQL文を使ってDr.Sumのデータを抽出する際に個人的に気を付けていることなどを備忘録として残します。

:::note info
補足
本記事の内容・キャプチャ等はDr.Sum Ver.5.6のものです。
設定方法など詳細は[公式Dr.Sum Ver.5.6 マニュアル](https://cs.wingarc.com/manual/drsum/5.6/ja/UUID-4ef8ef8f-69db-fdd4-11ce-66faaf2b299e.html)を参照してください。
:::

# Enterprise Manager SQL ExcecutorでSQL文を実行する

Dr.Sum Enterprise Managerを起動して、「SQL」ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2780637/f5e939b1-85cc-12af-c99e-70d8fa37e5c8.png)

SQL Excecutorが開くので、直接SQL文を入力し、SQL実行ボタンをクリックすると入力したSQL文が実行されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2780637/3e54044d-d738-7952-7fc7-ffdfa3d25b75.png)

詳しい使い方は「[マニュアル＞SQL ExecutorによるSQL文の実行](https://cs.wingarc.com/manual/drsum/5.6/ja/UUID-860bee2b-6e6d-caf0-4318-f761c07b89c7.html)」を参照ください。

# Dr.SumのSQLコマンドと関数について

Dr.Sumで使用できるSQLコマンドと関数はそれぞれマニュアルを参照してください。
DML、DDLの基本的な処理や関数は対応しています。

「[マニュアル＞SQLコマンド](https://cs.wingarc.com/manual/drsum/5.6/ja/UUID-79765596-8888-d9bd-cca7-1b8374cc768b.html)」
「[マニュアル＞関数](https://cs.wingarc.com/manual/drsum/5.6/ja/UUID-4152c606-df91-de0b-c52f-c00dfb6144f3.html)」

# Dr.Sumビューを作成する

冒頭でもお話しした通り、Enterprise ManagerでDr.Sum上にビューを作ることが可能です。
エディタを使ってビューを作成する方法は「[マニュアル＞ビューを新規作成する](https://cs.wingarc.com/manual/drsum/5.6/ja/UUID-03d35b3f-85b0-4048-c122-580120e29e3c.html)
」を参照してください。
また、SQLのCREATE文でビューを作成することもできますが、その場合はエディタでの編集はできません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2780637/10fcc052-ef28-f438-1a3c-92a80d7ed1f0.png)

# Dr.Sumビューの使いどころ

個人的に関わっている案件では、Dr.Sumへのデータ連携元としてDWH（SQL Serverなどが多い）がある構成になっているケースがほとんどですので、DWH側でVIEWを作成してそのselect結果だけをDr.SumのDBに取り込むことが多いです。その理由は、
・　レポートやボードからデータソースとしてDr.Sumビューを呼び出すよりもDr.Sumテーブルを呼び出すほうが集計や表示速度などのパフォーマンスが出る
・　DWH側でビューが作れれば、Dr.Sumのビューの作り方が分からない人でもビューを修正してDr.Sumに連携するデータを修正できる
といった点です。

逆にDr.Sumビューを使っているケースとしては、
・　データ転送量が多く（2000万件超）、何回かに分けてデータ転送する場合に、転送結果を結合して一つのデータソースとして扱いたい場合
・　レポート上で表示するデータをログインユーザー毎に絞り込み表示したい場合
などがあります。

Dr.SumのSQLクエリとビューの紹介は以上となります。
機能を使いこなせばDr.Sumでもかなりのことができますが、作り込みすぎると作った本人が居なくなった場合などにブラックボックスになる可能性はあるので、プロジェクト内で仕様はなるべく共通化することをお勧めします。
【Python 備忘1】print 関数

# Print 関数
ここでは、文字列や数値などのオブジェクトを出力するための __print関数__ について記載していきます。
単に __print__ といっても、さまざまな出力方法があります。
基本的な使い方は普段使いますが、用途が限られた使い方もたくさんあるので、
用途にあった出力方法をメモしておきます。

# 目次
* 通常出力
    * 数値出力
    * 文字列出力
    * 変数出力
    * 複数出力
* オプション


# 通常出力
### 数値出力
数値を出力する場合は、そのまま記載します。
```python:test.py
print(10)
```
```text:実行結果
10
```

### 文字列出力
文字列を出力する場合は、ダブルクォーテーション「 __"__ 」 や シングルクォーテーション「 __'__ 」 で、
囲んで記載します。
```python:test.py
print("Hello World")
```
```text:実行結果
Hello World
```

### 変数出力
数値や文字列、リストや辞書が格納された変数を記載することで、
それらを表示することも可能です。
```python:test.py
number = 10
strings = "Hello World"
lists = [10,"Hello World"]
dicts = {'one': 1, 'two': 2, 'three': 3}
print(number)
print(strings)
print(lists)
print(dicts)
```
```text:実行結果
10
Hello World
[10, 'Hello World']
{'one': 1, 'two': 2, 'three': 3}
```

### 複数出力
文字列と数値を共に出力したい場合は、数値を __str()__ 関数で文字列に変換する必要があります。
```python:test.py
age=10
print("私は" + str(age) + "歳です.")
```
```text:実行結果
私は10歳です.
```
str()関数を使用しない場合は下記エラーが出力されます。
```python:test.py
age=10
print("私は" + age + "歳です.")
```
```text:実行結果
Traceback (most recent call last):
  File "test.py", line 2, in <module>
    print("私は" + age + "歳です.")
          ~~~~~^~~~~
TypeError: can only concatenate str (not "int") to str
```

# オプション
__print__ 関数にはいくつかのオプションがあります。

### sep：区切り文字の指定（デフォルト：半角スペース）
__print__ 関数で複数のオブジェクトを同時に __print__ した場合、それぞれ半角スペースで区切られます。
この区切り文字を変更する際に使うのが、__sep__ オプションです。

```python:test.py
print("Hello","World")
print("Hello","World",sep=",")
print("Hello","World",sep="")
print("Hello","World",sep="\n")
```
```text:実行結果
Hello World
Hello,World
HelloWorld
Hello
World
```

### end：末尾の文字の指定（デフォルト：改行）
__print__ 関数でオブジェクトを出力すると、末尾に改行が出力されます。
出力内容の末尾を変更する際に使うのが、__end__ オプションです。

```python:test.py
print("Hello")
print("World")
print("Hello",end="")
print("World")
print("Hello",end=" ")
print("World")
```
```text:実行結果
Hello
World
HelloWorld
Hello World
```

### file：出力先の指定（デフォルト：標準出力）
__print__ 関数でオブジェクトを出力すると、
pythonを実行したコンソール上に内容が出力されます。
出力先を指定することができるのが、__file__ オプションです。

```python:test.py
with open('test.txt','w') as f:
    print("Hello World at console")
    print("Hello World at text file",file=f)
    print("Hello World at text file",file=f)
```
```text:実行結果
Hello World at console
```
```text:test.txt
Hello World at text file
Hello World at text file
```

### flush：バッファリングなしで出力するかどうかを指定：（デフォルト：False）
処理に時間がかかってしまうような状況で__print__ 関数でオブジェクトを出力する際、
なかなか出力されないことがあります。
そんな時にバッファリングなしで出力することができるのが、__flush__ オプションです。
※出力する内容は特に変わりません。

```python:test.py
print("Hello World")
print("Hello World",flush=True)
```
```text:実行結果
Hello World
Hello World
```

# あとがき
ここまで見てくださり、ありがとうございました。
普段pythonを使ったり使わなかったりしていますが、
ふと使った際に、これどうやるんだっけ、とか スペルどうだったっけ（英語苦手です…）と
なってしまうことが多々あったので、備忘としてメモのように残していこうと思います。
このような記事を書くのは初めてのことなので、これから少しずつ慣れていけたらなと思っています。
FBX Conveterでファイルが開けない（Mac）

# Addを押してもダイアログは出るが追加されない

![fbx-exporter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128851/b781a2b3-3572-84df-4a90-b7de256956b6.png)

環境：
macOS Ventura 13.5.2

## 解決方法

ドラッグ＆ドロップで追加された。
Macのターミナルでcode. を実行したらエラーになった



## ターミナルで code. を実行しようとしたら、、、

こんなエラーが出ました。

~~~
/usr/local/bin/code: line 6: python: command not found
/usr/local/bin/code: line 10: ./MacOS/Electron: No such file or directory 
~~~

原因はいくつかあります。1つだけ当てはまる人もいれば、全て当てはまる人もいるかもしれません。
また、1つの原因から付随して他の問題も解決する必要がある場合もあります。

## 原因
### 1、最新状態になったmacOSがPythonを削除している。
macOS Monterey 12.3 で Python 2.7 が削除されたため python コマンドが見つからない状態になっているようです。

https://developer.apple.com/documentation/macos-release-notes/macos-12_3-release-notes

### 2、VScodeアプリがダウンロードディレクトリにある

通常、このようなアプリはアプリケーションフォルダに格納されています。

ちなみに私は、 **なぜかVScodeが3つもあって重複していました。**　
過去の自分が意味不明なことをしていたようです（笑）

### 3,VScodeが最新版でない。

https://github.com/microsoft/vscode/issues/7426

![スクリーンショット 2023-09-19 16.18.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1625911/ae96b188-5b00-97eb-4355-a1f4882374d0.png)


## 解決策

### 1、フォルダの移動

VSコードがダウンロードフォルダにある場合は、アプリケーションフォルダに移動させましょう。
~~~
mv "/Users/shutohayashi/Downloads/Visual Studio Code.app" /Applications/
~~~


### 2、VScode を最新版にアップデートする
ここでアップデートしましょう。

![スクリーンショット 2023-09-19 16.15.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1625911/6c9d132b-78e2-b998-9793-49a1ce228075.png)

それでもエラーが出る場合があります。その場合は現在使っているVScodeが古いバージョンなのでアンインストールした方が良きです。

## 3、VScodeをアンインストール → 再インストール

アンインストール
~~~
rm -r /Applications/"Visual Studio Code.app"
~~~

アプリ本体だけ削除するので、拡張機能は維持されます。

あとは公式サイトからインストールしましょう

https://code.visualstudio.com/

私はこれで解決できました。3時間くらい調べて結局
##### 「バージョン古いだけかい！！」
となり、膝から崩れ落ちてしまいましたが、これで学習が進められそうです。

参考になれば嬉しいです。
捨てるPCのSSDを新しいPCに移植させた！

業務の一環で新しいPCのSSDが少ない！となって新しくカスタマイズしたPCの購入を検討していましたが、捨てるPCから抜いたSSDが余っていたのでそれを移植させました。
その時の備忘録として、手順を書いていきます。

## SSDの初期化
まずは、捨てるPCから抜いたSSDの初期化をしました。


1. SSDをPCに繋げてローカルディスクを右クリックします。
メニューの「その他のオプションを表示」をクリックします。
![スクリーンショット 2023-09-19 181259.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200261/03864417-6a55-c3bf-e334-fb6f4d219584.png)


2. 「フォーマット」をクリックします。
![スクリーンショット 2023-09-19 181347.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200261/147ee5c1-eec3-2397-414d-ebe02fca63f1.png)


3. フォーマット - ローカルディスク の開始ボタンをクリックします。
![スクリーンショット 2023-09-19 181412.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200261/fa284960-c2dd-bb30-6e35-9ccc7be274a9.png)


4. 「データが消去されます」という警告が出てきますが、構わずOKをクリックします。
![スクリーンショット 2023-09-19 181430.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200261/e1e85acb-09aa-53c0-15b0-265923151f5b.png)


5. 完了したら「フォーマットが完了しました。」というモーダルが出てくるので、OKをクリックします。
![スクリーンショット 2023-09-19 181511.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200261/bb78fbbf-cabf-6e1e-95d0-2f16d2697e6c.png)


6. そしたら、空のローカルディスクが表示されているのでこれでSSDの初期化は完了です。
![スクリーンショット 2023-09-19 181540.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200261/8737e1d3-51ac-aed5-bc85-611a762dd9db.png)


## SSDの移植
次に、SSDの移植をしていきます。
今回はLenovoのPCに移植しました。
開けたらこんな感じ↓でした。
右下にM.2のSSDを挿す場所があったのでここに挿しました。
![IMG_8364.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200261/0a1f309c-cb28-7609-e8dc-b1198b6d32a5.jpeg)

1. 挿したら、windowsの画面を見ます。
スタートボタンを右クリックします。
「ディスクの管理」をクリックします。
![スクリーンショット 2023-09-19 183536.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200261/88ed0fef-69d3-2237-8025-bb60c65ad064.png)


2. この先は以下のURLの参考に手順3からやりました。
https://inab818.site/microsoft-windows11/win11-add-harddisk-ssd-drive/
「未割り当て」を右クリック ->「新しいシンプルボリューム」を選択します。
↓
「次へ」を選択します。
↓
ボリュームサイズを指定（私はデフォルトのまま） ->「次へ」を選択します。
↓
任意のドライブ文字を指定（私はデフォルトのまま） ->「次へ」を選択します。
↓
「このボリュームを次の設定でフォーマットする」を選択します。
↓
「完了」を選択します。


3. 「ディスク管理」の画面を見ると割り当てられていることが確認できます。
![スクリーンショット 2023-09-19 183515.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200261/9b6cfbae-ed41-be9d-49d6-fc5bdaeb497e.png)


4. PCのストレージ画面を確認してもSSDの表示がされていれば完了です。
![スクリーンショット 2023-09-19 183415.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200261/cf294ce3-4272-b417-b42a-a08fe654b2f9.png)
1週間でAI-900に合格した話

## はじめに
夏場になるとどうにも資格試験の学習に力が入らず一週間前からやばい！と思い学習し始めたのですが、
1週間でAI-900に合格できたので学習方法について記事に残しておきたいと思います。

## 受験のきっかけ
Microsoft Learn Cloud Skills Challengeで無料のMicrosoft認定資格試験の受験資格を獲得したため

## これまでに取得した資格試験の経歴
今回の試験に多少影響しそうだなと思っていた資格と学習の経歴です。
 * AWS SAA,SOA取得済み、業務でもAWS 使用
 * Python 3 エンジニア認定データ分析試験合格済み
 * Azure未経験

実際受験した結果としては、AWSは同じクラウドの知識という面で試験内容が被るところもあるかと思っていたのですが、
機械学習の知識やAzureのサービスについて問われることが多かったのであまり関係なかったです。
ただし、Python 3 エンジニア認定データ分析試験受験の際に学習した機械学習の基礎の知識が役立ちました。
おそらく同じようにPython 3 エンジニア認定データ分析試験に合格している方やG検定に合格している方は比較的取得しやすいのではないかと思います。

## 受験結果
850点で合格できました。
黒塗りにするところが多いのでスコアレポートは出せませんが、測定されるスキルの内Azureのサービス以外の「人工知能のワークロードと考慮事項について説明する」が満点だったのがうれしかったです。

![スクリーンショット 2023-09-19 180555.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423172/46eeda37-cfd4-5135-d369-80b181fb6308.jpeg)

以下使用したドキュメントや問題集です。

## 使用した参考書
①Microsoft learnのAI-900の範囲
[Master the basics of Azure: AI Fundamentals](https://learn.microsoft.com/ja-jp/users/sandramarin/collections/zopanqdn7w1p1)
②Microsoft公式AI-900の試験要綱ページ内「無料の実践評価を受ける」で受けることができる模擬試験
[実践評価](https://learn.microsoft.com/en-us/credentials/certifications/exams/ai-900/practice/assessment?assessment-type=practice&assessmentId=26)
③Udemy
[【2023年最新版】AI-900: Microsoft Azure AI Fundamentals 模擬問題集](https://www.udemy.com/course/ai-900-microsoft-azure-ai-fundamentals-s/)

③のUdemyは全部回答してからでないと確認できないのに対し、
②の公式サイトの模擬試験は1問ごとに答え合わせができるので隙間時間に気軽に対策ができて良かったです。
が、公式サイトの模擬試験の注意点として以下がありました。

 * 公式サイトの模擬試験は回答に誤りがあるものがある
 * 問題文や回答の選択肢の内容が英語から訳したためかわかりづらい
なので両方周回し、
解説を読んでもよく分からなかった場合は①の公式ドキュメントを読むという流れを繰り返しました。
私は模擬試験は安定して9割、Udemyは1周程度（7割程度正解）できていた状態で受験しましたが、Udemyももっと周回した方が点が取れていたかもしれないです。


## おまけ
### スコアレポートの確認方法
メールに‎「「スコアレポートの表示」をクリックしてください」というリンクがあったのでそこからスコアレポートが確認できるかと思ったら表示できず、たどり着くまでに半日かかったので確認方法を残しておきます。
①「スコアレポートの表示」をクリックすると個人プロファイルの「アクティビティ」ページが表示される
②メニューに記載されている「認定資格」をクリックする
③以下画像のように表示されるので、過去の試験から「プロバイダーサイトに詳細を表示」をクリックする
![スクリーンショット 2023-09-19 103150.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423172/9dcd2a42-0943-a280-62e3-423c9206ead6.jpeg)
④PeasonVUEホームのページに移動するので、そこから「スコアレポートを表示」をクリックするとスコアレポートが確認できる

## 所感
Azureの試験は初だったのとAI-900は紙の参考書が無かったのでどうやって学習を進めようか不安だったのですが
公式の学習用のドキュメントが豊富だったのでUdemyの問題集を購入するだけで対策できたのが良かったです。
私はいつも資格試験の学習に3か月～6か月以上かけてちょっとずつ定着させていたので、
資格の合格体験記に1週間で合格したとか、1か月で合格したと書いている方とは違うタイプなんだろうなと思っていたのですが初めて短期間で合格できたのが驚きでした。
また、これまでに学習したことがちゃんと身についていたんだなと嬉しく思いました。

長々と記載しましたが、機械学習の基礎の復習もできるので私と似たような学習経歴を持つ方には良い試験だと思います。

最後に、Azureの資格試験受験を躊躇されている方やAI-900をこれから受験したいと考えている方の参考になれば幸いです。

## 参考
https://learn.microsoft.com/ja-jp/certifications/exams/ai-900/

https://learn.microsoft.com/certifications/exams/ai-900/practice/assessment?assessment-type=practice&assessmentId=26