OmniAICreator/Qiita-1.07M huggingface dataset:
cscの作法 その401

# 概要
cscの作法、調べてみた。
練習問題、やってみた。

# 練習問題
動画ファイルの情報を取得する、ffmpegのフロントエンドを書け。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/0da67ab3-10ad-4df4-f0c2-1cb7ea3ebd51.png)


# サンプルコード

```


using System;
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;

class form1: Form {
	form1() {
		Text = "ffmpeg";
		ClientSize = new Size(200, 180);
		Label label = new Label();
		label.Location = new Point(20, 20);
		label.Text = "ファイル情報";
		label.Font = new Font("Geneva", 20, FontStyle.Regular);
		label.BackColor = Color.Azure;
		label.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
		label.AutoSize = true;
		Button btn1 = new Button();
		btn1.Location = new Point(50, 100);
		btn1.Text = "file";
		btn1.Click += btn1_Click;
		Controls.AddRange(new Control[] {
			label,
			btn1
		});
	}
	void btn1_Click(object sender, System.EventArgs e) {
		string fileContent = string.Empty;
		string filePath = string.Empty;
		using (OpenFileDialog openFileDialog = new OpenFileDialog())
		{
			openFileDialog.InitialDirectory = System.IO.Directory.GetCurrentDirectory();
			openFileDialog.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
			openFileDialog.FilterIndex = 2;
			openFileDialog.RestoreDirectory = true;
			if (openFileDialog.ShowDialog() == DialogResult.OK)
			{
				filePath = openFileDialog.FileName;
				//MessageBox.Show(filePath);
				Console.WriteLine(filePath);
				Process p = new Process();
				p.StartInfo.FileName = "ffmpeg.exe";
				p.StartInfo.Arguments = "-hide_banner -i " + filePath;
				p.StartInfo.RedirectStandardInput = true;
				p.StartInfo.RedirectStandardOutput = true;
				p.StartInfo.RedirectStandardError = true;
				p.StartInfo.CreateNoWindow = true;
				p.StartInfo.UseShellExecute = false;
				p.Start();
				string line;
				line = p.StandardOutput.ReadToEnd();
				line = p.StandardError.ReadToEnd();
				Console.WriteLine(line);
				line = line.Replace("At least one output file must be specified", "");
				MessageBox.Show(line);
				p.WaitForExit();
				//Console.WriteLine("ok1");
			}
		}
	}
	[STAThread]
	public static void Main() {
		Application.Run(new form1());
	}
}



```

以上。
Amazon RDS/Auroraのエンドポイント識別子の命名基規則を勘違いしていた話

# 勘違いした話
AWSのRDSやAuroraを作成すると以下のようなエンドポイントが作成されるが`kbcftqi59dmm`の部分がインスタンス別でランダムに払い出されるものだと思っていた。

`test.cluster-kbcftqi59dmm.ap-northeast-1.rds.amazon.com`

# 識別子がどうやって払い出されるのか
以下の要素で識別子が決まる。
そのため、**Backupで取得していたものからリストアする場合も変更はない。**
- <font color="#31B600">**AWSアカウント**</font>
- <font color="#31B600">**リージョン**</font>

# 公式ドキュメント
>各 DB インスタンスには、DB インスタンス識別子があります。このお客様が指定する名前により、Amazon RDS API および AWS CLI コマンドを操作する際、DB インスタンスが一意に識別されます。DB インスタンス識別子は、AWS リージョン内でユーザー別に一意にする必要があります。
識別子は、RDS によってインスタンスに割り当てられた DNS ホスト名の一部として使用されます。例えば、db1 を DB インスタンス識別子として指定した場合、RDS は、db1.123456789012.us-east-1.rds.amazonaws.com などのインスタンスの DNS エンドポイントを自動的に割り当て、ここでの 123456789012 は<font color="#31B600">**アカウントの特定の地域の固定識別子です。**</font>

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/Overview.DBInstance.html
cscの作法 その400

# 概要
cscの作法、調べてみた。
練習問題、やってみた。

# 練習問題
カメラとマイクを列挙する、ffmpegのフロントエンドを書け。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/ed6062e1-5981-bc7e-e376-3b1cd7f78c02.png)

# サンプルコード

```


using System;
using System.Windows.Forms;
using System.Drawing;
using System.Diagnostics;

class form1: Form {
	form1() {
		Text = "ffmpeg";
		ClientSize = new Size(230, 180);
		Label label = new Label();
		label.Location = new Point(20, 20);
		label.Text = "デバイス一覧";
		label.Font = new Font("Geneva", 20, FontStyle.Regular);
		label.BackColor = Color.Azure;
		label.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
		label.AutoSize = true;
		Button btn1 = new Button();
		btn1.Location = new Point(50, 100);
		btn1.Text = "取得";
		btn1.Click += btn1_Click;
		Controls.AddRange(new Control[] {
			label,
			btn1
		});
	}
	void btn1_Click(object sender, System.EventArgs e) {
		Process p = new Process();
		p.StartInfo.FileName = "ffmpeg.exe";
		p.StartInfo.Arguments = "-hide_banner -f dshow -list_devices true -i nul";
		p.StartInfo.RedirectStandardInput = true;
		p.StartInfo.RedirectStandardOutput = true;
		p.StartInfo.RedirectStandardError = true;
		p.StartInfo.CreateNoWindow = true;
		p.StartInfo.UseShellExecute = false;
		p.Start();
		string line;
		line = p.StandardOutput.ReadToEnd();
		line = p.StandardError.ReadToEnd();
		Console.WriteLine(line);
		line = line.Replace("nul: Immediate exit requested", "");
		MessageBox.Show(line);
		p.WaitForExit();
	}
	[STAThread]
	public static void Main() {
		Application.Run(new form1());
	}
}

```

以上。
cscの作法 その399

# 概要
cscの作法、調べてみた。
ffmpeg叩いてみた。

# サンプルコード

```

using System;
using System.Diagnostics;

class Program {
	[STAThread]
	static void Main(string[] args) {
		Console.WriteLine("ok0");
		Process p = new Process();
		p.StartInfo.FileName = "ffmpeg.exe";
		p.StartInfo.Arguments = "-version";
		p.StartInfo.RedirectStandardInput = true;
		p.StartInfo.RedirectStandardOutput = true;
		p.StartInfo.CreateNoWindow = true;
		p.StartInfo.UseShellExecute = false;
		p.Start();
		string line;
		line = p.StandardOutput.ReadToEnd();
		Console.WriteLine(line);
		p.WaitForExit();
		Console.WriteLine("ok1");
	}
}


```

# 実行結果

```
>ff2
ok0
ffmpeg version N-93439-gb073fb9eea Copyright (c) 2000-2019 the FFmpeg developers
built with gcc 8.2.1 (GCC) 20190212
configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libdav1d --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynth --enable-libopenmpt
libavutil      56. 26.100 / 56. 26.100
libavcodec     58. 47.105 / 58. 47.105
libavformat    58. 26.101 / 58. 26.101
libavdevice    58.  7.100 / 58.  7.100
libavfilter     7. 48.100 /  7. 48.100
libswscale      5.  4.100 /  5.  4.100
libswresample   3.  4.100 /  3.  4.100
libpostproc    55.  4.100 / 55.  4.100

ok1
```

以上。
Oracle Cloud Infrastructure Architect Associate 勉強メモ（ネットワーキング（その他）)

# ネットワーキング・ビジュアライザー
OCIのネットワーク構成を図で表示できる機能。
リージョナル・ネットワーク・トポロジ（リージョンの範囲？）とクラウド・ネットワーク・トポロジ（クラウド全体の範囲？）の異なる範囲で見ることが可能。

# リージョン間のレイテンシ
OCIではリージョン間のレイテンシに関する過去５分間のダッシュボードを提供。（リージョンのデータセンター間をどのくらいの遅延で通信できているか）
バックアップ先を決定する参考になる（ネットワーク的にどこが近いか）
量子コンピューターが仮想通貨・ブロックチェーンに与える危険性について解説


この記事は量子コンピューターのブロックチェーン・仮想通貨への潜在的な脅威について説明します。量子コンピューターが実用化すれば、仮想通貨の秘密鍵やハッシュ関数を解読可能になり、セキュリティや信頼性を損なう可能性があります。対策として量子耐性暗号や量子ネットワークが存在しますが、これらも完全には保証できません。それらの採用は仮想通貨のパフォーマンスや利便性に影響を及ぼす可能性もあります。

量子コンピューターが仮想通貨・ブロックチェーンに与える危険性について解説 

https://maricablog.com/?p=1387
スロークエリで本番サーバーをダウンさせ、大混乱を巻き起こした話

<h2>・はじめに</h2>
最近とても暑くなってきましたので涼しくなるために個人的にとても怖かったやらかし話を一つ。<br>
先日、本番サーバーをダウンさせてしまったので自戒のために振り返ろうと思います。

