OmniAICreator/Qiita-1.07M huggingface dataset:
【Android】Test dynamic delivery offline のやり方

# はじめに
Android AppではDynamic Feature Modules（DFM）を用いて、リリース後に必要な機能を追加でインストールすることができます。これにより開発者がアプリのサイズを小さくできたり、ユーザも使わない機能はそもそもインストールしないことでストレージの節約にもなるという便利なものです。
今回はこちらのDFMのインストール状況をユーザへと通知するテストを、内部テストを使用することなく完全オフラインで実施できるようにしていきます。

# FakeSplitInstallManagerの実装

https://developer.android.com/reference/com/google/android/play/core/splitinstall/testing/FakeSplitInstallManager

こちらを使っていきます。
通常のDFMのリクエストやインストールをするにはSplitInstallManagerを使いますが、そのFakeバージョンになります。
まずはSplitInstallManagerやSplitInstallManagerFactoryを使っている箇所を、それぞれFakeSplitInstallManagerとFakeSplitInstallManagerFactoryに入れ替えます。
この時に、FakeSplitInstallManagerFactory.createの第二引数はディレクトリ名を指定します。

```java:
private FakeSplitInstallManager mFakeSplitInstallManager;
mFakeSplitInstallManager = FakeSplitInstallManagerFactory.create(this, this.getExternalFilesDir("local_testing"));
```

# 分割されたAPKファイルを生成する
FakeSplitInstallManagerを実装した状態で通常通りAndroid Studioでビルドしても、アプリはクラッシュしてしまう。そこで、aabファイルを生成 -> bundletoolでapksを生成という形で、実機にインストールできるものを作っていく
まずはAndroid Studioでaabを作成する。
aabができたら、それがあるディレクトリに移動し、下記コマンドを用いてapksファイルを生成する。

```
bundletool build-apks --local-testing --bundle sample.aab --output sample.apks
```

# apksファイルのインストール
apksファイルがあるディレクトリで、下記コマンドを用いてapkを実機にインストールする。

```
bundletool install-apks --apks=sample.apks
```

これでインストールが完了します。あとはDFMをインストールする動作を実機で確認すれば、ダウンロードからインストールまでの動作テストをすることができます。

# 意図的にネットワークエラーを起こす
テストで「ダウンロード失敗時」のケースをカバーするために、意図的にエラーを起こします。
エラーを起こしたい処理の箇所に下記コードを追記します。

```
FakeSplitInstallManager.setShouldNetworkError(true);
```

# おわりに
設定をするのはややこしいところもありますが、内部テストでの公開をすることなくダウンロードからインストールの動作を確認できるのは便利です。
ただし、こちらはあくまでもmock的な立ち位置です。最終的な確認の際にはPlay Storeを用いて確認すると、一層品質の担保ができそうです。
テキストファイルにpayloadを設定してpost送信

# テキストファイルにpayloadを設定してpost送信

## テキストファイルを準備する(urlの末尾、payload)
update-employee,{"user_id":"9991","name_sei":"山田","name_mei":"花子"}
update-employee,{"user_id":"9992","name_sei":"山田","name_mei":"太郎"}

## payloadの送信

    try:
        datalist = []
        txtfile = r'<post.txtのフルパス>'
        with open(txtfile, 'r', encoding="utf-8") as f:
            while True:
                data = f.readline()
                if not data:
                    break
                dlist = data.split(',')
                datalist.append((dlist[0], data.replace(
                    dlist[0] + ',', '').replace('\n', '')))
        urlbase = '<エンドポイント(末尾の手前まで)>'
        headers = {
            'Content-Type': 'application/json'
        }
        for data in datalist:
            url = urlbase + data[0]
            payload = data[1]
            req = urllib.request.Request(
                url, payload.encode(), headers, 'POST')
            with urllib.request.urlopen(req) as res:
                body = res.read()
    except Exception as e:
        print(e)
WindowsServerとライセンスのぼんやりした認識

#サーバ関連のライセンスはとてもややこしい

物理サーバーにWindowsServerをゼロから構築する機会があった。
自分の備忘録としてぼんやりとした認識を書き留めておく。
結論として、用意するライセンスは①サーバと②ユーザにかかるものの2つが必要。

##①サーバ本体にかかるライセンス
Windowsパソコンを自作するとき、osライセンスを購入する。
サーバでも同様で、osを購入するときに付いてくるライセンスがこれだ。
ただし、サーバはパソコンと違って、1台のスペックにかなりの差がでてくる。
ゴリゴリに高性能なサーバと貧弱なサーバを同じラインセスで許可してしまうのは具合が悪い。
そこで、1台に1ライセンスではなく、「物理コア数」にもとづいて算出する。

用意したマシンのスペックによっては複数ライセンス購入する必要がある。

##②使用するユーザにかかるライセンス
クライアントアクセスライセンス（CAL）も用意する必要がある。
サーバを使用するユーザ数分サーバライセンスとは別に購入する必要がある。
（以前はサーバライセンスを購入すると数ユーザ分のCALが付いてきたようだ）

CALはサーバに登録するとかそういうものではない。
あくまで、ユーザ側の管理でこれだけのユーザが使用するから相応のCALを保有していますということを示せればよい…らしい。


**リモートデスクトップCAL**
標準の2ユーザを超えてリモートデスクトップを使用する場合、さらにリモートデスクトップCALをユーザ数分購入する必要がる。
こちらは通常の？CALと異なり、購入後、正式に認証を受けなければならない。





ぼんやりとした認識なので、今後も修正と追記を随時更新していく。
サーバのライセンスは仮想が関係するとますますややこしくなるので、これからはクラウドを使用しましょう！


#参考資料
Windows Server 2019 システム構成ガイド（NEC）
https://jpn.nec.com/express/systemguide/sw/WS2019.pdf
cdnで読み込んでいたライブラリからhttpの読み込みができなかった

# アクシデント内容
サイトがhttpsでcdn先がhttpのライブラリが読み込めなかった

# 原因
httpsの規約？でhttpのライブラリを読み込めない
（その旨のエラー文がデベロッパーツールで表示されていた気がする…たしか）

# 対処
- ライブラリをDLしてそれを参照する
Javaの開発環境　　~JDK(Java Development Kit)~

#Java Development Kitとは？
日本語でjava開発キットと呼ばれています。Javaの開発に必要な各種ツールが入ったアプリケーションです。このJDKにはJavaコンパイラとJava仮想マシンが入っています。

-Javaコンパイラ  :Java用のファイルにプログラムを変換する。
-Java仮想マシン :Java用のファイルを読み取り実行する。

#Javaにおけるプログラミングとは？

プログラムというのは一般的に、まず、人間語で元ネタ（ソースコード）を作ります。
人間語で書かれたプログラムの元ネタファイルは「ソースファイル」です。
#↓

次にコンパイル(編集)という作業を通して、コンピュータが分かる形に変換します。
#↓
これでコンピュータさんは、プログラムの内容を理解できるようになりました。
あとは実際に動かします。
#↓
いろいろと端折りましたが、これが、プログラムができるまでの流れです。

プログラムを作るときには、コンパイルしてくれるやつ（コンパイラ）が必要です。
他にも、必要なものや、あると便利なものが、あれやこれやとあります。

以上を踏まえてJavaのプログラムを作るときに必要な、あるいは、あると便利なあれやこれやを詰め合わせたものが「JDK（ジェイディーケー）」です。
Chormeでクリックした要素に枠線が付く現象

# 概要
chromeである要素だけクリックした時に黒い枠線が表示されることがあった

# 原因
対象要素にtabindex属性が付いていて、クリックした時に強調としてoutlineプロパティが設定される

### そもそもなぜtabindexが付いたのか？
スライダーライブラリのslick.jsを使用しており、そこでtabindexが付与されるようになっていた

# 対処2種
- tabindexを削除
- outline: none;を設定

（そもそもスライダーの範囲の横に余白があり、その余白部分だけ枠線が表示されていた。なのでスライダーとそのコンテンツのサイズは合わせるようにすれば良い）


# 参考
[tabindex - HTML: HyperText Markup Language | MDN](https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/tabindex)

[outline - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/outline)