<h2>・発端</h2>
これは先日、自分が担当になったプロジェクトでの話です。<br> 
<br>
ある日、上司から新しいプロジェクトの立ち上げについて連絡がありました。<br>
その際に開発リソースの配分を考慮して、バックエンドの実装は私が担当することになりました。<br>
技術的な面や設計など不慣れなことも多く不安はありましたが、同時に遅延せずに必ずスケジュール通りにリリースしてみせるという気概をもってプロジェクトを進めていきました。<br>
<br>
スケジュールが少々タイトだったことや想定していない不具合が見つかり、修正を必死に終わらせたりと想定外の作業が発生した際には辛かったですね…<br>
周囲の多大な協力もあり、なんとかリリース当日までこぎつけ本番確認も問題なく完了し、これで一安心かと思った矢先に事件が起こりました。<br>

:::note alert
<b>本番サーバーがダウンしました。</b>
:::

<h2>・なぜ障害が発生してしまったのか</h2>
直接的な原因はタイトルにもあるスロークエリです。<br>
しかし、他にも原因はあると考え、1つは技術的に未熟であることもう1つは気持ちに余裕がなかったことだと思います。<br>
<br>
普段なら気を配れていたこともリリースが迫っていたこと、想定外の不具合が見つかったことでそちらの対応に精一杯になっていました。<br>
この焦りから生まれた余裕の無さが今回の事故を引き起こした1番の原因だと考えています。

<h2>・今後、障害を起こさないためには</h2>
いくつか原因があるのでそれぞれに対してあげると自分自身のマネジメントも踏まえて、気持ちの余裕をいかなる状況でも持てるようにすることが1つあるかなと考えています。<br>
前述した通り焦って作業しても良い結果には結びつかないのでここは課題として改善していければと思います。<br>
<br>
技術的なところは日々、向上させるしかありませんが、今回のような事故は検証をしっかり行っていれば未然に防げていたはずですので、自分自身で事故が起こりにくい環境整備、負荷検証などのフローを整備し実施、改善していくことが大事かと考えます。<br>

<h2>おわりに</h2>
代償は非常に大きかったですが今回の件があったことで見直すべき点を認識できたことはせめてもの救いだったかと考えています。<br>
今後はこのような事故を起こさないよう今回の事故の教訓を活かして、エンジニアとして少しでも成長できるよう努めます。<br>
<br>
最後に私の起こした事故の対応に奔走してくださった皆様には本当に感謝しかありません。<br>
改めて本当にありがとうございました。<br>
誤削除ストレージをPhotoRecで復元する：ラズパイ、Ubuntu。PhotorecとTestdiskの違いについて

手順：

1.Photorecをインストール
```
sudo apt update
sudo apt install testdisk -y
```

2.Photorec　実行

```
sudo photorec
```

3.復旧させたいストレージを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352042/6fd80321-0535-75ed-634e-f085310e9fcd.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352042/310e378c-9fd0-0c3a-1d2d-0790ae591b6d.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352042/baa83a49-17e5-8ccd-9486-46554447f309.png)

4.復旧先を選択（同じストレージ上には不可）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352042/4f5337db-b5dc-f18f-1298-10e903e4d996.png)

5.復旧状況の確認

![capture 2023-07-12 19.53.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352042/0e98f4d6-76e1-360c-a931-c07114c42971.png)

---

Photorec 以外にも Testdisk というツールもありますが、違いは、PhotorecとTestdiskは、どちらもCGSecurityによって開発されたデータ復旧ソフトウェアのツールですが、それぞれ異なる目的で使用され、特徴も異なります。

1. Testdisk（テストディスク）：Testdiskは主に失われたり損傷したりしたパーティションの復元や、起動できないディスクを起動可能にするためのものです。パーティションテーブルの修復や削除されたパーティションの回復など、ディスクに関連する問題の修正に使用されます。Testdiskは低レベルで動作し、ディスクの構造やファイルシステムと直接やり取りします。

2. Photorec（フォトレック）：Photorecは、主に写真、ビデオ、ドキュメントなどのマルチメディア形式のファイル復旧に特化しています。ファイルシステムが損傷している場合やパーティションが失われている場合でも、ファイルの復旧が可能です。Photorecはファイルの形式に依存せず、ファイルのヘッダーとフッターを検索し、それに基づいてファイルを特定して復旧します。

PhotorecとTestdiskの主な違いは次の通りです：

- 目的：Testdiskはディスクやパーティションの復旧に主に使用され、Photorecは主にマルチメディア形式のファイル復旧に焦点を当てています。
- ファイルシステムのサポート：Testdiskはさまざまなファイルシステムに対応していますが、Photorecはファイルシステムの構造に依存せず、さまざまなファイルシステムからのファイル復旧を行います。
- ファイルの復旧方法：Testdiskはディスクの構造を修復し、ファイルシステムやディレクトリ構造の回復を行います。Photorecはファイルのヘッダーとフッターを検索し、ファイルのシグネチャに基づいて個別のファイルを復旧しますが、ファイルシステムに依存しません。
- 利便性：Testdiskにはコマンドラインインターフェース（CLI）とステップバイステップのインタラクティブモードがありますが、Photorecはよりシンプルで使いやすいインターフェースを提供しています。
- スキャン速度：Photorecはファイルのシグネチャに基づいてスキャンするため、特定のファイル形式の復旧ではTestdiskよりも速い場合があります。Testdiskはディスクの構造に焦点を当てているため、より時間がかかる場合があります。

両方のツールは、総合的なデータ復旧のために一緒に使用することができます。Testdiskはパーティションの修復やディスクに関連する問題の解決に使用し、Photorecは損傷したり失われたパーティションから特定のファイル形式を復旧するために使用します。
LambdaAuthorizerで403エラーを401エラーにして任意のメッセージを付与したい（CDK）

# やりたかったこと

lambdaAuthorizerで認証エラーが発生した際に、クライアント側でエラー内容によって処理を分けたい

# 構成

サーバレスアプリ、SPAの構成

- 静的資源
    - Nuxt3(historyモード)
    - S3に格納
- API
    - nodejs
    - lambda-APIGateway

# 制約事項

Nuxt3のルーティングモードをhistoryモードにしている関係上、CloudFrontにて403エラーを200に変換している。[参考](https://qiita.com/gakumac/items/b948eb1a13ab6a0375f5)
上記の設定を入れている関係上、LambdaAuthorizerで403エラーを返却してもCloudFrontで200に変換されてしまう
LambdaAuthorizerではデフォルト401の場合、エラーメッセージを編集できない

# 対応したこと
1. APIGatewayのGatewayResponseを利用して、403エラーを401に書き換える
1. LambdaAuthorizerで403エラーが返却されたときに任意のメッセージを設定する

### ソースコード
- APIGatewayの[GatewayResponse](https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-gatewayResponse-definition.html)を利用して、403エラーを401に書き換える
```api-stack.ts
import * as apigw from 'aws-cdk-lib/aws-apigateway'
export class ApiStack extends cdk.Stack {
  constructor(scope: Construct, id: string, appContext: AppContext, props?: cdk.StackProps) {
    super(scope, id, props)
    const api = new ApiGateway({
      cdkApp: this,
      appId: 'testApi'
    })
    // gatewayResponseを追加
    api.apigw.addGatewayResponse('unauthorized', {
      type: apigw.ResponseType.ACCESS_DENIED,
      statusCode: '401',
    })
  }
}
```

- LambdaAuthorizerで403エラーが返却されたときに任意のメッセージを設定する
```lambdaAuthorizer.ts
// 抜粋
const generatePolicy = (principal: string, effect: string, resource: string, errorMessage?: string): GeneratePolicy => {
  return {
    principalId: principal,
    policyDocument: {
      Version: '2012-10-17',
      Statement: [
        {
          Action: 'execute-api:Invoke',
          Effect: effect,
          Resource: resource
        }
      ]
    },
    // contextで任意のエラーメッセージを設定できるようにする
    context: {
      errorMessage: errorMessage ? errorMessage : 'User is not authorized to access this resource with an explicit deny'
    }
  }
}
```

```api-stack.ts
     api.apigw.addGatewayResponse('unauthorized', {
      type: apigw.ResponseType.ACCESS_DENIED,
      statusCode: '401',
      // lambdaAuthorizerからの返却値のcontextをマッピング
      templates: {
        'application/json': '{"message": "$context.authorizer.errorMessage"}'
      }
    })
```
【React】React18の新機能 useInsertionEffectとは？

## はじめに
この記事では、 CSS in JS を開発している人にしか関係ない、React18の新機能 `useInsertionEffect`について解説します。

https://react.dev/reference/react/useInsertionEffect

## `useInsertionEffect` とは？
`useInsertionEffect`は、DOMが変更される前に実行される `useEffect` みたいなものです。

:::note warn
**注意点**
`useInsertionEffect`は、CSS in JSライブラリー開発者のためのHooksです。
*CSS in JSのスタイルを反映させる場所で使うことを想定されているため、他の場所では`useEffect`や`useLayoutEffect`を使いましょう！*
:::

### 使い方
以下の例は、DOMの変更の前にスタイルを挿入するために呼び出ししている例です。

```typescript
// CSS-in-JS library
import { useInsertionEffect } from 'react';

function useCSS(rule) {
  useInsertionEffect(() => {
    // <style>...</style>を挿入する
  });
  return rule;
}
```

### `useLayoutEffet`より先に実行してくれるのがいい。
レンダリングの処理は以下のようになっており、`useInsertionEffect` は、`useLayoutEffet` が実行される前に実行されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/bab2c306-3bb9-3454-43ed-378a28dbe1fb.png)