[テキスト入力欄のフォーカス時にChromeなどが自動付加する枠線を消す方法 - スタイルシートTipsふぁくとりー](https://www.nishishi.com/css/chrome-textform-clear-border.html)
defer内でのエラーを考慮する。

# はじめに
- `defer`句でエラーが起きたらどうするか

# 結論
- ※個人的に良いなと思ったものです。
- `close`時に発生したエラーとオリジナルのエラーの両方をログなどで確認することができる。

```golang
defer func() {
    deferError := c.Close() //c.Closeはエラーを返す可能性があります。
    if deferError == nil {
      return
    }
    err = fmt.Errorf("close: %v,  original: %v", deferError, err)
}()

//よくない例
defer c.Close()
```

# 参考
[Go 言語(Golang) はまりどころと解決策](https://www.yunabe.jp/docs/golang_pitfall.html#defer-%E3%81%AE%E4%B8%AD%E3%81%A7%E7%99%BA%E7%94%9F%E3%81%97%E3%81%9F-error-%E3%82%92%E5%87%A6%E7%90%86%E3%81%97%E5%BF%98%E3%82%8C%E3%82%8B)
reCAPTCHA v2 と v3 の違い

この記事では reCAPTCHA v2 と v3 の違いについて説明します。

reCAPTCHA v2 には、Invisible、Checkbox、Android の3種類があります。この記事では v2 Invisible を主に扱います。Checkbox は「私はロボットではありません」のチェックボックスを見せるタイプ、Android はアプリに組み込むタイプで、これらを使いたい場合は、特に迷うことはないと思います。

https://developers.google.com/recaptcha/docs/invisible
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/c7491410-215e-504d-b21b-6a5f5294f7f8.png)


v3 は v2 を完全に置き換えるものではありません。v2 は、[今後も並行してセキュリティおよびユーザビリティの改善とサポートを行う](https://developers.google.com/recaptcha/docs/faq#should-i-use-recaptcha-v2-or-v3)と、FAQ にあります。

## 見た目は一緒

v2 Invisible と v3 の見た目は同じです。デフォルトでは、画面の横にバッジが表示されます。このバッジは[消すこともできます](https://developers.google.com/recaptcha/docs/faq#id-like-to-hide-the-recaptcha-badge.-what-is-allowed)。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/666cc432-f451-3f2b-a5ee-54b46536d13f.png)

## 何が違うのか

v2 と v3 の最も大きな違いは、怪しいリクエストを通すかどうかという点です。

v2 は、人間かボットか怪しいリクエストが来たら、画像認証のクイズを出して、ボットをシャットアウトしようとします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/51d9eb6b-b99a-9e67-860f-7bba2d11dba7.png)

一方、v3 には、画像認証を出す機能はありません。人間だろうがボットだろうが、全てのリクエストを素通しにします。代わりに、リクエストに対して「人間らしさ」のスコアを設定します。

![recaptcha.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/30cfdb11-6df0-8f57-9d2e-d32b0ef2bd8b.png)


## v3 はパワーユーザー向け

[FAQ の冒頭に書かれている通り](https://developers.google.com/recaptcha/docs/faq#should-i-use-recaptcha-v2-or-v3)、v3 はパワーユーザー向けです。スコアに応じた対処方法を、自前で実装する必要があります。

低いスコアが出た場合の対処方法は、例えば以下のようなものが考えられます。

 * [ログイン時に、二要素認証の入力を求める](https://developers.google.com/recaptcha/docs/v3#interpreting_the_score)
 * ブログコメントをすぐには掲載せず、モデレータが評価するまで保留にする
 * 問い合わせをスパムフォルダに保存する
 * **しきい値を設定して、足切りを行う**

足切りというのは、スコアが悪かったら、ボットだろうが人間だろうが、リクエストを拒絶するという意味です。

![recaptcha2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2596/9755e591-e0b9-f60c-0bb5-6e4a539be371.png)


## 本当に足切りしていいの？

追加で開発を行うことなく v3 を採用しようとすると、大抵は「足切り」を行うことになると思います。その場合は、導入しようとしているサービスで本当に足切りが許されるのかどうか、よく検討する必要があります。

例えば、お客様からの問い合わせやクレームを受け付けるフォームで「あなたは人間らしくないからエラーにします」と言って許されるのかどうか、ということです。何人のお客様がエラーで離脱したか、本当のところはわかりません。

スパムフォルダのような機能があるなら、v3 は非常に良い選択肢です。ただし、その場合は、誤検知を定期的にチェックしなければなりません。


## 導入先の違い

### v3 の導入ページ

v3 はユーザのインタラクションを邪魔しないので、できるだけ複数のページに導入することが推奨されています。

 * https://webmaster-ja.googleblog.com/2018/10/introducing-recaptcha-v3-new-way-to.html

例えば、WordPress の人気プラグイン Contact Form 7 では[v3 を全ページに導入する](https://contactform7.com/ja/faq-about-recaptcha-v3/#stop-script-loading) 前提で足切りを行っているようでした。[v2 を捨てて v3 に移行した意図はわかりませんが](https://wordpress.org/search/recaptcha+v2+intext%3A%22Plugin%3A+Contact+Form+7%22)、全ページに入れれば十分な精度が出せるという判断なのかもしれません。

### v2 の導入ページ

v2 は、アクションが起きる特定のページにだけ導入すれば十分です。他に入れられる場所がありません。

reCAPTCHA トークンは有効期限が 2 分と非常に短いので、確認画面がある場合は、確認画面に入れるのが適切です。トークンを、フォームから確認画面へと持ち回るべきではありません。

v2 Invisible では、送信ボタンを押してからサーバで検証するまでの期間が 2 分です。v2 Checkbox は、チェックを入れた状態でしばらく放置すると、勝手にチェックが外れます。

## まとめ

v2 と v3 は守備範囲が違うので、使い分けが必要です。検索すると v3 が v2 の改良版・高機能版だという解説が山ほど見つかりますが、間違いです。

v3 単体では、v2 のようにボットを排除することはできません。v2 の画像認証の代わりに、自力でなんとかする(できる)のが v3 です。

単にお問い合わせフォームやコメント、ユーザ登録などでスパムを避けたい場合は、v2 Invisible を使う方が簡単です。
flutter webをgithub pagesでdeployする

1.リリースためのファイルをbuild

```
flutter build web --release
```

2./build/web/index.htmlのheader→baseのhrefをrepository名前に修正

```html
  <base href="/repository-name/">
```
3./build/webフォルダをgithubの新しいrepositoryへpush
4.github repositoryのSettingsのGitHub Pagesでhostを設定する
5.完了
ROS launchファイルで環境変数を取得する

roslaunchでは以下のように表記することでホームディレクトリのパスを取得できる.

```
$(env HOME)
```

![Screenshot from 2021-03-28 22-21-34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589906/b92bcb97-a9b4-91d6-2ce1-f281efe0f4ec.png)

参考

https://myenigma.hatenablog.com/entry/2016/05/28/111714
【Rails6】友達登録機能（ユーザーフォロー機能）の単体テストの実装例

※2022年から技術系の記事は[個人ブログ](https://yamaday0u.net/category/programmer/)に投稿しております。ぜひこちらもご覧ください→[yamaday0u Blog](https://yamaday0u.net/category/programmer/)

以前、別の記事で実装方法を紹介した友達登録機能の単体テストの実装例を紹介します。
[【Rails6】友達登録機能（ユーザーフォロー機能）の実装方法](https://qiita.com/yamaday0u/items/c4793b2b16ead7b2db76)

**目次**
- 前提条件
- Modelの確認
- 単体テストのコードの実装

## 前提条件
- RSpecをインストール済み。
- [前回の記事](https://qiita.com/yamaday0u/items/c4793b2b16ead7b2db76)で紹介した通りに友達登録機能を実装していること。

## Modelの確認
[前回の記事](https://qiita.com/yamaday0u/items/c4793b2b16ead7b2db76)からの再掲です。

```app/models/relationship.rb
# アソシエーションの定義
class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"

  # バリデーションの定義
  validates :follower_id, presence: true
  validates :followed_id, presence: true
  validates :follower_id, uniqueness: { scope: :followed_id }
end
```

```app/models/user.rb
class User < ApplicationRecord
  # メソッドの定義
  # ユーザーをフォロー
  def follow(other_user)
    active_relationships.create(followed_id: other_user.id)
  end
end
```

## 単体テストのコードの実装

```spec/models/relationship_spec.rb
require 'rails_helper'

RSpec.describe Relationship, type: :model do
  before do
    # ユーザー2名を事前に生成して登録
    @user = FactoryBot.create(:user)
    @another_user = FactoryBot.create(:user)
    # app/models/user.rbで定義したfollowメソッド（友達申請）の実行結果を変数@relationshipに代入
    @relationship = @user.follow(@another_user)
  end

  describe '#create' do
    context 'successfully' do
      it 'is valid with follower_id, followed_id' do
        expect(@relationship).to be_valid
      end
    end

    context 'unsuccessfully' do
      # 友達申請する側の値（follower_id）がなければ保存できない。
      it 'is invalid without follower_id(active relationship)' do
        @relationship.follower_id = ''
        @relationship.valid?
        expect(@relationship.errors.full_messages).to include('Follower must exist')
      end
      # 友達申請される側の値（followed_id）がなければ保存できない。
      it 'is invalid without followed_id(passive relationship' do
        @relationship.followed_id = ''
        @relationship.valid?
        expect(@relationship.errors.full_messages).to include('Followed must exist')
      end
      # 同じ組み合わせの友達申請のデータがすでに保存されている場合は保存できない
      it 'is invalid with duplicate relationship' do
        @relationship.save
        # @relationshipとは別のレコードとしてanother_relationshipを用意
        another_relationship = @user.follow(@another_user)
        # another_relationshipに@relationshipと同じ値を代入
        another_relationship.follower_id = @relationship.follower_id
        another_relationship.followed_id = @relationship.followed_id
        another_relationship.valid?
        expect(another_relationship.errors.full_messages).to include('Follower has already been taken')
      end
    end
  end
end
```

テストコードの最後の「同じ組み合わせの友達申請のデータがすでに保存されている場合は保存できない」は、`app/models/relationship.rb`で定義した以下のバリデーションが機能しているかを確認しています。

```app/models/relationship.rb
  validates :follower_id, uniqueness: { scope: :followed_id }
```
React bug: disabledにしたボタンがonMouseEnterやonMouseLeaveイベントを妨げる


# `disabled`にしたボタンの上からすぐ下の要素にマウスポインタを移しても`onMouseEnter`イベントが起こらない

`disabled`にしたボタンのすぐ下に、`onMouseEnter`イベントハンドラを定めた要素が置かれたときに生じる問題です。`disabled`にしたボタンの上から下の要素の領域にマウスポインタを入れても、`onMouseEnter`イベントが起こりません。Chromeで確認されました。

![2103002_001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128949/2848104d-141e-43e3-8289-47470bb066cb.png)

GitHubのReactを見ると、まさにそのものズバリのissue(問題)「[Bug: MouseEnter does not fire when coming _from_ disabled elements](https://github.com/facebook/react/issues/19419)」(2020/07/21)がいまだopen(未解決)のままになっています。マウスイベントの扱いを最適化する際に取りこぼしがあったようで、直すにはあちこちに手を入れなければならないらしいです。


# 確認のためのコードと対処法
つぎに掲げるのが、問題を確かめるための簡単なコードです。`button`要素は`disabled`とし、そのすぐ下の要素(`div`)に`onMouseEnter`と`onMouseLeave`のイベントハンドラを定めます。ボタンの上をとおって、下の領域にマウスポインタが入ると、`onMouseEnter`イベントは起こりません(Chromeの場合)。

```react
function App() {
	const mouseEnterHandler = (event) => {
		console.log('mouse enter');
	};
	const mouseLeaveHandler = (event) => {
		console.log('mouse leave');
	};
	return (
		<div>
			<button disabled>disabledボタン<br />
			この上をとおって下の領域に入る</button>
			<div
				onMouseEnter={mouseEnterHandler}
				onMouseLeave={mouseLeaveHandler}
			>
				onMouseEnter/onMouseLeaveハンドラからコンソール出力
			</div>
		</div>
	);
}
```

`disabled`の属性を与えたボタンは基本、マウスイベントを受け取らなくてよいでしょう。だとすると、つぎのように親要素(`span`)に包んで、マウスイベントをスルーさせれば([`pointer-events`](https://developer.mozilla.org/docs/Web/CSS/pointer-events)プロパティ値`none`)、下の要素は`onMouseEnter`が受け取れます。

```react
<span style={{ pointerEvents: 'none' }}>
	<button disabled>disabledボタン<br />
	親で包んでマウスイベントをスルー</button>
</span>
```

動きが確かめられるように、テスト用のコードをCodePenに掲げました。左が問題の`disabled`ボタンです。右のボタンは親に包んでマウスイベントをスルーさせています。

<p class="codepen" data-height="265" data-theme-id="light" data-default-tab="js,result" data-user="FumioNonaka" data-slug-hash="XWpmZBy" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="React bug: onMouseEnter does not fire when coming from disabled button elements">
  <span>See the Pen <a href="https://codepen.io/FumioNonaka/pen/XWpmZBy">
  React bug: onMouseEnter does not fire when coming from disabled button elements</a> by Fumio Nonaka (<a href="https://codepen.io/FumioNonaka">@FumioNonaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>


# disabledのボタンを含めると親のonMouseLeaveイベントが起こらない
「[React does not fire onMouseLeave events on a disabled button](https://lecstor.com/react-disabled-button-onmouseleave/)」は、`onMouseLeave`イベントが起こらない例を紹介しています。イベントハンドラが定められた要素に、`disabled`のボタンを含んだ場合です。CodeSandboxに[コード例](https://codesandbox.io/s/8xlw160jl9)が公開されているので、試してみることもできます。こちらは、つねに呼び出されないというわけではないようです。おそらく、前述の問題と根は同じでしょう。

もっとも、`disabled`ボタン自身に`onMouseLeave`ハンドラを書いたとき実行されないというのは、むしろ正しい動きに思えます。`onMouseEnter`ハンドラが呼び出される方が問題です。標準JavaScriptコードでイベントリスナーを定めたときは、どちらのイベントも起こりません。
パッケージ管理（Linuxコマンド）

## yumコマンド{パッケージ管理（CentOS）}
### 基本的な使い方

```
 yum [オプション] [コマンド] [パッケージ名称] 
 ```
### パッケージのインストール
yumコマンドでパッケージのインストールを実行するには、installコマンドを利用してパッケージ名称を指定する。

```
 yum install <パッケージ名称> 
 ```
インストール作業はroot権限が必要なので、このinstallコマンドもroot権限で実行する必要がある。

```sql:command&#x20;example
[root@ip-10-0-0-15 html]# sudo su
[root@ip-10-0-0-15 html]# 
[root@ip-10-0-0-15 html]# yum install httpd
```

### パッケージの情報表示
パッケージ名称だけではその内容がよくわからない場合、infoコマンドにより詳細情報を表示できる。

```
yum info <パッケージ名称> 
```
httpdパッケージの詳細情報は以下。

 ```sql:httpd&#x20;samaly
[ec2-user@ip-10-0-0-15 ~]$ yum info httpd
読み込んだプラグイン:extras_suggestions, langpacks, priorities, update-motd
インストール済みパッケージ
名前                : httpd
アーキテクチャー    : x86_64
バージョン          : 2.4.46
リリース            : 1.amzn2
容量                : 4.0 M
リポジトリー        : installed
提供元リポジトリー  : amzn2-core
要約                : Apache HTTP Server
URL                 : https://httpd.apache.org/
ライセンス          : ASL 2.0
説明                : The Apache HTTP Server is a powerful, efficient, and extensible
                    : web server.
```
プログラミング超初心者がAWSクラウドプラクティショナーに合格した勉強法について

#はじめに

初めまして、るあと申します。

この記事ではプログラミング初学者の筆者がAWS資格に十日程度で合格することができた為、今後受験される方のお役に立てればと思い共有させていただきます。

#AWSクラウドプラクティショナー資格について
AWS資格は現在11種類あり、その中でも基礎・入門レベルの資格がこのクラウドプラクティショナー資格です。
そのためAWSクラウドの特徴や料金プランなどの基本的な事を理解していれば、合格は可能だと思われます。
試験時間は90分で問題は65問出題されます。試験料は11,000 円（税別）と若干高額のため一発合格を目指したいところですね！

#具体的な勉強方法
自分はAWSって何？って感じだったので最初はインターネットで調べたり本屋でパラパラ読んでみたりしてました汗
プログラムやクラウドについてある程度知っている方だとかなり楽だと思います。
その後、資格対策本を買って読みました！

読んだ本:　 [AWSクラウドプラクティショナー](https://www.amazon.co.jp/AWS%E8%AA%8D%E5%AE%9A%E8%B3%87%E6%A0%BC%E8%A9%A6%E9%A8%93%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88-AWS%E8%AA%8D%E5%AE%9A-%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%BC-%E5%B1%B1%E4%B8%8B-%E5%85%89%E6%B4%8B/dp/4797397403/ref=sr_1_1?dchild=1&keywords=aws+%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%BC&qid=1616934654&sr=8-1 " Home")

一週目は完全には理解できないものもありましたが、とりあえず無視して読み進め一週目終了
気になったり理解して起きたいワードがあったらGoogle検索しておくと時間はかかるが理解は深まります。
二週目は理解しているところは読み飛ばし不安なところを集中的に学習します。

これだけだと受かるか不安だったので、Udemyの模擬試験問題集を購入

購入したもの: https://www.udemy.com/course/aws-4260/

これを繰り返し解いて少しずつ覚えていきます。
勉強とはできない事を少しずつできるようにする作業なので、問題を解いて間違えを見直しもう一度解く。
当たり前ですが、ある程度の理解があればこれが一番効率がいいと思います。
自分は基本レベルが７~9割くらいの点数が取れるまでやりました。
応用レベルの問題はあまり解きませんでしたがあまり本番でも出なかったのでまずは基本レベルを8割以上取れるようにし、余裕があれば応用レベルにもチャレンジしてみてください。

自分が試験対策で行った勉強は以上です。
まとめると、ある程度本やサイトで大まかに理解し、暗記はテストと見直しで覚えていくって流れですね！

#試験当日

自分はピアソンで受けました。PSIはわかりませんがピアソンの方が評判良かった気がします。

時間がかなり余るので落ち着いて解けると思います。
自分は４０分程度で見直しも終わり途中退室しました。
合否はその場でわかりますが細かい点数については後日メールで届きます。
問題の難易度はUdemyの基本レベルと同じくらいでたまにわからないものがありましたが
1000点満点で700点以上で合格のため基礎レベルで落とさなければ大丈夫です。
ちなみに筆者は843点でした。

#終わりに
AWSはとても便利で様々な企業が導入しているサービスなので、今後も他のAWS資格や実務などで理解を深めていきたいと思いました。

読んでいただきありがとうございます。受験される方々、心より応援しております！
WSL2回線速度遅いよ問題

#はじめに
現行環境でCUDA on WSL2を導入後し，WSL2内でwgetやgit cloneなどを実行すると回線速度が大幅に減少してしまっている事象が指摘されています。
[Very slow network speed on WSL2 (Microsoft/WSL/issues/4901)](https://github.com/microsoft/WSL/issues/4901)
私も同じ事象になりましたので解決策を記載します。

#何が起きている？
WSLがネットワークを利用するのにHyper-Vの仮想スイッチを使用しており，これが悪さをしているようです。
2017年にも同様の[issue](https://github.com/microsoft/WSL/issues/2477)が存在します。

#どうすればよい？
スレッドではデバイスマネージャーでHyper-V仮想LANの"Large Send Offload v2"を"Disabled"に変更すると直ると報告されておりますが私の環境ではそもそも上記仮想LANが認識されませんでしたのでこの解決策は使えませんでした。
![](https://user-images.githubusercontent.com/582304/90357406-75368180-e085-11ea-88ff-12094db04acd.png)

あれこれネットワーク設定を見直した所，物理LANのネットワーク構成でipv6をoffにすることでやっと回線速度が回復し，WSL2上でまともな開発環境を整えることができました。
大学入学共通テスト「情報」の問題を全問解説してみた（元資格の予備校講師が徹底解説！）

##大学入学共通テスト「情報」のサンプル問題を全問動画解説してみました。

２０２１年３月２４日に大学入試センターが、２０２５年の大学入学共通テストから
情報科目を新設すると発表しました。

同時に、サンプル問題が提供されましたので、
今までの、IT業界１５年　、資格の予備校講師などの経験を生かして全問動画解説しました。

予備校なら結構なお金かかると思いますが、所得による教育格差をなくしたいので無料で公開します。

##第１問　セキュリティ、ネットワーク、情報デザイン等

取り急ぎ第１問解いてみたところ、満点でしたので
どのように解けばいいのかの思考プロセスもあわせて解説できればと思っています。

現時点で教科書はありませんが、情報科の先生が事前に勉強している、
教員研修用教材の内容も紐づけながら解説したいと思います。

https://note.com/toppakou/n/n073f5388861c


##第２問　プログラミング、アルゴリズム

練習すれば、解くことは出来るけど、これを書籍ベースで教えるのは
教える方も、教わる方も結構大変だと感じています。

今回の解説も、動画をメインにして、文字おこしも動画と突き合わせて確認頂けると幸いです。

プログラミングのペーパーテストのコツは、変数（特にカウントアップ系、配列系）に
実際に値を渡してシミュレーションをペーパー上で行うことです。


穴埋めなので、自分自身ならこんなプログラムにしない！
という突っ込み問題も今後出てくると思いますが、
実際の開発現場でも他人のコードに突っ込み入れながら、

退職者の作業を引き継ぐことは普通にあるので、
ヒントとなる処理の日本語説明と突き合わせながら
ソースコードの意図を読み取れるように頑張りましょう。

プログラミング問題は、初めの問題で意図を読み取れないと、
芋づる式に全問落とす可能性もあるので、あせらず初めの問題から丁寧にプログラム仕様を把握していきましょう。

https://note.com/toppakou/n/n31b52dd22d6f

##第３問　データ分析、データ活用

複雑な複数の表を組み合わせて、意図を読み取れなければ何を言っているか分からない問題です。

あと、数学Ⅰの四分位数の知識と第２四分位数（Q2）が中央値であることの
知識が無いと答えられない問題もあります。

逆にその知識があれば解けます。標準偏差関連では値が大きいほど
ばらつきが大きいということを知っているかどうかということも大切になってきます。

https://note.com/toppakou/n/nc99bb50c2ccb

##YouTubeチャンネル
これからも解説動画増やしていきます。
https://www.youtube.com/channel/UC_0LOFMRg60xDBfkzMAmkfA


##メモ
RailsにおけるSeedファイルの作成

##seedファイルって何？？

seedファイルというのは簡単に言うと初期データのことです。　　
コーディング中に何らかの理由でデータベースをリセットした場合、中に入っているデータももちろんですが消えてしまいます。
しかし、Seedファイルを最初に作成してしまえばあとは反映させるだけで何回でも蘇ります。


##基本的な書き方

```
User.create!(
   email: 'test@test.com',
   name: 'テスト太郎',
   image: File.open('./app/assets/images/test.jpg')
)
```


Userモデルに対して[ email ][ name ][ image ]というカラムを指定し、それぞれの型に合わせて記述します。

Userモデル以外にも、Postモデルなども同じ要領で作成します。
モデル名とカラム名が変わるだけです。

```
Post.create!(
  user_id: 2,
  title: "テスト",
  product: "テストです"
)
```
Postモデルの場合、投稿はユーザーが存在しないと出来ないため、先にUserモデルを記述しておく必要があります。そういないとエラーが発生します。


##大量生産することも可能

```
5.times do |n|
    User.create!(
      email: "test#{n + 1}@test.com",
      name: "テスト太郎#{n + 1}",
      image: File.open('./app/assets/images/test.jpg')
    )
  end
```

しかし、このやり方はあまりオススメはしません。
ポートフォリオとして企業に見せる場合、初期データは実際の運営を想定したデータを投影する方が見栄えがいいからです。
面倒ですが手作業で一つずつ実際のユーザーを想定して作成することをオススメします。

```
[
  ['test1@test.com', 'テスト太郎', image: File.open('./app/assets/images/test.jpg')],
  ['test2@test.com', 'テスト徹子', image: File.open('./app/assets/images/test.jpg')]
].each do |mail, name, img|
  User.create!(
    { email: mail, name: name, image: img}
  )
```
無駄なく記述する場合なら、この記述方法が一番コードを少なくかけます。
欠点はカラムが多くなるとどれがどのカラムを指しているかわかりずらくなることです。


##中間テーブルの場合

フォロー機能の場合

```
Relationship.create!(
  user_id: 1,
  follow_id: 5
)
```

タグ機能の場合

```
Tagmap.create!(
  tag_id: 20,
  post_id: 12
)
```

中間テーブルの場合は、アソシエーションしているモデルのidそれぞれに、該当させたい番号を設定するだけで大丈夫です。
また、この時もモデルにデータが存在しないとエラーが起こるため、上の行に記述しておく必要があります。


##仕上げ

seedファイルを書いたあとは

```
rails db:seed
```

としてあげることでseedに書かれた内容をデータベースに反映させることができます。
実際にデータが投入されたかどうか、コンソールなどで確認して見てください。
herokuで画像を正しく配置しているのにActionView::Template::Errorがでる場合の対処法



# 背景
作成したアプロケーションをherokuにデプロイしたところ下記のエラー画面が表示された
![スクリーンショット 2021-03-28 17.26.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1248254/b2fb3aa4-8b18-519a-190d-a13668836c51.png)

ログを確認してみると以下のようなエラー文が表示されている。

```ruby
ActionView::Template::Error (The asset "slider-01.jpeg" is not present in the asset pipeline.
```
トップページに使用しているslider-01.jpegがassetに配置されていないと言われているがローカルでは問題なく表示されている。
画像のサイズの問題かと思い、画像サイズの変更も行ったが改善は見られなかった。

# 環境
ruby 2.6.5
Rails 6.0.3.5
heroku/7.51.0

# 解決方法

試行錯誤の結果、jpegファイルが問題ではないかと仮説を立て、試しにpngファイルに変更してみるとエラーは表示されなくなった。

どうやらherokuではjpg、pngは問題なく使用できますがjpegになると不具合が起きるようです。

# 最後に
初投稿ですので見づらい箇所がございましたら申し訳ございません。
どなたかの参考になりましたら幸いです。
GPUは認識しているのにgeneratorを使うとcuDNN初期化エラーが出たけど解決した話

ドハマりしました。
何とか解決できたので同じ悩みの方のお役に立てるよう解決策をメモします。

#環境

- Anaconnda
- GPU : NVIDIA Geforce RTX 2060 with Max-Q
- tensorflow-gpu : 2.1.0
- cudatoolkit : 10.1
- cudnn : 7.6.5

#症状
対応表を見てGPUに合ったTensorflow, CUDA, cuDNNをインストール、GPUが認識され自作CNNでGPUが使えていることまでは確認しました。

しかし、量の多いデータを処理する時にImagegenerator を使ったところ、モデル学習のところで下記エラーが発生。

`Unknown: Failed to get convolution algorithm. This is probably because cuDNN failed to initialize, so try looking to see if a warning log message was printed above.`

調べてもGPUがそもそも認識していない場合の対処方法が多くあまり参考になりませんでした。

#解決法
下記の記事を参考にして解決。
<https://note.com/axg28923/n/na41c975d212c>

まずAnaconda promptでエラーが吐かれた場所を確認。
`Successfully opened dynamic library cudnn64_7.dll`
がありcudnn64_7.dllファイルをtennsorflowが読みに行けている。

問題は、
`Could not create cudnn handle: CUDNN_STATUS_ALLOC_FAILE`
とあるのでGPUは動く気はある。問題はメモリ。

環境変数を設定して対応。
windowsの検索窓から「システム環境変数の編集」を選択。
詳細設定から環境変数を選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690432/1d642ece-fa2c-a398-d5dd-426642099ba3.png)

システム環境変数を新規登録。
変数名は「TF_FORCE_GPU_ALLOW_GROWTH」
変数値は「true」と記入。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690432/6c9f3d77-3c0d-4247-f84a-8461f3c199d1.png)

再起動すれば完了。
晴れてcudnnが使えるようになりました。
ffmpegでDiscordのVCに動画を音声と共に流す

# はじめに
お久しぶりです。初めての方は初めまして。

ffmpegを使って、DiscordのVCに動画を音声と共に流してみたいと思います。

この記事は、Ubuntu Desktop 20.04での方法を書いています。
また、各コマンドの意味は一番最後の`ffmpeg`以外ほとんど理解していないため、その点はご容赦下さい。

加えて、この行為によってどうなっても知りません。何も保証しません。できません。よろしくお願いします。

# 他との違い
他のV4L2の記事では全て映像のみですが、この記事では**両方**扱います。

# セットアップ
まず、必要なものは以下の通りになります。

- ffmpeg
- ブラウザかDiscordのデスクトップクライアント
    - 今回はGoogle Chromeを使用します
    - FirefoxはVCとの相性が悪いのでおすすめしません
- 次に挙げるもの

## 物理カメラを無効化
`ls /dev/video*`して出てきたビデオデバイスを、必要に応じて無効化します。
これをしないと、映像を止めた隙に顔バレします。ご注意下さい。

## 仮想カメラ (映像のために必要)
- http://www.nofuture.tv/diary/20200413.html
- https://sounanda.jp/itskillup/?p=1601

上記を参考に、

```bash
# インストール
sudo apt install v4l2loopback-dkms v4l2loopback-utils

# /dev/video10 を生成
sudo /usr/sbin/modprobe v4l2loopback devices=1 video_nr=10 card_label="OBS Cam" exclusive_caps=1
```
このようにして、`OBS Cam`という名前で`/dev/video10`を生成します。

## 仮想マイク (音声のために必要)
- https://stackoverflow.com/questions/61990828/how-to-redirect-an-audio-stream-to-a-virtual-pulseaudio-microphone-with-ffmpeg

上記を参考に、

```bash
# 仮想マイクを作成
pactl load-module module-null-sink sink_name="virtual_speaker" sink_properties=device.description="virtual_speaker"
pactl load-module module-remap-source master="virtual_speaker.monitor" source_name="virtual_mic" source_properties=device.description="virtual_mic"
```

`virtual_mic`という名前でマイクを生成します。

# 本番
1倍速以上でデコードできる動画を使うことをおすすめします。

まず、DiscordでVCに参加します。何をしても文句を言われない所にして下さい。

次に、先に何らかの動画を次のコマンドで再生します。

```
PULSE_SINK=virtual_speaker ffmpeg -re -i "video.mkv" -f v4l2 /dev/video10 -f pulse "stream name"
```

その後、Discord側で配信の設定を行います。

![カメラ設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111609/14f10018-cc3e-4b45-7543-bdfc87f44c5e.png)

![マイク設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111609/5270cb81-845a-58f2-2ff8-a1e587f6b709.png)

画像の通り、カメラを`OBS Cam`に、マイクを`virtual_mic`にします。
また、同時に映像が反転していないことを確認してください。
音声に関しては、サブ垢を使うか同じチャンネルの別の人に確認してもらって下さい。

反転している場合の対処法は、後述します。

その後、`ffmpeg`を`Ctrl+C`で止めてから再度再生すれば、映像を音声つきでDiscordに流すことができます。

# その他
## コマンドの説明
```
PULSE_SINK=virtual_speaker ffmpeg -re -i "video.mkv" -f v4l2 /dev/video10 -f pulse "stream name"
```
主要な部分だけ説明します。

- `PULSE_SINK=virtual_speaker` --- 出力先のマイクデバイスの指定
- `-re` --- リアルタイム出力。これがないと速度が無制限になるので、一瞬で終わる
- `-f v4l2 /dev/video10` --- 映像の出力先の指定
- `-f pulse "stream name"` --- 音声の出力先の指定。`stream name`はよく分からない

なお出力を同時に2つ指定していますが、これでも問題は無いようです。

## 反転してしまう場合
- https://duxyng.wordpress.com/2013/04/07/rotateflip-video-with-ffmpeg/

ffmpegが映像をV4L2に入力する際、場合によっては左右反転する場合があるので、そのときは`-vf hflip`をV4L2の出力に追加して下さい。
他の反転は上記のURLを参照して下さい。

# おわりに
以上の方法で、DiscordのVCに動画と音声の両方を流すことができます。

...というかこれってBotでやるべきだと思うんですけど
初めてのPython学習-基礎②-

#複数のデータをまとめて扱う

```
リスト[要素1,要素2,要素3,要素4,.....]
```

※要素の番号（インデックス番号）は0から始まる


**▼文字列のリスト**

```
['yakiniku','yakimeshi','yakitori']
```

焼き肉・・・インデックス番号[0]
焼き飯・・・インデックス番号[1]
焼き鳥・・・インデックス番号[2]

```
foods = ['yakiniku','yakimeshi','yakitori']
print('今日の晩御飯は' + foods[1] + 'です')

※文字列のリストを作る際には、””を忘れない

コンソール
　→今日の晩御飯は焼き飯です
```

**▼数値のリスト**

```
[1,2,3,4,5]
```

**▼文字列と数字が混合したリスト**

```
['banana','orange',1,2]
```

**▼リストを変数に代入**

```
cars = ['toyota','matsuda','nissan','honda']
print(cars)
```

**▼リストの中身を変更**
リスト[インデックス番号] = 値

```
cars = ['toyota','matsuda','nissan','honda']
cars[1] = 'daihatsu'
print(cars)

コンソール
　→['toyota','daihatsu','nissan','honda']
```

**▼リストに要素を追加**

```
リスト.append(値)

cars = ['toyota','matsuda','nissan','honda']
cars.append('suzuki')
print(cars)

コンソール
　→ ['toyota','matsuda','nissan','honda','suzuki']
```

#リストの要素を全て取得
for文

**▼for文を使うと出来ること**
リストの要素の数だけ処理を繰り返すことが出来る

**▼構文**
for 変数名 in リスト:

**▼処理の流れ**

```
cars = ['toyota','matsuda','nissan','honda']

for car in cars:
    print('私の愛車は' +car+ 'です')
```

①変数carにtoyotaが代入
②for文の中身【print('私の愛車は' +car+ 'です')】が実行される【私の愛車はtoyotaです】
③変数carにmatsudaが代入
④for文の中身【print('私の愛車は' +car+ 'です')】が実行される【私の愛車はmatsudaです】
⑤変数carにnissanが代入
⑥for文の中身【print('私の愛車は' +car+ 'です')】が実行される【私の愛車はnissanです】
⑦変数carにhondaが代入
⑥for文の中身【print('私の愛車は' +car+ 'です')】が実行される【私の愛車はhondaです】

#辞書を使う
リストと同じく複数のデータをまとめて管理する

**▼違い**
リスト・・・要素を番号で管理
辞書・・・名前（キー）を付けて管理

```
変数名 = {'dragonball':'gokuu','onepiece':'luffy','slamdunk':'hanamichi'}
※dragonballがキー、gokuuが値
```

①キーには文字列を使う
②{}で囲む
③キーと値の間は(:)、要素同士の間は（,）を使って区切る

**▼要素を取り出す**
辞書名[キー]

```
comics = {'dragonball':'gokuu','onepiece':'luffy','slamdunk':'hanamichi'}
print('好きな漫画の主人公は' + comics['dragonball'] + 'です')
　→好きな漫画の主人公はgokuuです
```

**▼要素を変更する**

```
comics = {'dragonball':'gokuu','onepiece':'luffy','slamdunk':'hanamichi'}
comics['dragonball'] = 'gohan'
print('好きな漫画の主人公は' + comics['dragonball'] + 'です')
　→好きな漫画の主人公はgohanです
```

**▼辞書に要素を追加する**

```
comics = {'dragonball':'gokuu','onepiece':'luffy','slamdunk':'hanamichi'}
comics['naruto'] = 'naruto'
print(comics)
　→{'dragonball':'gokuu','onepiece':'luffy','slamdunk':'hanamichi','naruto':'naruto'}
```

※リストではappendを用いたが、辞書では用いない

**▼辞書の要素を全て取得する**
※リストと同様にfor文を使う

```
構文
for 変数名 in 辞書:

処理の流れ
comics = {'dragonball':'gokuu','onepiece':'luffy','slamdunk':'hanamichi'}

for comic_key in comics:
    print(comic_key + 'の主人公は' +comics[comic_key] + 'です')

①変数comic_keyにdragonballが代入
②for文の中身【print(comic_key + 'の主人公は' +comics[comic_key] + 'です')】が実行される【dragonballの主人公はgokuuです】
③変数comic_keyにonepieceが代入
④for文の中身【print(comic_key + 'の主人公は' +comics[comic_key] + 'です')】が実行される【onepieceの主人公はluffyです】
⑤変数comic_keyにslamdunkが代入
⑥for文の中身【print(comic_key + 'の主人公は' +comics[comic_key] + 'です')】が実行される【slamdumkの主人公はhanamichiです】
```
HiLetgo ESP32 ESP-32S NodeMCU開発ボード2.4GHz WiFi + Bluetoothデュアルモード

「[HiLetgo ESP32 ESP-32S NodeMCU開発ボード2.4GHz WiFi + Bluetoothデュアルモード](https://www.amazon.co.jp/gp/product/B0718T232Z/)」を入手した。WifiとBluetoothが使えて、Arduino IDEでプログラムが書けるということでまずは使ってみた。
#Arduinoのインストール  
  1. [Arduino インストーラをダウンロード](https://www.arduino.cc/en/software)する。
  2. Arduinoを起動して、**Preferences**ウィンドウを開く。
  3. 追加のボードマネージャのURLに次のアドレスを追加する。
    `https://dl.espressif.com/dl/package_esp32_index.json`
  3. **ツール** > **ボード** > **ボードマネージャ** を選択する。
  4. `esp32`を検索し、最新のバージョンをインストールする。  

#シリアルドライバのインストール
このボードで使われているチップは CP2102。添付文書で確認できるのと、マイクロUSBコネクタの近くの黒いチップに細か〜い字でCP2102と書かれている。ドライバは、[Silabs CP210x VCP](https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers) をインストールする。

#LEDを点滅させるプログラムの作成
  3. Arduino IDEを起動して、**ファイル** > **新規ファイル** で新しいスケッチを作成する。
  4. 次のコードをペーストする(ボードの添付文書に記載があるが、2番ポートにLEDは接続されている)。

```
// Blinking LED

// 2番ピンを指定
const int LED = 2;

void setup() {
  // ピンモードを出力に設定
  pinMode(LED, OUTPUT);
}

void loop() {
  // 点滅は、点灯を1秒、消灯を2秒の繰り返し
  digitalWrite(LED, HIGH);
  delay(1000);
  digitalWrite(LED, LOW);
  delay(2000);
}
```
  - **ファイル** > **保存** を選んで、名前を入力して Save する。

#ESP32に書き込む
自動では書き込みができなかったため、手動でボード上のボタンを操作しながらスケッチをESP32に書き込む。

1. ボードをArduino IDEを起動しているコンピュータのUSBポートに接続する。
2. **ツール** > **ボード** から `NodeMCU-32S`を選択する。
3. **ツール** > **シリアルポート**から、ボードを接続したシリアルポートを選択する。 
4. ID0を押し続けながら、ENを押して放し**マイコンボードに書き込む**をクリックする。
4. 転送が始まったら、ID0から手を放す。
4. 書き込みが成功したら、`Hard resetting via RTS pin...` と出る。

#青色LED点滅
この段階で、青色LEDが点滅しているはず。

#参考
- [Building an AWS IoT Core device using AWS Serverless and an ESP32](https://aws.amazon.com/jp/blogs/compute/building-an-aws-iot-core-device-using-aws-serverless-and-an-esp32/)
- [ESP32-WROOM-32 - 手動書き込み](https://ht-deko.com/arduino/esp-wroom-32.html#04_02)
【備忘録】Rails6でrails db:create実行時にPostgreSQLに接続できなかった時の対処の話

#なんの話か
Rails6を使って新しいアプリ開発をやろうとして、ローカルのPostgreSQLを使おうとしたら発生したエラーの解決までの話しです。正直ポスグレを今回初めて使用したため、エラーに対して根本的な原因や、仕様などを理解しながら解決できるのかわかりませんでした。エラー文を読みながら進めていくと、なんとなくどういう状態に陥っているのかがわかるエラーでしたが、どうしてもつまづいてしまった部分をSEの知り合いに助けていただきました。

#起こったこと
`rails _6.0.3.2_ new memo-app --database=postgresql`コマンドで、DBにポスグレを指定してアプリ作成し、
`rails db:create`でデータベースを作ろうとしたとき、下記のエラーに遭遇。


```terminal
$ rails db:create
could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/tmp/.s.PGSQL.5432"?
Couldn't create 'APP_NAME_development' database. Please check your configuration.
rails aborted!
PG::ConnectionBad: could not connect to server: No such file or directory
        Is the server running locally and accepting
        connections on Unix domain socket "/tmp/.s.PGSQL.5432"?
***
```

要約すると、postgreSQLに接続できず、DBの作成に失敗しています。

#やったこと① postgreSQLの再起動してstatusnの確認

```
$ brew services restart postgresql
Stopping `postgresql`... (might take a while)
==> Successfully stopped `postgresql` (label: homebrew.mxcl.postgresql)
==> Successfully started `postgresql` (label: homebrew.mxcl.postgresql)
```

を実行して、postgreSQLを再起動をかけて、


```
$ brew services list
```
で状態を確認したが、

```terminal
Name       Status  User  Plist
postgresql error   yoshi /Users/yoshi/Library/LaunchAgents/homebrew.mxcl.postgresql.plist
```

statusがエラーのまま。もちろんこのまま実行しても、最初のエラーが再び吐き出されるだけ。
local環境にはpostgreSQLがあって起動できるけど接続できる状況にないことがここでわかります。

#やったこと②
ここでつまづいてどうしようもなかったので、SEの先輩に一言アドバイスをもらいました。
データディレクトリがどういう状態か確認してみよ、とアドバイスをいただき、

```
$ postgres -D /usr/local/var/postgres
```

で確認したところ、

```
FATAL:  database files are incompatible with server
***
DETAIL:  The data directory was initialized by PostgreSQL version 12, 
which is not compatible with this version 13.1.

```
という文章がでてくれました。DBファイルの互換性に問題があります。
Ver12で初期化された現在のデータディレクトリは、Ver13.1では互換性がないので使えません。
ということです。

#最終的には
そうなると、アップデートすればいいのでは？という仮定に至り、

`brew postgresql-upgrade-database`でアップデートをかけて、
`brew services restart postgresql`で再起動した上で、

再度DB作成してみたところ、

```
$ rails db:create
Created database 'memo_app_development'
Created database 'memo_app_test'
```
無事DBの作成に成功したので、解決しました。
根本的な原因は、いつのまにかVer12で作成されたDBに問題があったところです。
おそらく何が何だかわからず変なコマンド打ったりしていたのでこうなったのでは、と...。

#参考文献
https://qiita.com/sf213471118/items/0e843af332ea0210c810
iosにおけるDash.jsが動かない問題についてのメモ

HTML5環境上でDash.jsがiosの各種ブラウザーで動かない現象があって調べたのでメモとして残す
# 概要
Dash.jsのios対応は公式としてはどう扱っているのか確認したかったので確認した
https://github.com/Dash-Industry-Forum/dash.js/issues/3303

>Hi, iOS does not support the MediaSourceExtensions which are required to use dash.js. On iOS devices you have to use HLS.


`MediaSourceExtensions` (通称MSE）なるものがiosでは対応できてないらしいからHLSを使ってくれてとのことらしい

# そもそもMSEとはなんなのか
https://en.wikipedia.org/wiki/Media_Source_Extensionshttps://en.wikipedia.org/wiki/Media_Source_Extensions

なるほど、W3C側で規格制定しているメディアスストリーミング規格らしい

https://caniuse.com/mediasource
これをみた感じ記事投稿時点ではなぜかios(ipad os)13以降でのみのサポートらしい、ようはiphoneではサポートしてないということになりそう

# 背景
そもそもAppleはMpegDashではなくてHLSを推してるところがありあまりMpegDashとApple製品の関連の話においていい話をあまり聞かない

# 対応策
そもそもMpegDashは一つのストリーミング配信方式であって目的ではないのでios相手の実装はHLSで配信するようにする

# 感想
なんかいかがでしたかブログみたいな記事で恐縮だけどあまり話題にもなってないのでもし同様の問題を引っかかってる同志の問題解決に貢献できたらうれしい
【Python】セルのnumber_format属性を使ってセルの表示形式を変更する。

pythonを使用してExcelファイルの操作を勉強しています。
本日の気づき(復習)は、セルの表示形式に関してです。
pythonでExcelを操作するため、openpyxlというパッケージを使用しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927441/769a2764-9789-ef12-dad1-ce941189d53f.png)

上記のようなブック「商品リストを」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927441/5c5e8bce-f924-d4ab-adf9-21bf0345c7b6.png)

この様な表に変更したいです。

# number_format属性

```python
cell.number_format = 'セルの表示形式'
```

number_format属性には表示したい形式を代入します。
代入する値はExcelの画面上で設定する時と同じようです。

- yyyy"年"mm"月"dd"日"：年月日を表示
- hh"時"mm"分"ss"秒"：時間を表示
- 0.00：小数点以下二桁で表示
- #,##0：三桁のカンマ区切り
- 0%：パーセント表示

代表的なものはこれくらいでしょうか。

# 最終的なコード

今回はセルの横幅も変更しています。

```python
from openpyxl import load_workbook

wb = load_workbook('商品リスト.xlsx')
ws = wb.active

column_width = {'B': 3.5, 'C': 15, 'D': 20, 'E': 10, 'F': 10, 'G': 10}

for col, width in column_width.items():
    ws.column_dimensions[col].width = width

for row in ws.iter_rows(min_col=3, max_col=7, min_row=3):
    row[0].number_format = 'yyyy-mm-dd'
    row[2].number_format = '#,##0'
    row[3].number_format = '#,##0'
    row[4].number_format = '0.0%'

wb.save('商品リスト_表示形式変更.xlsx')
```

入荷日の列幅を変更しないとちゃんと表示してくれなかったので
ついでに他の列幅も変更しちゃいました。

表示形式を変えるときは注意しないといけないですね。
MySQLユーザー作成〜権限付与まで

# はじめに

MySQLインストール後、ユーザー作成から権限付与までをよく忘れるので記載します。

# 実行環境
OS:CentOS7
MySQLバージョン:5.6・5.7

#ユーザー作成
`mysql -u root`でrootでMySQLコマンドラインに入る。
※パスワード設定済みであれば「-p」オプションを追加。

```MySQL:
mysql> CREATE USER 'username'@'hostname' IDENTIFIED BY 'password';
```
`username`や`hostname`は、必ずしも引用符で囲む必要はないよう。
ただ、特殊文字やワイルドカード文字 (「%」など) を含む場合には引用符が必要なため、`password`含めて3点セットで囲んでおくのが無難な気はします。

# 権限付与
作成したユーザーに権限を付与する。付与する権限とそのレベルによって構文が変わります。

1.グローバル権限

特定のサーバー上の全てのデータベースに適用される。`GRANT ○○ ON *.* TO`構文を利用。
サンプルの`ALL`、`SELECT`や`INSERT`の部分は付与したい権限に応じて変更します。

```Mysql:サンプル
mysql> GRANT ALL ON *.* TO 'username'@'hostname';
mysql> GRANT SELECT, INSERT ON *.* TO 'username'@'hostname';
```

2.データベース権限

特定のデータベース内のすべてのオブジェクトに適用。`GRANT ○○ ON db_name.* TO`構文を利用。

```Mysql:サンプル
mysql> GRANT ALL ON db_name.* TO 'username'@'hostname';
mysql> GRANT SELECT, INSERT ON db_name.* TO 'username'@'hostname';
```

3.テーブル権限

DB内の特定のテーブルのカラム全てに適用。`GRANT ○○ ON db_name.tbl_name TO`構文を利用。

```Mysql:サンプル
mysql> GRANT ALL ON db_name.tbl_name TO 'username'@'hostname';
mysql> GRANT SELECT, INSERT ON db_name.tbl_name TO 'username'@'hostname';
```

4.カラム権限
特定テーブル内の単一カラムに適用。各権限のあとに、括弧で囲まれた1つ以上のカラムを指定する。
構文は`GRANT ○○ (col1), ○○ (col1, col2) ON db_name.tbl_name TO`のようになる。

```Mysql:サンプル
mysql> GRANT SELECT (col1), INSERT (col1,col2) ON db_name.tbl_name TO 'username'@'hostname';
```


# 権限付与結果の確認
以下で登録されたユーザーおよび権限設定を確認する

```mysql:ユーザー確認
mysql> select user, host from mysql.user;
```

```mysql:権限確認
mysql> show grants for 'username'@'hostname'
```

# 最後に
いまいちな理解の部分もあったのですが公式を参照したらちゃんと書いてありました。
やはり公式をしっかりと見るのが大事なようです。

# 参考ページ
https://dev.mysql.com/doc/refman/5.6/ja/grant.html#grant-global-privileges
https://qiita.com/shuntaro_tamura/items/2fb114b8c5d1384648aa
初めてのPython学習-基礎①-

#はじめに
Progateを用いて、初めてPythonの学習をしてみました。
Progateやり終えたあとのゴールは店舗売上の要因分析自動的に行うオリジナルアプリの開発です。


#この記事でアウトプットする内容
Progate Python1

#目次
1.データ出力の仕方
2.変数
3.条件分岐
4.入力を受け取る


##データ出力の仕方
###**▼データ出力とは**
コンソールにコーディングした内容で画面に表示させたい情報を表示させる

###**▼方法**

```py
print("文字列")
```

""で囲むと文字列として判別
""がなければ数値（演算ができる）

###**▼演算子**
`+` 足し算
`-` 割り算
`*` 掛け算
`/` 割り算
`//` 割り算の商
`%` 割り算のあまり
`**` べき乗

計算ができる

#変数
###**▼変数**
データを入れておく箱

###**▼変数名**
箱に付ける名前

###**▼変数の定義**
変数名 = 値
※「=」は、等しいではなく「右辺を左辺に代入する」という意味

```py
name = 'john'
print(name)・・・johnが出力
print('name')・・・「name」という文字列が出力`
```


###**▼変数名を決める際のルール**
◎英単語を用いる
◎2語以上の場合はアンダーバーで区切る

×変数名が数字開始
×ローマ字
×日本語

###**▼変数の値の更新**
変数名に新しい値を定義することで変数の値を上書きできる（出力結果を変えられる）

省略した書き方（予め定義した変数を後で変更する場合）
`x = x + 10`  ->   `x += 10`
`x = x - 10`  ->   `x -= 10`
`x = x * 10`  ->   `x *= 10`
`x = x / 10`  ->   `x /= 10`
`x = x % 10`  ->   `x %= 10`


###**▼文字列の連携**
演算子は文字列の連携を行うことも出来るが

```py
my_name = 'john'
print('My name is' + my_name + '.')
```

データ型の異なる文字列型と数値方との連結はできない

```py
age = 25
print('私の年齢は' + age + '歳です。')　→異なる型同士のため連結不可
```

型変換をする必要がある

```py
age = 25
print('私の年齢は' + str(age) + '歳です。')　→文字列に変換したため、文字列型同士で同じとなり連結が可能となる
```

###**▼型の変換**

```
数値型→文字列型：str(変数名)
文字列型→数値型：int(変数名)
```


#条件分岐
**想定する結果によって動かすプログラム（処理）を変えることが出来る**

if
「もし〜〜ならば◯◯を行う」

elif
「if文が成り立たなかった場合■■を行う」

else
「ifにもelifにも当てはまらない場合は××を行う」


```py:（例）
score = 70
if score == 100: <-条件式が成り立つ
    print('よくできました！')  -> インデントを下げないと判別されない

elif score >= 60:
    print('まずまずです')

else:
    print('頑張りましょう')
```


->この場合「まずまずです」が出力される。
**※条件式が成立したら、それ以降の処理は実行されない**


###**▼比較演算子**
`x == y`  →左右の値が等しいとき成り立つ
`x != y`　→左右の値が等しくないとき成り立つ
`x < y`　→右辺のほうが大きい場合に成り立つ
`x <= y`　→右辺が左辺以上の場合に成り立つ
`x > y`　→左辺のほうが大きい場合に成り立つ
`y >= y`　→右辺が左辺以下の場合に成り立つ

###**▼真偽値**
`True` →条件式が成り立つとき
`False` →条件式が成り立たないとき

###**▼条件式の組み合わせ**
「条件1も条件2も成り立つ」のような場合は「and」を用いる
※全ての条件式がTrueの場合に全体がTrueとなる
※比較を連結すればandを記載せずとも書くことができる

```py:（例）
if time > 10 and time < 18:
if 10 < time < 18:
```


「条件1もしくは条件2が成り立つ」のような場合は「**or**」を用いる
※複数の条件式のうち1つでもTrueの場合に全体がTrueとなる

「条件の否定をしたい」場合は「**not**」を用いる
※条件式が「True」であれば全体が「False」に。「False」であれば「True」となる

組み合わせると・・・

```py
book = 10
book_price = 2000
total_price = book * book_price

if book == 10
    print("本を" + str(book) + "冊買いました")
    print("価格は" + str(total_price) + "円です")
```

#入力を受け取る
**input**

inputを用いるとコードを実行した際にコンソールに文字を入力出来るようになる。

```py
変数名 = input('コンソールに表示させる文字列')
```

※inputで受け取った段階では文字列型で受け取っている→数値として扱いたいなら、型変換を用いてint型に変更する

```py:（例）
unit_price = 10000
input_count = input('客数を入れてください：')
client = int(input_count)

total_price = unit_price * client

print('今日の売上は' + total_price + '円です')
```
【5分でざっくり理解！】Laravelでクリーンアーキテクチャ超入門


バックエンド初心者のための、クリーンアーキテクチャ**超超超ざっくり解説**です。

フロントエンドエンジニア歴1年半＆バックエンド実務未経験の僕が、2021年4月からバックエンドとして働くにあたり勉強した備忘録です。

この記事の目的は、**「クリーンアーキテクチャ、ナンモワカラン」**なレベルの人に対して、**「自分でもできるかもしれない！チャレンジしてみたい！」**と一歩踏み出せる勇気を与えることです。

# クリーンアーキテクチャとは

乱暴ですが、一言でまとめます。

クリーンアーキテクチャとは

**「修正コストの低く・保守性の高い開発をすることを目的とし、役割と責任を適切に分けるための設計思想の一つ**」です。

（クリーンアーキテクチャ = 設計における最適解！という訳ではないです。）


# かつての僕
最近でこそモデルにメソッドを書くという技を覚えましたが、それまでは MVC の「C」に全てをぶちこんでいました（それ以外のやり方があるなんて知らなかった）。

いわゆる 「**fat controller（ファットコントーラー）**」

## どれくらいfatだったのか？

1つのアクションが150行、そんなアクションが3つも4つも存在する巨大なコントローラです。書いた自分ですら見るのが嫌になるほどのファットっぷり。

# 副業で、クリーンアーキテクチャな設計に出会う

そんな中、2020年末頃から参画したRailsプロジェクトで、クリーンアーキテクチャな設計で書かれたコードと出会います。たしかに、僕の知ってるモデル・コントローラー以外のヤツがいっぱい登場してきます（サービス、 デコレーターなど）。

この頃から、**僕もクリーンアーキテクチャ理解した先にある向こう側の景色を見たい！**と強く思うようになりました（ミーハー精神炸裂）。

# ついにあの本を手に取る

早速、『Clean Architecture 達人に学ぶソフトウェアの構造と設計』をポチる。「これさえ手に入ればおれもそっち側の住人だぜ！」という期待に胸を膨らませていました。

## 読んだケド、、、
、、、うん！ナンモワカラン！（**終始、「何言うてますのん？」状態**）

# 解説記事を漁る

「クリーンアーキテクチャ　解説」　で検索。

いくつか記事を読むと、だいたいこれ↓↓が出てきます。

![スクリーンショット 2021-03-28 19.35.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/d86f3efd-77b2-4193-3412-4118a570d3e9.png)

そして、「この図を見ながら、その通りに当てはめて実装していきましょう」と結論付ける記事が多い。「**いやこれ見て分かったら苦労せんわ！！！**」と何度も心で叫びます。

# 教えて偉い人！

とりあえず自力で理解することは早々に諦め、クリーンアーキテクチャをLaravelに適用するにはどうすればいいのか偉い人に聞いてみた。

簡易的に導入するなら、まずはこんな感じ？と教えていただいたのがコントローラーとモデルをさらに分解して以下の４つに分ける方法。

- Controller（ルーティング）
- UseCase（ビジネスロジック）
- Repository（モデル操作）
- Model（DBとのやりとり）

おおーーー！！分かった気がする、、、、！？

いやちょっと待って、、、

**ビジネスロジック**ってなに・・・？

# ビジネスロジックとは

https://qiita.com/os1ma/items/25725edfe3c2af93d735

[こちらの神記事](https://qiita.com/os1ma/items/25725edfe3c2af93d735)によれば、


>**アプリケーションをプレゼンテーション・ビジネスロジック・データアクセスの 3 つに分けたとき、「プレゼンテーションでもデータアクセスでもない部分がビジネスロジック」**
>
>と考えるととっかかりやすいです。

とのことです。個人的にはこの説明がすごく理解の手助けになりました。

![スクリーンショット 2021-03-28 20.03.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407378/60965cb0-6e73-9d20-c18e-a2712e574333.png)

## プレゼンテーション層
アプリケーションとユーザとのやりとりを担当する。（**ビューやコントローラ**）

## データアクセス層
ファイルや DB に対してデータを読み書きする。（**モデル**）

## ビジネスロジック層

プレゼンテーション層・データアクセス層「**以外**」


なんか分かってきた気がするぞ、、、、！！！

# 実際の機能で考えてみる

最後に、実際の機能で

- Controller（ルーティング）
- UseCase（ビジネスロジック）
- Repository（モデル操作）
- Model（DBとのやりとり）

に分けて考えてみます。

## いいね機能

ユーザーが何らかのコンテンツを投稿し、それを別のユーザーが閲覧できる（Twitterみたいな）サービスにおける、一般的ないいね機能で考えてみます。


### Controller（ルーティング）
ハートアイコンをクリックし、エンドポイント（例：　/products/1/like）を叩く

### UseCase（ビジネスロジック）
一人1日いいねは10件まで、自分のproductにはいいねできない　などのロジックを書く


### Repository（モデル操作）
バリデーションを行い、「Modelでlikesテーブルにレコード追加する処理」を呼ぶ

### Model（DBとのやりとり）
likesテーブルにレコード追加



# まとめ
バックエンドの現場でコードを書いたことのない人間がクリーンアーキテクチャを理解しようとしたこと自体そもそも無理があったかもしれませんし、結果としてほとんどクリーンアーキテクチャの核心的な部分には触れることができていません。

ただ、僕がそうだったように初心者はこの記事のレベルで行き詰まってしまうことになるでしょう。そんな方々の手助けになれば幸いです。

**正解はないです。少しずつでもよいので、使いながら理解を深めていきましょう！！！**
Railsにおけるcancancan権限設定方法と使い方

Railsのポートフォリを作成する時にcancancanというgemを使いましたが、参考になる記事が少なかったので備忘録をかねて記録を残します。

##初期設定(Gemのインストールとモデル作成)

```Ruby:初期設定(Gemのインストールとモデル作成)
gem 'cancancan' #Gemfileに設定する。

bundle install  #Gemをインストールする。

rails g cancan:ability #ターミナルに打ち込んでモデルを作成する。
```
すると
app/modelsability.rbが作成されます。

##権限の定義方法

本家のサイトにサンプル例が載っています。

https://github.com/CanCanCommunity/cancancan/blob/develop/docs/Defining-Abilities.md


権限は下記の5つがあり、主にCRUD関係の権限です。

```Ruby:基本の権限の理解

read: フロントのビュー画面表示

create: データの作成

uodate: データの更新

destroy: データの削除

manage: 全ての権限

```


```Ruby:書き方のイメージ

can 権限, モデル名

#または

cannnot 権限, モデル名

```

でモデルに権限を与えていきます。詳しくは下記を参考にしてください。


```Ruby:権限の定義の方法サンプル例

class Ability
  include CanCan::Ability
  def initialize(user)
    can :read, :all # ログインしていないユーザーも含め全てのユーザーへの権限を与える    
    if user.present?  # ログインしているユーザーに対して権限を与えるための条件分岐
      can :manage, Post, user_id: user.id 
      if user.admin?  # adminに対して権限を与えるための条件分岐
        can :manage, :all
      end
    end
  end
end
```

***できる権利***
***できない権利***
それぞれを上に重ねていって権限を与えていくイメージです。
**下の階層に行けば行くほど、多くの権限が与えられる書き方になります。**
となると、adminを全ての権限を持つので1番下にくる流れになることがわかります。
基本は、canで権限を与える。
**managae**権限や全ての権限対象**all**で与えすぎてしまって分はcannnotでマイナスするイメージです。
最後に適応された権限が有効になるようです。
【GAS】年間行事予定表を1年間分まとめてカレンダーに書き込む

学校の年間行事予定表を１年間分まとめてカレンダーへ書き込むGASです。

Googleが指定するcsv形式で予め作成した予定一覧をカレンダーのインポート機能を使って読み込めば、カレンダーに予定をまとめて書き込むことはできるのですが、それも億劫になってきたので今回GASで一気に書き込めるようにしました。

ごく初歩的な機能だけで十分なため、GASプログラミング未経験者にも理解しやすいコードだと思います。

## 特徴

- １年間分まとめてカレンダーへ書き込める。
- 特定の日に複数の行事がある場合には行事1と行事2に分けて入力することにより、同日最大2つまで行事を書き込むことができる。
- 行事は全て全日扱いで書き込む。
- 各学校毎に行事予定表のレイアウトが違っても、ダウンロードしたシートに適切にマッピングさせれば幅広く対応可能。

## 簡単な使い方
1. [年間行事予定表toカレンダー（公開用）](https://bit.ly/2PcR8e6)をクリックし、コピーをドライブ上へ作成
2. シートの中で変更して良いのは背景が灰色の部分（年度、行事）のみ。行事の入力が済んだら、メニューバーの「ツール」->「スクリプトエディタ」を開く。※ ここで別シートに各学校毎のカレンダーを貼り付けておき、対応するデータをマッピングさせておけば毎年の更新作業が楽です。
3. 13行目のカレンダーIDを年間行事予定表を書き込みたいカレンダーのカレンダーIDへ変更して保存。カレンダーIDの場所は例えば、[カレンダーの種類やアドレスを確認する方法](https://support.gluegent.com/hc/ja/articles/206261502-カレンダーの種類やアドレスを確認する方法について教えてください-)
を参照のこと。

4. 実行ボタンを押すと、GASが各種アプリケーションに対する追加の権限を求めてくるので承認する。左下の「詳細を表示」を押すと、以下の画面になるので「YearScheduleForStudents(public)（安全ではないページ）に移動」をクリックして先に進めば良い。

![承認画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558740/680bc2be-a1ff-7ddf-8ccc-063c5796bdd4.png)
5. スプレッドシートに戻り、新たに出来たメニューバーの「toCalendar」->「カレンダーへ出力」を実行。
6. 確認ウィンドウが表示されるので問題なければ「OK」で指定したカレンダーへ行事予定表が書き込まれる。
7. 実行中にエラーが発生した場合、ウィンドウにエラーの詳細が表示され実行が止まる。

```javascript:コード.gs
function onOpen() {
 const ui = SpreadsheetApp.getUi();
 const menu = ui.createMenu("toCalendar");
 menu.addItem("カレンダーへ出力", "myFunction");
 menu.addToUi();
}

function myFunction() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('年間行事予定表');
  const range = sheet.getDataRange().getValues();

  const id = 'カレンダーID'
  const calendar = CalendarApp.getCalendarById(id);
  const days = [];

  // イベント作成時にアイドル時間を作らないとGASが止まる。アイドル時間の200に根拠はない
  const THROTTLE_SLEEP_TIME = 200;

  // 日付を作成
  for(let i = 1; i < 276; i++){
    days.push(`${range[0][0]}/${range[i][0]}/${range[i][1]}`);
  }
  for(let i = 276; i < 367; i++){
    days.push(`${parseInt(range[0][0])+1}/${range[i][0]}/${range[i][1]}`);
  }

  range.shift();
  let result = Browser.msgBox("行事予定表をカレンダーに書き込みます。\\n 【注意】 この操作は取り消せません！",Browser.Buttons.OK_CANCEL);
  if(result == "ok"){
    try{
      for(let i = 0; i < days.length; i++){
        if(range[i][2] !== ''){
          const date = new Date(days[i]);
          calendar.createAllDayEvent(range[i][2], date);
          Utilities.sleep(THROTTLE_SLEEP_TIME);
        }
        if(range[i][3] !== ''){
          const date = new Date(days[i]);
          calendar.createAllDayEvent(range[i][3], date);
          Utilities.sleep(THROTTLE_SLEEP_TIME);
        }
      }
    }catch(e){
      Browser.msgBox(e);
    }
  }
}
```
## 補足（THROTTLE_SLEEP_TIMEについて）

どうもGAS実行時に意図的にアイドル時間(THROTTLE_SLEEP_TIME)を作らないと1日あたりの割り当て（カレンダーイベント作成数：無料アカウント5000個、G Suite Basic以上10000個）以内であるにも関わらずエラーで止まってしまいます。勤務校の行事予定表はTHROTTLE_SLEEP_TIME=200(ミリ秒）で問題なく動きましたが、この数値はイベント数次第です。新たに作成するイベントが少なければ0ミリ秒でもエラーが起こらないこともあります。実行時に
>You have been creating or deleting too many calendars or calendar events in a short time. Please try again later.

あるいはこの日本語訳が表示されたら、スクリプトエディタで18行目のTHROTTLE_SLEEP_TIMEの値を調整してみて下さい。

これに関しては現状、スクリプト実行時間の6分制限を意識しながら調整するしかなさそうです。
Androidで"storage/emulated/0/"直下にアクセスしてファイルを保存

#はじめに
androidでファイルを保存したいとき、ユーザーがアクセス可能なルートディレクトリ（"storage/emulated/0/"）に保存すると何かと便利。
そこでファイルアクセス方法を調べてみた。

#現在は非推奨
以前（Android10以前）ではEnvironment.getExternalStorageDirectory()を使用して外部パブリックストレージへのパスを取得できた。
しかし現在では非推奨となり使用できない。

https://nagare-it.com/115/
https://nagare-it.com/121/
http://blog.lciel.jp/blog/2014/02/08/android-about-storage/

#推奨方法
1. Context#getExternalFilesDir(String)
1. MediaStore
1. Intent#ACTION_OPEN_DOCUMENT.