`useLayoutEffet`は、画面の描画する前に実行するため、`useEffect`のちらつきを解決してくれくれる便利なHooksです。

`useLayoutEffect`の処理と同じタイミングで、CSS in JSのスタイル挿入をしてしまうと、変更前のスタイルで、`useLayoutEffect`の処理が行われ、`useLayoutEffect`で意図した通りに処理が行われません。

そのため、更新されたスタイルで、`useLayoutEffect`を実行させるためにも、`useInsertionEffect`を使ってスタイルを挿入してあげるのが良いのです。

## sample
sampleでは、ボタンを押すと、ボタンの背景色が変わり、その背景色を取得しています。
その後、取得した値をボタンの文字に反映させています。

### 理想
こちらのsampleは理想的な状態です。

https://codesandbox.io/embed/useinsertioneffect-forked-wrx4fd?embed=1&file=%2Fsrc%2FApp.tsx%3A1%2C1

sampleの中の`AdjacentStyle()`では、背景色を変更し、変更したスタイルを、`<style>`に挿入しています。
また、`getBgColor()`では、背景色を取得して、テクスト変更させています。

そして、`isRed`の値が変わったタイミングで`useInsertionEffect`と`useLayoutEffect`を実行しているというサンプルです。

---

### `useLayoutEffect`で処理した場合
CSS in JSのスタイルの挿入と`useLayoutEffect`の処理が同じタイミングになるとこうなります。

https://codesandbox.io/embed/useinsertioneffect-sample1-g3phy9

`getBgColor()`が実行されるときは、まだ、スタイルが挿入されていない状態なので、
ボタンのテキストには、挿入前の`background-color`の値が入ります。

その後、スタイルが挿入されることになるので、ボタンの`background-color`は変わります。
そのため、`#0000ff`のbackground-colorなのに`rgb(255,0,0)`が表示され、
`#ff0000`のbackground-colorなのに`rgb(0,0,255)`が表示されるみたいな状態になります。


## まとめ
この記事では、 CSS in JS を開発している人にしか関係ない、React18の新機能 `useInsertionEffect`について解説しました。

`useInsertionEffect`は、使い方は、ほぼ`useEffet`や`useLayoutEffect`と同じですが、
CSS in JSを開発している人向けの機能のため、`useLayoutEffect`より実行タイミングが早いという特徴があります。

業務ではほぼ使うことはないと思いますが、気が変わってCSS in JS開発することになったらこの記事を見返そうと思います。

---

最後まで読んでくださってありがとうございます！

普段はデザインやフロントエンドを中心にQiitaに記事を投稿しているので、ぜひQiitaのフォローとX(Twitter)のフォローをお願いします。

https://twitter.com/degudegu2510
【楽しいChatGPTプロジェクト】どこでもアクセスしやすい！ChatGPT Next Web - あなたの個人用ChatGPTウェブアプリを簡単にデプロイしましょう


# 1. まず最初に、無料でGithubアカウントを登録する必要があります。
```
https://github.com
```
# 2.無料のコンテナ【vercel】または【railway】を選択します。
# vercel
```
https://vercel.com/
```
# railway
```
https://railway.app/
```
# 3.オープンソースプロジェクト【Chatgpt-next-web】を選択します。
```
https://github.com/Yidadaa/ChatGPT-Next-Web
```

# 4.Open AIキーを取得します【作成クリックします】。
```
https://platform.openai.com/account/api-keys
```
# 5.無料のドメインまたは自分のドメインを使用してバインドします。

# 6.携帯電話でもアクセスできます。
UNABLE_TO_LOCK_ROW フロー


+ [The flow tried to update these records: null. This error occurred: UNABLE_TO_LOCK_ROW: unable to obtain exclusive access to this record or 197 records](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V000076DfFY)


:::note
Supercharge Scheduled Paths with Enhancements for Minutes, Batching, and Limits
https://help.salesforce.com/s/articleView?id=release-notes.rn_automate_flow_builder_scheduled_paths_minutes.htm&release=234&type=5
:::
Flutterパッケージ(simple joystick)を初めて作成&公開してみた。

# どんなものを作成したのか？
- ゲームなどによくあるJoyStickを作成しました。
### こんな感じ
![demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/351982/e51607cd-4617-ab60-c50a-e4cd970155a4.gif)

# なぜ作成したか
- Flutterをやり始めて少し経ったので一つくらいパッケージを公開したかった。
- 類似するものもあったが、シンプルなものが見つからなかった。
- Animationの勉強がしたかった。

## よかったら使ってください！！
## バグ、issueなどありましたらGithubにどんどん追加してください:clap:

https://pub.dev/packages/simple_joystick
https://github.com/NaokiNishizawa/simple_joystick
正整数の切り上げの式　【備忘録】

Atcoderの解説に出てくる式に度々詰まるのでメモ

詳しく教えてくれてるサイト

https://daeudaeu.com/kurisuke_kiriage_shisyagonyu/


#### 自分なりにめも
除数を3とし、被除数1〜5まで考えてみる

1/3= 1/3
2/3= 2/3
3/3= 1
4/3= 1+1/3
5/3= 1+2/3

小数点以下のうち一番小さいのは1/3 つまり1/除数

1-(1/除数)=(除数-1)/除数を足して、繰り上げてから切り捨てることで、整数の切り上げができる。

```:c++
int a; //被除数
int b; //除数
int ans //計算結果

ans = a/b +(b-1)/b 
```

式を整理して
```:c++
int a; //被除数
int b; //除数
int ans //計算結果

ans = (a + b - 1 ) / b; 
```

#### 値の大きさにより型にも気をつける
int : 2^31-1
long long : 2^63-1

#### 関数
* 四捨五入：round()
* 切り上げ：ceil()
* 切り捨て：floor()
ubuntu-drivers devicesでGPUの型番が表示されないとき

# Object
　UbuntuでNVDIAのGPUドライバーを入れる際、ubuntu-drivers devicesでNVIDIA-GPUの型番が表示されないときの対処法

## To do
　接続されているpicデバイスの更新を行うことで解決できる．

### pciデバイスの更新
NVDIAのGPUの接続を確認する．またデバイス認識の更新を行う．
```
$ sudo apt update && sudo apt upgrade -y
$ sudo update-pcidevices 
$ lspci | grep -i nvidia
```

### 推奨GPUドライバーの検索
以下のコマンドで推奨(recommended)ドライバーを調べる．
```
$ sudo apt install -y ubuntu-drivers-common
$ ubuntu-drivers devices
```
## 参考
Ubuntu18.04のインストールできず、黒画面（処方箋：nomodeset）

# 目的
　本記事では、NVDIAのRTX2080のPCにUbuntu18.04をUSBブートでインストールしたところ、ブート画面でフリーズ、ハングし、にっちもさっちもいかなくなった人（CUIとかGUIとかもよくわからなったあの時の自分）向けに対処法である．

## すること
1. Linuxのカーネルオプションでグラフィック機能を無効化して起動し(**nomodeset**)、インストール
2. インストール後の同様にカーネルオプションで起動
3. Ubuntuに元から入っているnouveauを無効化する
4. NVDIAドライバーを入れる (たくさん転がっているので割愛)

## nomodesetとは
Kernel mode setting(KMS)を無効化することを指す．カーネル空間でディスプレイの解像度と深度を設定しているらしい．
KMSについては、お勉強中


### 環境
- iiyamaのなんか
- CPU corei9 9th
- GPU RTX-2080
- Ubuntu18.04のUSBブートイメージ
- 
## Ubuntuのインストールができない
　筆者のPCにUbuntu18.04を入れようとしたところ、GRUBから通常のライブ画面に移行中に画面が崩れファンが異常に回転、システムがハングした．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3008380/ab0d18b8-5733-58fb-85e2-374e1c382f9e.png)


## 原因
　Ubuntu18.04とNVDIAのGPUが相性が悪い、、、
　多くの記事では、インストール後にUbuntuで用いられているグラフィックドライバー(nouveau)を無効化し、NVDIAのGPUドライバーを入れる必要がある．一方で、今回のようにブートローダーから制御が渡されてすぐnouveauが原因で画面がフリーズすることが分かった．

## 対策
 Linuxのブートオプション(**nomodeset**)を使い、グラフィック機能を無効化して起動する．
 nomodesetとは、Kernel mode settingの無効化を意味する．

####  1. インストール時
1. インストーラーを起動後，「**GNU GRUB**」が開かれるのを待つ．
2. 図のようにUbuntuの起動オプションが表示されたら，キーボードの「**E**」を押す．
3. 環境により，多少表示が変わるが「**linux/boot/vmlinuz~**」で始まる行の末尾に「**nomodeset**」を追記し，オプションでカーネルを起動させる．
3. 起動後はインストーラーの流れに沿って作業を進める．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3008380/271b15a9-d082-12df-75c2-81541d55455d.png)

#### 2. インストール後
　インストール後、再起動時にもカーネルオプションでの起動が必要になる．
　GRUB画面が省略される場合、PCの起動後メーカーのロゴが消えてすぐ（BIOSからMBR，GRUBへと制御が移るとき）**Escキー**を連打する．するとGRUB shellに入るので「**normal**」と入力して，Enterキーを一度だけ押し，GRUBメニューを開く．

#### 3. nouveauの無効化
汎用グラフィックドライバーの「**nouveau**」とNVIDIAのGPUの相性が悪い．そこでブラックリストを作り、無効化する．

```$  sudo vim /etc/modprobe.d/blacklist-nouveau.conf```

blacklist-nouveau.confファイルを作り、以下の2行を記述する．
```
blacklist nouveau
options nouveau modeset=0
```

作成したブラックリストを読み込み，再起動して有効化する．これにより，起動時にフリーズする不具合も解決することができる．
```
$  sudo update-initramfs -u
$  reboot
```
iPhone 14 Pro, iPhone 14 Pro Max でステータスバーとナビゲーションバーの間に白い余白が生じる

iPhone 14 Pro, iPhone 14 Pro Maxでは「dynamic island」と呼ばれる余白領域が新たに生じている。

[USE YOUR LOAF - iPhone 14 Screen Sizes](https://useyourloaf.com/blog/iphone-14-screen-sizes/)

単純にステータスバーの高さのビューを追加しているだけだと余白が出てしまう。そのため、UIWindowの`safeAreaInsets.top`により余白の部分を含んだ高さを取得し、その高さのビューをUIWindowに追加し、そのビューの背景色を変えるなどすれば対応できる。

```swift:UIApplication+StatusBar.swift
extension UIApplication {
    var statusBarView: UIView? {
        if #available(iOS 13.0, *) {
            let tag = 3848245

            let keyWindow = UIApplication.shared.connectedScenes
                .map({$0 as? UIWindowScene})
                .compactMap({$0})
                .first?.windows.first

            if let statusBar = keyWindow?.viewWithTag(tag) {
                return statusBar
            } else {
                let topPadding = keyWindow?.safeAreaInsets.top
                let statusBarView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: topPadding ?? 0.0))
                statusBarView.tag = tag
                statusBarView.layer.zPosition = 999999

                keyWindow?.addSubview(statusBarView)
                return statusBarView
            }

        } else {

            if responds(to: Selector(("statusBar"))) {
                return value(forKey: "statusBar") as? UIView
            }
        }
        return nil
    }
    
    func setStatusBarColor(with color: UIColor = UIColor(hex: "#3269D8FF")!) {
        statusBarView?.backgroundColor = color
    }
}

```

参考: 

[Stack Over Flow - How to change the status bar background color and text color on iOS 13?](https://stackoverflow.com/questions/56651245/how-to-change-the-status-bar-background-color-and-text-color-on-ios-13)
[Apple Developer Forum - Space under status bar(dynamic island) in iPhone 14 Pros](https://developer.apple.com/forums/thread/715417)
Node.js Test Runnerでモジュールのモックをしたい


## はじめに

プライベートで書いているスクリプトのテストをしたいが、メンテナンスするパッケージを増やしたくないと思い、
Node.js v18 から標準で使えるようになった[Node.js Test Runner](https://nodejs.org/api/test.html) を使ってテストできないかを調べていました。

調べてみたところ、モジュールのモックをする方法で少し詰まったので、その解決方法を書きます。
なお、今回試すのは CJS です。

## やりたいテスト

今回やりたかったテストは、テスト対象のモジュール内で使用されている別のモジュールをモックするようなテストです。

例として、以下のスクリプトを使います。

```js:add.js
exports.add = (a, b) => {
  return a + b;
};
```

```js:mul.js
const { add } = require("./add");

exports.mul = (a, b) => {
  let result = 0;
  for (let i = 0; i < b; i++) {
    result = add(result, a);
  }
  return result;
};
```

そして、今回テストしたいのは`mul`関数で、`add`関数をモックしたいとします。

この場合であれば、[`mock.method`](https://nodejs.org/api/test.html#mockmethodobject-methodname-implementation-options) 関数を使い、以下のようにモックすることができます。

```js:mul.test.js
const { test, mock } = require("node:test");
const assert = require("node:assert");

// `mul`よりも先に`add`をモックする必要がある
const addModule = require("./add");
mock.method(addModule, "add", (a, b) => {
  return 1;
});

const { mul } = require("./mul");

test("mul", () => {
  assert.equal(mul(2, 3), 1);
});

```

同様にビルトインモジュールなどもモックできます。

```js:readFileSync.test.js
const { test, mock } = require("node:test");
const assert = require("node:assert");
const fs = require("node:fs");

mock.method(fs, "readFileSync", (path) => {
  return "hoge";
});

test("readFileSync", () => {
  assert.equal(fs.readFileSync("sample.txt"), "hoge");
});
```

なお、モックしたいモジュールが`module.exports`で定義されている場合、`mock.method`ではモックできないようです。（いろいろ試したけど、できる方法を見つけれなかった。`Module._load`でモック関数を返すしか思いつかなかった。）

## Refs

- [Node 18 に Test Runner なるものがあったので試してみた（Experimental） - Qiita](https://qiita.com/ringtail003/items/5fda350bb273bab7e1c9)
- [Spies and mocking with Node test runner (node:test) | Željko Šević | Node.js Developer](https://sevic.dev/notes/spies-mocking-node-test-runner/)
- [Read Node.js Source Code to Deeply Understand the CJS Module System - Alibaba Cloud Community](https://www.alibabacloud.com/blog/read-node-js-source-code-to-deeply-understand-the-cjs-module-system_599765)
Overleafではコンパイルモードがファストだと画像が表示されなくなる

「何を当たり前のことを」と思われるかもしれませんが、これでハマったので書きます。

画像部分に枠線とファイル名だけが表示されるのは仕様であってエラーやバグではありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092391/affd7b02-8d41-5d79-2d32-4cab2c696e55.png)

https://www.overleaf.com/learn/how-to/Fixing_and_avoiding_compile_timeouts

>Use draft mode when working online. To do that, select the downward-pointing arrow next to the Recompile button, and then choose the Fast `[draft]` option. This replaces all of your graphics with boxes and makes the PDF compile much faster:


対処法としては、リコンパイルボタン横の矢印を選択し、コンパイルモードをファスト`[draft]`からノーマルにすると画像が表示されるようになります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092391/53af2569-9850-0bff-0f59-1f5a9676ef9f.png)



画像が表示されない場合の対処法は他にもいくつかあるようなので、適当に紹介しておきます。

https://paper.hatenadiary.jp/entry/2016/10/17/020513

https://lisa0.hatenablog.jp/entry/2021/10/11/033743

https://takahero2016.hatenablog.com/entry/2020/12/14/180000
VSCode：特定のファイルだけ保存時の自動フォーマットが効かない時の対処方法


## 環境

- Mac
- VSCode
- ESLint
- Prettier

## 事象

tsxファイルは保存時に自動フォーマットされるが、tsファイルは保存時に自動フォーマットされなかった。

## 原因調査、対策

settings.jsonを確認すると、

`"editor.formatOnSave": true,`
があったので、保存時に自動フォーマットされるはずだが。。。

手動でフォーマットしてみた。
(macOS) Shift + Option + F

複数のフォーマッターがあるので、選択してくれとのこと。
![スクリーンショット 2023-07-13 0.24.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128077/e40ff96d-5b02-bef4-1bc7-0aee6cec3f7a.png)

Prettierを選択する
![スクリーンショット 2023-07-13 0.24.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128077/425afa5b-a331-1ac7-adb5-b570bf0f0b49.png)

これで、保存時に自動フォーマットできるようになった。解決。

settings.jsonにtypescriptが追加されてた。
直接書いてもいいかも。

```
  "[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "editor.formatOnSave": true,
+ "[typescript]": {
+   "editor.defaultFormatter": "esbenp.prettier-vscode"
+ }
```
UEFNにFBXを読み込むとローポリになる

# BlenderからFBXでエクスポート
BlenderからUnityやMayaやUnreal Engineなど3D環境・ゲーム開発環境に読み込むためにはFBXファイルにして読み込みます。

①　Blenderの【ファイル】→【エクスポート】→【FBX(.fbx)】でウィンドウ開きます。
②　パスモードを【コピー】にし、右隣の【テクスチャを埋め込む】を有効化します。
③　オブジェクトタイプの【メッシュ】のみを選択します。
④　【FBXをエクスポート】をクリックします。

![スクリーンショット 2023-07-12 234055.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3511430/080544ae-a3f1-c6b2-ba2c-b8261c857dff.png)

# UEFNにFBXをインポート
UEFNのコンテンツブラウザにドラックアンドドロップし、【全てインポート】をクリックします。
すると、UEFNのコンテンツとして扱えるようになります。

![スクリーンショット 2023-07-12 235055.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3511430/0c0c51d4-52a8-cedd-aa8c-9d933b138309.png)


# UEFN上ではローポリになってしまう
通常のUnreal EngineではBlenderと同じマテリアルにもかかわらず、UEFN上ではローポリになってしまいました。

Unreal Engine 5.2の場合
![スクリーンショット 2023-07-12 235521.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3511430/761e173f-8865-5e76-af54-ac93773c76ad.png)


UEFNの場合
![スクリーンショット 2023-07-12 234741.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3511430/74f1a8a6-7255-15ac-5f54-2505cfd962f3.png)


# 原因
UEFNの環境ではデフォルトのLODがすべて小物を対象としているようです。
そのため、大きな建造物を配置する際にはLOD設定をし直さないといけません。
https://dev.epicgames.com/documentation/en-us/uefn/setting-the-level-of-detail-in-unreal-editor-for-fortnite

![スクリーンショット 2023-07-13 203840.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3511430/16f0baaf-0760-6213-9395-0169c4a0ad8d.png)
【Java】学習記録①

## 初めての投稿です！
4月からプログラミングの学習を本格的に始めました。

先日無事に転職活動を終え、入社先でJavaを本格的に学ぶことになりました。
その前にまず予習として学習していきたいと思います。

ProgateのJavaの無料範囲は学習した状態から始めていきます。

# 「はじめてのJavaプログラム：Javaプログラムの入門書」

こちらを拝読しました。（丁度Kindle Unlimited3ヶ月無料...！）

ほとんどの内容はProgateで学んだことだったので、サラサラと読み進めました。

この本から新しく学んだことをまとめたいと思います。

## 文字列操作
文字列操作とは、文字列が持っている『機能』を利用して文字列に対して処理を行うこと
<br>

**機能の一例**
- length()：　文字列の長さを取得
- substring(a,b)： 文字列をaからbまで切り取り
- replace(a,b)： 文字列のaをbに置き換え
- indexOf(a)： 文字列にaという文字が何番目にあるか取得
- split(a)： 文字列をaという文字で分割
- endsWith(a)： 文字列がaという文字列で終了しているかどうか検証
- contains(a)： 文字列がaという文字列を含んでいるかどうか検証
<br>

substring(a,b)については、
- a番目は切り取られる文字に含まれる
- b番目は切り取られる文字に含まれない
- b番目の手前の文字までが切り取られる文字に含まれる

となるので注意が必要

## Fileクラス
Fileクラスとは、フォルダやファイルを操作するためにJava言語が用意しているクラス

```Java:main
// Fileクラスの宣言
File file = new File("files");

// フォルダ内にあるファイル名を全て取得
String[] files = file.list();

//配列filesの要素を取出して表示
for(String fileName:files){
    System.out.println(fileName);
}

```
>Fileクラスをインスタンス化して、変数fileに代入する
インスタンス化の際に「files」というフォルダ名（ファイル名）を渡す

ということです。
以降は既知なので省略します。
numpyで三角関数早見表

自分用メモです

## 正三角形
### 正三角形の1辺の長さから高さを計算
```math
h=\frac{\sqrt{3}}{2}a
```
```python
h = numpy.sqrt(3) / 2 * a
```

### 正三角形の1辺の長さから面積を計算
```math
S=\frac{\sqrt{3}}{4}a^2
```
```python
S = numpy.sqrt(3) / 4 * a ** 2
```

### 正三角形の高さから1辺の長さを計算
```math
a=\frac{2}{\sqrt{3}}h
```
```python
a = 2 / numpy.sqrt(3) * h
```
### 正三角形の高さから面積を計算
```math
S=\frac{1}{\sqrt{3}}h^2
```
```python
a = 1 / numpy.sqrt(3) * h ** 2
```
### 正三角形の面積から1辺の長さを計算
```math
a=\sqrt{\frac{4}{\sqrt{3}}S}
```
```python
a = numpy.sqrt(4 / numpy.sqrt(3) * S)
```
### 正三角形の面積から高さを計算
```math
h=\sqrt{\sqrt{3}S}
```
```python
h = numpy.sqrt(numpy.sqrt(3) * S)
```
## 直角三角形
### 直角三角形の底辺・高さから斜辺を計算
```math
c=\sqrt{a^2+b^2}
```
```python
c = numpy.sqrt(a**2 + b**2)
```
### 直角三角形の底辺・高さから角度を計算
```math
\theta=\tan^{-1}\frac{b}{a}
```
```python
c = numpy.arctan(b/a) # ラジアン
c = numpy.degrees(numpy.arctan(b/a)) # 角度
```
### 直角三角形の底辺・斜辺から高さを計算
```math
b=\sqrt{c^2-a^2}
```
```python
b = numpy.sqrt(c**2 - a**2)
```
### 直角三角形の底辺・斜辺から角度を計算
```math
\theta=\cos^{-1}\frac{a}{c}
```
```python
c = numpy.arccos(a/c) # ラジアン
c = numpy.degrees(numpy.arccos(a/c)) # 角度
```
### 直角三角形の底辺・斜辺から面積を計算
```math
S=\frac{1}{2}a\sqrt{c^2-a^2}
```
```python
S = 1 / 2 * a * numpy.sqrt(c**2 - a**2)
```
### 直角三角形の底辺・角度から高さを計算
```math
b=a\tan\theta
```
```python
b = a * numpy.tan(rad) # ラジアン
b = a * numpy.tan(numpy.deg2rad(deg)) # 角度
```
### 直角三角形の底辺・角度から斜辺を計算
```math
c=\frac{a}{\cos\theta}
```
```python
c = a / numpy.cos(theta) # ラジアン
c = a / numpy.cos(numpy.deg2rad(theta)) # 角度
```
### 直角三角形の底辺・角度から面積を計算
```math
S=\frac{1}{2}a^2\tan\theta
```
```python
S = 1 / 2 * a**2 * numpy.tan(theta) # ラジアン
S = 1 / 2 * a**2 * numpy.tan(numpy.deg2rad(theta)) # 角度
```
### 直角三角形の高さ・斜辺から底辺を計算
```math
a=\sqrt{c^2-b^2}
```
```python
a = numpy.sqrt(c**2 - b**2)
```
### 直角三角形の高さ・斜辺から角度を計算
```math
\theta=\sin^{-1}\frac{b}{c}
```
```python
theta = numpy.arcsin(b / c) # ラジアン
theta = numpy.degrees(numpy.arcsin(b / c)) # 角度
```
### 直角三角形の高さ・斜辺から面積を計算
```math
S=\frac{1}{2}b\sqrt{c^2-b^2}
```
```python
S = 1/2 * b * numpy.sqrt(c**2 - b**2)
```
### 直角三角形の高さ・角度から底辺を計算
```math
a=\frac{b}{\tan\theta}
```
```python
a = b / numpy.tan(theta) # ラジアン
a = b / numpy.tan(numpy.deg2rad(theta)) # 角度
```
### 直角三角形の高さ・角度から斜辺を計算
```math
c=\frac{b}{\sin\theta}
```
```python
c = b / numpy.sin(theta) # ラジアン
c = b / numpy.sin(numpy.deg2rad(theta)) # 角度
```
### 直角三角形の高さ・角度から面積を計算
```math
S=\frac{b^2}{2\tan\theta}
```
```python
S = b**2 / 2 * numpy.tan(theta) # ラジアン
S = b**2 / 2 * numpy.tan(numpy.deg2rad(theta)) # 角度
```
### 直角三角形の斜辺・角度から底辺を計算
```math
a=c\cos\theta
```
```python
a = c * numpy.cos(theta) # ラジアン
a = c * numpy.cos(numpy.deg2rad(theta)) # 角度
```
### 直角三角形の斜辺・角度から高さを計算
```math
b=c\sin\theta
```
```python
b = c * numpy.sin(theta) # ラジアン
b = c * numpy.sin(numpy.deg2rad(theta)) # 角度
```
### 直角三角形の斜辺・角度から面積を計算
```math
S=\frac{1}{2}c^2\sin\theta\cos\theta
```
```python
S = 1/2 * c^2 * numpy.sin(theta) * numpy.cos(theta) # ラジアン
S = 1/2 * c^2 * numpy.sin(numpy.deg2rad(theta)) * numpy.cos(numpy.deg2rad(theta)) # 角度
```
KinesisトリガーのLambdaに入ってくるデータが突然空になった

# 起こったこと

突然Lambdaに入ってくるイベントが下の状態なってしまい、デコード時エラーで動かなくなってた。

```javascript
{
  Records: [
    {
      kinesis: {
        data: "", // <-- 本来はここにbase64エンコードされたテキストが入ってくる。
        ...
      },
      ...
    }
  ]
}
```

原因はIP固定にするためVPCに入れたこと。
分かったのはそれだけ。たぶんAWSのバグと思う。

# 対応１

とにかくVPCが悪さしてるのでVPCとトリガーを付け直す。

1. 一旦LambdaからVPC設定とKinesisトリガーを外してデプロイ。
1. VPC設定だけ付け直してデプロイ。
1. Kinesisトリガーを付け直してデプロイ。

1回で治らない場合があるのでこれを数回繰り返す…

### 結果 → 失敗

治っても次のデプロイでまたデータが入らなくなる。

# 対応２

KinesisトリガーとVPC Lambdaを分けてしまおうかとも考えたが、以前から何度も作って普通に稼働している構成なのでLambdaを一回消して再デプロイしてみた。

1. Lambdaを消してデプロイ。
1. Lambdaを追加して再デプロイ。この時VPCは付けずにVPCの権限だけ付けておく。
1. VPCに入れて（VpcConfigを設定して）再デプロイ。

### 結果 → 失敗

変化なし。

# 対応３

もうLambdaを２つに分けてしまう。

1. LambdaからVPC設定、権限を削除。
1. VPC用のLambdaを追加。
1. GlobalのLambadでKinesisを開封してVPC Lambdaへイベント送信。

### 結果

確認中

# あとがき

糞が！だからAWS嫌いなんだよ！！
androidアプリをつくろう001

はじめまして、「おかす」です。
普段はバックエンドのエンジニアとして働いています。
神が突然「アンドロイドアプリを作れるようになりなさい」と言ってきたので
完全に0の状態からある程度さわれるようになるまで頑張ろうと思います。
このqiitaでは学んだことや経過を記録していきます。

目標はアプリを20個作成したのち、ラインのように
メッセージやスタンプを送受信できるようなアプリを作成することです。
アイデアをひねる頭がないため、このアプリ20個は以下を参考にします。
<b>【実践で身につけるiOSアプリ開発】 10日で21個アプリを作った話
https://www.yukisako.xyz/entry/swift-kusoapps-summery</b>

期限は自分に激甘のため、100年以内とします。

ではいきなりですが、環境構築からはじめましょう。

環境はWindows10です。
まずはソースコードを書いたりなんやらできる総合開発環境をインストールします。
なにやら「Android Studio」がよさそうなのでこれを使いましょう。
言語は特にこだわりがないため「java」で開発することにします。

次に実際の開発を想定してバージョン管理もしていきます。
今回は「GitHub」を使用することにしました。
リポジトリを作成→SSH keyの設定→クローン
ということをやりたかったのですが、SSH keyの設定で少しつまづきました。
SSH keyを生成できるソフト「PuTTy」でキーを作成したのですが、
デフォルトで選択されている「SSH-1 key(RSA)」が現在使用できないようで、
なんでクローンできないんだーってなりました。
以下画像の「SSH-2 ECDSA key」にすることで解決しました。
※ParametersもECDSAに変えることを忘れずに。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557044/ea3452ca-28ba-00d6-8d10-8a5a4e338f82.png)

なんやかんやでクローンまでできました。

それではある程度準備ができたので「Android Studio」を起動。
UIが「PhpStorm」にそっくりでとっつきやすそうです（仕事でPhpStormを使用）

作業用フォルダを用意し、プロジェクトを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557044/96c3840f-d15b-eb74-e7b4-58c726afbaa3.png)

1つ目のアプリは
体重を入力すると、アインシュタインの特殊相対性理論からその人の持っているエネルギーを求めて，
原発何基分が一年に作るエネルギー量かを計算するアプリです

実装を分解してみると以下になりますね（だいたい）
・テキストの設置
・テキストの背景、枠組みの色の変更
・テキストボックスの設置
・テキストボックスのヒントを設定
・ボタンの設置
・ボタン押下で処理を実施させる（四則演算＋テキスト表示）

実装メモ
・デザインを見ながら直感的に開発をする場合、activity_main.xmlのタブを開き、
　「コード/Split/デザイン」のうちデザインを選択
・パレットからテキストやボタンをプレビューへドラッグすることでオブジェクトを設置することができる
・値や文字サイズなどのプロパティを編集するときは画面右側の対応する項目か、
　　右上の「コード/Split/デザイン」のうちのコードから編集できる
・文字列などは直接書き込まず、values/strings.xmlに
　「<string name="button_text">change</string>」のように定義する。
　使用するときは「@string/設定したname(今回はbutton_text)」のような形で指定する。
　ダブルクォート不要。
・パレットからボタンを設置した際、レイアウトの制約がないと怒られた。
　→設置オブジェクトを右クリして赤丸をセットすることで回避できた。（あってるの？これ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557044/1f7071b1-aa6f-8b37-d1db-b7d74212e144.png)
・テキストカラーの変更：textColor：@color/cian_blue
・テキスト範囲のバックグラウンドの色変更：solid android:color="#ff0000"
・テキスト範囲のバックグラウンド枠組の色変更：stroke android:color="#00ff00" android:width="2dp"



　時は満ち完成！次いってみよ～
useReducerを使った状態管理

前回に引き続き、Udemyの教材を見ながら勉強していて自分なりの復習用に記事を残しています。

## useReducerとは

> 多くのイベントハンドラにまたがって state の更新コードが含まれるコンポーネントは、理解が大変になりがちです。このような場合、コンポーネントの外部に、リデューサ (reducer) と呼ばれる単一の関数を作成し、すべての state 更新ロジックを集約することができます。

出典: [state ロジックをリデューサに抽出する](https://ja.react.dev/learn/extracting-state-logic-into-a-reducer)
定義されたstateとロジックを一つのhooksにまとめて一元管理できるという事でしょうか。
具体的にはどういう事かuseStateと比べながら見ていきます。

## useStateのおさらい

まずuseStateは、ユーザ操作の結果として画面上の表示内容を変更する様な場合の時に、変更した値をどこかに保持する事を可能にするhooksになります。
例えば、ネットで買い物する時に購入ボタンを押したり、購入する商品の点数を変更したりした時の現在の入力値を保持します。
以下、簡単な例をコードに書きました。

```index.js
import {useState} from "react"

const initialState = {
  count: 0
}

export const Example = () => {
  const [state, setState] = useState(initialState)
  const incrementClick = () => {
    setState({count: state.count + 1})
  }

  const decrementClick = () => {
    setState({count: state.count - 1})
  }

  return (
    <>
      <p>数値は: {state.count}</p>
      <button onClick={incrementClick}>プラス</button>
      <button onClick={decrementClick}>マイナス</button>
    </>
  )
}
```
Reactを学んだことがあるなら、誰でも知っている様なコードですね。
全く同じ内容をuseReducerで書いてみたいと思います。

```index.js
import {useReducer} from "react"

const initialState = {
  count: 0
}

const reducer = (state, action) => {
  switch (action.type) {
    case "incremented":
      return {...state, count: state.count + 1}
    case "decremented":
      return {...state, count: state.count - 1}
    default:
      throw new Error("Error")
  }
}

export const Example = () => {
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    <>
      <p>数値は: {state.count}</p>
      <button
        onClick={() => {
          dispatch({type: "incremented"})
        }}
      >
        プラス
      </button>
      <br />
      <button
        onClick={() => {
          dispatch({type: "decremented"})
        }}
      >
        マイナス
      </button>
    </>
  )
}   
```
初期値のオブジェクトまではuseStateと一緒ですが、useReducerではreducer関数を作成し`action.type`によって数値をプラスするかマイナスにするかを分けています。
プラスボタンを押下したらdispatchでreducer関数を発火させます。
プラスボタンのtypeが `"incremented"`なので `return {...state, count: state.count + 1}`が実行され数値がプラス1されます。
マイナスボタンも同様でdispatchでreducer関数を発火させて、マイナスボタンのtypeが `"decremented"`なので `{...state, count: state.count - 1}` が実行され数値がマイナス1になります。
これだとあまりuseReducerのメリットが分からないかと思うので、以下のyoutubeの動画の様に３つのuseStateを1つにまとめたりすることも可能の様です。
<br>


<iframe width="560" height="315" src="https://www.youtube.com/embed/RZPAQV7JvNU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

（こんなことが出来るのか…と閲覧しながら思いました）
まだ個人的に知識が定着しているとは言い難いので、実務でも積極的に使ったり小さなアプリとか作って一つ一つ覚えていきたいと思います。
Reactに限らずですが、プログラミングは奥が深い…:disappointed_relieved:

## 参考になった記事
https://qiita.com/uhyo/items/cea1bd157453a85feebf

https://reffect.co.jp/react/react-hook-reducer-understanding/
PowerAppsからPowerAutomeにデータを送る

20230712

■PowerAppsとPowerAutomateのデータ形式
PowerAppsからPowerAutomateにデータを送る際にJSON形式でデータを送信します。
PowerAppsで時間（日付型）を日本時間に設定しても、JSON関数でUTCに変換されます。
なのでPowerAutomateで値を出力するときにはJSON関数でUTCに変換された値が出力されます。

この点を踏まえて、以下の二つの方法が有効と考えます。
①PowerAppsでJSON変換する前に日付型をテキスト型に変更する。
②PowerAutomate上でUTCを日本時間に変換する。


※JSONとは？
JavaScript Object Notationの略で、コンピュータ高で無く人間が読んでも理解できるように作成されたデータフォーマット。
{}や[]などの括弧を使って記述する。
他言語同士のデータのやりとりで利用する共通フォーマットのイメージ。

時代的にはXMLー＞JSONがデータフォーマットとして利用されていた。
XML　マークアップ言語で直感的にわかりづらかった。Csvよりは複雑なことができた。


JSONは次のデータ型に対応している
　文字列
　数値
　null
　bool値
　オブジェクト
　配列

●オブジェクトとは？
　オブジェクトの中にオブジェクトを入れることが可能。こちらをネストするという。
  例
　　｛"a":1,
      "b":2,
      "c":{
           "d":4,
          "e" :5
         ｝
     ｝

■今回の気づき
PowerAppsからPowerAutomateにデータが与えられたときに、PowerAutomateの環境のせいでデータが変換されていたと思っていたことで本件の調査が難航しました。
データの受け渡しがPowerAppsとPowerAutomateで行っていると思っていましたが、もっと細部まで確認する必要がありました。
具体的には以下を確認する必要がありました。
①PowerAppsからデータが出る直前の状態
②PowerAppsからデータが出た直後状態
③PowerAutomateにデータが届く直前の状態
④PowerAutomateにデータが届いた直後の状態

ネットワークでもファイアウォールのLANケーブルの外向き、内向きみたいな話に似ているな。
あとは境界値分析の考え方かな。一回勉強したなあ。
状況をもっと細かく見れるか確認する必要があると感じました。
要件定義で聞いたことがすべてではないと思って、言葉や状況に疑いを持とう！
RailsのNokogiriでamazonをスクレイピングする際に503エラーが出たので回避した方法

## やったこと
- ユーザーエージェントを10種類用意
- リファラを3種類用意
- wait_timeを5種類用意

それらをランダムに組み合わせてアクセス。

ここまでやれば、問題なくスクレイピングできた。

```ruby
  # ユーザーエージェントを偽装するオプションを設定。amazonの503エラーの回避
  user_agents = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 OPR/77.0.4054.254',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.55',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 OPR/77.0.4054.254',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:90.0) Gecko/20100101 Firefox/90.0',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 Edg/92.0.902.55',
    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36',
    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 OPR/77.0.4054.254'
  ]

  referrers = [
    'https://narou-osusume.com/osusumes/2',
    'https://narou-osusume.com/osusumes/10',
    'https://narou-osusume.com/osusumes/15'
  ]

  # ランダムにユーザーエージェントとリファラを選択
  user_agent = user_agents.sample
  referrer = referrers.sample

  # ユーザーエージェントを偽装するためのヘッダーを設定
  headers = {
    'User-Agent' => user_agent,
    'Referer' => referrer
  }

  # 人間っぽく偽装するために、wait_timeを挟む
  wait_times = [0.3, 0.5, 0.7, 1, 1.2]
  wait_time = wait_times.sample
  sleep(wait_time)

  html = URI.open(url, headers).read
  doc = Nokogiri::HTML(html)
```
主キーについて　【スッキリわかるSQL入門】学習備忘録

SQL初学者の学習備忘録です。


## 主キーとその必要性
#### 重複した行の問題点
SQL上で完全に重複した行が存在すると、そのうちのある行だけを区別することは出来ない（WHERE句での指定ができない）
→テーブルの中に重複した行が格納されることは避けるべき

#### 主キーとなる列が持つべき特性
- 必ず何らかのデータが格納される（NULLでない）
- ほかの行と値が重複しない
- 一度決めた値は変化しない

###### ex)社員テーブルにおける社員番号
同姓同名、年齢、性別など全て同じデータがあっても、
- 社員番号を持たない社員は存在しない
- 同じ社員番号が異なる社員に割り当てられることはない
という条件を満たすとき、社員テーブルで重複する行は存在しない

WHERE句で特定できるためにも、全てのテーブルは主キーとなる列を必ず持つべきである。

#### 主キーの区別
- 自然キー
　上記の社員番号のように自然に存在し、主キーになりえる列
- 人口キー・代替キー
　主キーの役割を果たせる列が存在しない場合に作成された主キーの役割持つ列
- 複合主キー
　複数の列を組み合わせることで、実質的に主キーとして扱う
　ex)氏名、住所、生年月日を組み合わせて主キーに


#### テーブルに列を追加する方法
主キー制約を付ける（PRIMARY KEY制約）を付ける

###### 主キー制約の指定（単独列）
```SQL
CREATE TABLE テーブル名(
 ID INTEGER PRIMARY KEY,
 name VARCHER(40)
)
```
###### 主キー制約の指定（複合主キー）
```SQL
CREATE TABLE テーブル名(
 ID INTEGER ,
 name VARCHER(40)
 PRIMARY KEY (ID,name)
)
```
単独列でも複合列のように記述可
Databricks Certified Machine Learning Associate体験記

# はじめに
Databricks Certified Machine Learning Associateを受験したので、勉強方法と感想を書いてみました。
試験概要については下記リンクを参照しただければと思います。

https://www.databricks.com/learn/certification/machine-learning-associate

# 前提
受験前の自分のスキルセットは以下のような感じ。
・社会人2年目で業務はデータ分析基盤の導入支援、Azureを扱っている。
・大学では統計を学習、機械学習についても基本的は知識は勉強済み。
・PySparkは経験なし。Pythonは少し経験あり。（scikit-learn, pandasとか）
・英語は大学受験以降ほとんど勉強していない。

# 学習方法
メインの学習方法は2つ
### ①学習用ノートブック

https://github.com/databricks-academy/please-see-databricks-academy

上記GitHubにある「apache-spark-programming-with-Databricks」（sparkの概要）と「scalable-machine-learning-with-apache-spark-japanese」（機械学習）をやっていきました。
（現在はGitHub からは入手できなくなり、Databricks Academy にサインインする必要があるようです。）
環境は[Databricks Community Edition](https://community.cloud.databricks.com/login.html)が無料で使えて良いです。（一部機能に制限あり：[Databricks Community Edition FAQ](https://www.databricks.com/product/faq/community-edition)）
Community Editionの使い方は下記の記事が参考になると思います。

https://qiita.com/taka_yayoi/items/5918a0e88d9b2472578c

### ②模擬試験問題
公式の模擬試験はなかったので、Udemyを活用しました。

https://www.udemy.com/course/databricks-certified-machine-learning-associate-2023/

### 学習期間・進め方
ちゃんと勉強した期間は3か月程度。1~2時間を週3日ぐらいやってました。
進め方は下記の感じでやっていきました。
1. 最初は理解できなくても、一旦学習方法①をすべて実行してみる。
2. 学習方法②の模擬試験を解いて、間違えた問題の解説を読む。その都度、学習方法①に戻って復習。復習する際は、問題と解答をDeepLで翻訳して英語を勉強。
3. 間違えた問題だけもう1回解く。
4. 2,3を模擬試験問題分繰り返す。
5. 試験前にはもう一度全部解く。

機械学習についての基礎的な知識が無い方は、最初にそちらを勉強しておかないと厳しい気がします。

# 感想
・**模擬試験は絶対やったほうがいい**です。
・オンライン受験だったので、専用のブラウザをインストールが必要でした。そのため、**私用PCを使うのが良い**と思います。（社用PCだとインストール不可な可能性があるため。）また、試験中はカメラオンにして、試験官に監視されます。
・本当に**全部英語**でした。問題・選択肢はもちろん、試験官とのチャットも英語です。（ヘルプを求めなければチャットは発生しないです。）
・**そこまで難しい英語ではない印象**です。問題は一文で選択肢は単語だけだったりするので、何となく理解できると思います。
・問題の内容については下記の記事に書かれている感想と同じ印象でした。UIに関する問題（どこを見ればモデルのバージョンがわかるかみたいな問題）も出るので、ノートブックを実行後に結果がどこにどのように表示されているかを確認しておくのは大切かなと思います。

https://qiita.com/nttd-yuan/items/2dba37360161dfbcbe4a

# おわりに
上記の勉強方法でギリギリ合格することができました！（合格ライン70％で71％でした。。。）
受験予定の方々への参考になれば幸いです。
ショップサイト　ER図 ２　

### :zap: はじめに
改正版です。

管理者フラグを使うとセキュリティがあまくなるので今回は、
顧客と管理者を分けて作成しました。
_________
**ER図**
https://www.draw.io/ で作成


![スクリーンショット 2023-07-14 22.28.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3492906/2446a9b1-4294-5d28-d83a-b838108a0475.png)

### :shamrock:管理者テーブルについて
管理者テーブルは、通常、管理者ユーザーの認証情報（ログインID、パスワード等）と基本的なプロフィール情報（名前、連絡先情報等）を保持するために使用されます。管理者が行う主なタスクは、ウェブサイトの管理と運用であり、特定のユーザーやデータと直接的な関連性（つまり、アソシエーション）を必要とすることはほとんどありません。

**管理者テーブルのアソシエーション(繋がり）**: 
管理者テーブルは他のテーブルと具体的なアソシエーションを持つ必要が通常はありません。これは、管理者がデータベース内の全ての情報にアクセスし、それを管理する能力を持つためです。
_____
### :shamrock:ステータス内容
**会員ステータス**
・　有効　
　・退会
**注文ステータス**
・入金待ち
・入金確認
・出荷済み
・配送中
・配達済み
**販売ステータス**
・販売中
・売り切れ
**制作ステータス**
・着手不可
・製作待ち
・製作中
・製作完了

:star:enumを使って管理するため、statusのデータ型はintegerになる。
### 参考

https://zenn.dev/goldsaya/articles/272826e55c9809


https://zenn.dev/ganmo3/articles/f048886ba2009e
[javascript]Axios並列処理方法

<h2>Axiosとは?</h2>
Axiosはブラウザ、Node.jsのための<b>Promise API</b>を活用する<b>HTTP非同期通信ライブラリ</b>です。 実務ではFetchApiよりAxiosをよく使うそうです。

<h2>Axiosの並列処理</h2>

```javascript:login.js
import axios  from 'axios';

    // 省略
#onSubmit = e => {
    axios.post('/api/authentication', loginData)
      .then(result => {
        return result.data.result;
      })
      .then(({ id, token }) => {
        const options = { headers: { token } };
        this.#data.store.token = token;

        return axios.all([
          axios.get(`/api/user/${id}`, options),
          axios.get(`/api/user/${id}/posts`, options),
        ]);
      })

      .then(([profile, posts]) => {
        this.#data.store.userProfile = profile.data.result;
        this.#data.store.userPosts = posts.data.results;

        location.href = '/#/profile';
      })
      .catch(error => {      
        this.#loginFail = true;
        this.render();
      });
  }
```
上のロジックは、ログインフォームでログインボタンを押した時submitイベントが発生した時、呼び出される関数です。関数の内容では、ログイン認証に成功をすると、ログインユーザのプロフィール情報と書き込みを表示します。
<br>
```javascript:axios.post()
axios.post('/api/authentication', loginData)
  .then(result => {
    return result.data.result;
  })
```
Axiosはpromiseをリターンするライブラリであるため、thenチェーンを使用しています。最初のthen()は成功したとき、呼び出されます。
<br>
```javascript:axios.all(array) - api並列処理
.then(({ id, token }) => {
    const options = { headers: { token } };
    this.#data.store.token = token;

    return axios.all([
      axios.get(`/api/user/${id}`, options),
      axios.get(`/api/user/${id}/posts`, options),
    ]);
 })
```
ログインに成功したら、idとtokenを持ってユーザープロファイルと投稿情報を出力します。 そのためには、ユーザープロファイルapiと投稿情報apiを呼び出す必要があります。 この2つのapiを直列処理するよりも並列処理する方が時間的に節約できるので、api呼出方式を並列処理方式にした方が良いでしょう。 
そのためにはaxios.all(array)を使います。
<br>

```javascript:axios.all(array) - リターン処理
.then(([profile, posts]) => {
  this.#data.store.userProfile = profile.data.result;
  this.#data.store.userPosts = posts.data.results;

  location.href = '/#/profile';
})
.catch(error => {      
  this.#loginFail = true;
  this.render();
});
```
並列処理したapiの中で、誰が一番最初に終わるのかは分かりません。
並列処理したapi n個のapiがすべて完了したとき、次にthenを呼び出すことになります。 
[profile, posts] この順序はaxios.all(array)のarrayの順序が保障されます。
catch()はapi通信結果が異常だった場合、この関数がキャッチします。
【Python】マイクロ秒を含まない形式(2023-07-12 22:43:13)で現在の日時を取得する方法

# 概要
マイクロ秒を除外して現在の日時（秒までの情報）を取得するコードを3つ紹介します。
`2023-07-12 22:43:13`のような形式です。
標準ライブラリの`datetime`（日付と時間を操作するためのクラスや関数を提供するモジュール）を使用します。

# サンプルコード解説

```py
from datetime import datetime

# 1 
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(now)

# 2
now = datetime.now().replace(microsecond=0)
print(now)

# 3
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-7]
print(now)

# 出力結果
# 2023-07-12 22:43:13
# 2023-07-12 22:43:13
# 2023-07-12 22:43:13
```

上記、いずれの場合も同じように「`2023-07-12 22:43:13`」と表示され、マイクロ秒の部分をカットできています。

### 1.`strftime()`を使用して秒までの情報を取得する方法
1つ目の方法では、`strftime()`を使用して、指定されたフォーマットに基づいて日時を文字列に変換しています。
`"%Y-%m-%d %H:%M:%S"`はフォーマット文字列であり、以下のような意味になります。

%Yは、4桁の年を表します（例: 2023）
%mは、2桁の月を表します（01から12）
%dは、2桁の日を表します（01から31）
%Hは、24時間形式の時を表します（00から23）
%Mは、分を表します（00から59）
%Sは、秒を表します（00から59）


フォーマット文字列を使用しているため自由にカスタマイズできます。

### 2. `replace(microsecond=0)`を使用してマイクロ秒を除外する方法:
2つ目の方法では、`replace(microsecond=0)`によって、マイクロ秒の部分が0に置き換えられます。0に置換されることで、マイクロ秒が表示されなくなる、という方法です。
結果が`datetime`オブジェクトとして返されるため、さらなる日時の計算や操作が可能であるという柔軟性はありますが、2回の関数呼び出しを行うため、パフォーマンス上のわずかなオーバーヘッドが発生する可能性があります。

### 3. スライスを使ってマイクロ秒を除外する方法:
2つ目の方法では、スライス`[:-7]`によってマイクロ秒部分を除外しています。
直感的にどこで区切っているのかわかりづらく、また、マイクロ秒を含む文字列を一度生成してからスライスしているので、無駄な処理があります。
HTML5プロフェッショナル認定試験 レベル1　を受けてみた

とっても久々に記事を書きます:santa:
先日、表題の通り、HTML5プロフェッショナル認定試験 レベル1を受けてみました。
<h2>所感</h2>
そこまで知らなくても生きていけるけど、
知ってたら綺麗なマークアップが出来る様になるという感じでした。

（意外と知らないことが多かった...）

特にHTMLやCSSは新しい記述が増えてました！

<h2>勉強方法</h2>
Ping-t(<a href="https://mondai.ping-t.com/" target="blank">https://mondai.ping-t.com/</a>)で全問題を解いて、解説をよく読む。
<br>
やった勉強はこれだけです！
これで合格しました♪
<br><br>
ただ、500問近くあるので<b>10時間くらい</b>かかりました:innocent:

<h2>まとめ</h2>
自分にとっては、HTTPレスポンスやHTTPリクエスト系の問題が1番学びが多かった。

あとRGBコードの問題は投げ出しましたw
（RGBコード覚えてられないw）

フロント系の基礎的な知識を勉強される方にはお勧めです！

レベル２からはJavaScriptの問題になり、難易度が高くなります！
よし！レベル２が本番ですね..:smirk_cat:
SwiftUIでハンドスピナーを作る方法

# はじめに
この記事では、スワイプするとハンドスピナーの回転が加速するアニメーションを作る方法を紹介します。

# 対象者
SwiftUIの基本的な使い方を知っている人


# やり方

```swift
struct ContentView: View {
    @State private var angle: Double = 0

    // 加速度
    @State private var acceleration: Double = 0

    private let timer = Timer.publish(every: 0.01, on: .main, in: .common).autoconnect()

    var body: some View {
        Image(systemName: "fanblades.fill")
            .resizable()
            .frame(width: 300, height: 300)
            .rotationEffect(.degrees(angle))
            .gesture(
                DragGesture()
                    .onChanged { value in
                        // 加速度を更新
                        acceleration += value.translation.height / 10
                    }
            )
            .onReceive(timer) { _ in
                // 減速させる
                acceleration += -(acceleration * 0.01)
                // 角度を更新
                angle += acceleration
            }
    }
}
```

## ポイント
- `DragGesture`を使って、スワイプした距離を加速度に反映させる
- `Timer`を使って、0.01秒おきに角度の更新と減速を行う

# おわりに
何年か前にハンドスピナーがすごく流行ったことがありましたね。この記事が参考になったという方は、フォローといいねしていただけると嬉しいです😆