OmniAICreator/Qiita-1.07M huggingface dataset:
プロジェクト管理の基本(プロジェクトを始める人へ)

#はじめに
　私が、初めてプロジェクトを作った時は、プロジェクト管理を考えずに、失敗していまいました。
その時のような状況の前にこの方法があることを知ってほしいです。
なにか個人でアプリを作りたい！と考えた時に初めに、何を行いますか？
2008年時点で、ITプロジェクトの成功率は、31.1％です。
プロジェクトを続け、成功させようと思うとプロジェクト管理が必要です。
そして、自分の肝に命じるためにも`新しいプロジェクトを始める時のプロジェクト管理について`書きました。
優しい心で見ていただければ幸いです。

#プロジェクトとは
「有限性かつ独自性の業務」と言われています。
　・有限性：明確な開始時点と終結時点をもつ
　・独自性：他業務と類似性があっても基本的な部分でユニーク
つまり、`「どのようなシステム」を「いつまでに」「どのくらいの予算でつくるのか」`ということです。

「そんなこと、考えてる時間がもったいない！」
と思っている方もいらっしゃるかもしれませんが、これを考えないと後にも話しますが、プロジェクトが失敗してしまったり、プロジェクトが続かず、時間だけが流れていってしまいます。

#成功と失敗の定義を決める
　プロジェクトが成功できたかどうかは、`「当初予定していた品質・予算・納期（QCD）」を順守できたか`であります。
　アプリ開発でいうと、「品質（どこまでの機能をつけて、ユーザー何人を目指すのか）、予算（かかるお金や学習コストなど）、納期（いつまでにリリースするか）」を初めに、メンバーで話し合うことで、漫然とプロジェクトを進めることがなくなり、全体像が見えるようになります。

#プロジェクトマネジメントの技法を体系的にまとめたもの
　・`PMBOK(Project Management Body Of Knowledge)`：
　プロジェクトマネジメントに関するノウハウや手法を体系立ててまとめたものです。1987年にアメリカの非営利団体PMIが「A Guide to the Project Management Body of Knowledge」というガイドブックで発表してから徐々に知られるようになりました。[OBPMNeo](https://products.sint.co.jp/obpm/blog/serial-umeda01)

　プロジェクトマネジメントの10個の知識体系を気をつけることが大切です。
昔は、`QCD（制約条件）→品質・コスト・納期`ができていれば大丈夫と考えられていましたが、PMBOKでは次の10個を元に管理するべきであるとされています。


　1. 統合管理
　→作業プロセス間の調節

　2. ステークホルダ管理
　→作業範囲の明確化

　3. スコープ（作業範囲）管理
　→作業範囲の明確化

　4. タイム管理
　→作業スケジュールの管理

　5. コスト管理
　→予算とかお金の管理

　6. 品質管理
　→品質基準の適合チェック

　7. 人的資源管理
　→必要な要員の確保

　8. コミュニケーション管理
　→作業の伝達と共有

　9. リスク管理
　→潜在リスクの識別と管理

　10. 調達管理
　→外部への発注検計と実施

#PDCAサイクル
　`Plan(計画)→Do(実行)→Check(評価)→Act(改善)`
を繰り返し行うことで、継続的な業務の改善を促すことができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502903/f4509b4a-abe1-ec9f-75eb-52997c2dc56e.png" width="300px">
#しないことを決める
　「あの機能も作りたい、この機能もつけた方が便利かな」そう思うことは、多いと思います。

しかし、全て入れていたら「しないといけないこと（スコープ）」に、しなくても動くことが近づいてきて`成功できなくなってしまいます`。

例えば、初めは、おしゃれな動きをする機能がなくても、とりあえず使えるようにするのが、目的ならば「しない事リスト」に入れるということです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502903/b146784f-5099-3b7b-e024-0e5f43ffd3f0.png" width="300px">
#作業範囲を理解する
　・`WBS(Work Breakdown Structure)`を訳すと、
W(作業を)B(分割して)S（体系化する）となり、`作業を階層構造で現した図`です。

PMBOKではスコープ管理で使用されます。これをすることで、今プロジェクトに必要な作業を把握しやすくなることができます。

どこまで、分けるかと言うと`管理できる作業になるまで`分けます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502903/961dab5c-dcac-7aed-7beb-391e8ec796e4.png" width="300px">
WBSは、[miro](https://miro.com/?utm_source%3Dgoogle%26utm_medium%3Dcpc%26utm_campaign%3DS|GOO|BRN|WW|JA-JA|Brand|Exact%26utm_adgroup=%26utm_custom%3D10309935545%26utm_content%3D442346683419%26utm_term%3Dmiro%26matchtype=e%26device=c%26location=1009540&gclid=EAIaIQobChMIg8fg-_Xr7wIVe9xMAh2JMQe1EAAYASAAEgItIfD_BwE)で簡単に作ることができます。



#最後に
　いかがだったでしょうか。
ここで、気をつけないといけないのが、ここまでするのに時間がかかりすぎて、プロジェクトに進むことができないということです。
なので、ほどほどに時間を制限して、管理すると良いと思います。

プロジェクトを始める前に一度上記のプロジェクト管理をしてみてはいかがでしょうか。

学生IT団体：[watnow](http://www.watnow.jp/)
**キータカレンダー4月** 7日目ももでした。

#参考文献
TAC　基本情報技術者試験　試験対策テキスト３
令和02年 イメージ&クレバー方式でよくわかる 栢木先生の基本情報技術者教室
キタミ式イラストIT塾 基本情報技術者 令和02年 (情報処理技術者試験)
【Oracle Cloud Infrastructure】Email Delivery を使用してメールを送信する際に "Envelope From address <{From Address}> not authorized" が返ってきたときに確認する項目

表題の通り。
Email Delivery(OCI のマネージド SMTP サーバ）を使用してメールを送信する際に、`535 Authorization failed: Envelope From address <{From Address}> not authorized` が返ってくる時は以下を確認しよう。

1. の Approved Senders はすぐ気がつくが、2. のポリシについては忘れがちなのでメモ。

## 1. From Address が Approved Senders(承認済送信者) に追加されているか

まず、Email Delivery 経由でメールを送信するには、Approved Senders に送信したい From アドレスを追加する必要がある。
また、Suppression List に追加されていないかも併せて確認するとよい。

https://docs.oracle.com/ja-jp/iaas/Content/Email/Tasks/managingapprovedsenders.htm

https://docs.oracle.com/ja-jp/iaas/Content/Email/Tasks/managingsuppressionlist.htm

## 2. 使用している SMTP Credential を作成したユーザ/IAM に `use approved-senders` のポリシが適用されているか

加えて、SMTP 認証を行うための SMTP Credential を作成するユーザ/IAM に対して、`use approved-senders` のポリシを適用する必要がある。
これが設定されていないと、ユーザが承認済送信者の検証を行えないため、表題のエラーが発生しメールを送信できない。

https://docs.oracle.com/ja-jp/iaas/Content/Email/Reference/gettingstarted.htm

## おわりに

~~トラブルシューティングのドキュメントも用意されているが、`535 Authorization failed` のケースについて記載されていない。。
アップデートされるとうれしい。~~
久しぶりに確認したらアップデートされていて、`535 Authorization failed` のケースについても記載されていました🙏

https://docs.oracle.com/ja-jp/iaas/Content/Email/Concepts/troubleshooting.htm
shellscriptの演算

shellscriptで演算の勉強をしたのでまとめます。
#演算の書式
基本的には
$(( 演算 ))

$(expr 演算 )

###演算子
和 : +
差 : -
積 : ```$(( * )) か  $(expr ¥* ) (macは\*)```
商 : /
剰余 : %
exprは積の時だけ¥が必要になる。

```file.sh

echo $(( 1+1 ))
echo $(( 10 - 5 ))
echo $(expr 3 ¥* 2 )
echo $(expr 6/2 ) 
echo $(expr 9 % 2 )
```
shellscriptの演算は結構アバウトみたいで空白があってもしっかり計算してくれるっぽい。

#変数の扱い
*var1とvar2があるとして。

```$(( var1 + var2 ))```

もしくは

```$(expr $var1 + $var2 )```
変数も空白はあってもなくても読み込まれる。

#複雑な計算
文字列をパイプで渡すと計算してくれるbcコマンドを使う。
###bcコマンドインストール
```
yum install bc
```
インストールしたら、

```file.sh
echo '10.5 + 0.3' | bc
```
などとしてファイルを実行すると結果が帰ってくる。

```file.sh
echo 'scale=10;2/3' | bc -l 
```
と書くと小数点が10桁まで表示される。
詳しくはmanなどで確認したほうがいいかも。

間違いがありましたらご指摘ください。
Flutter setup memo (macOS)

# 環境

- M1 MacBook Pro
- macOS Big Sur 11.2.3
- Flutter 2.0.4
- Xcode 12.4
- Android Studio 4.1  

# 問題
- `flutter doctor -v` で Permission 系エラー  
sudo 付けるとイケるが  

```
Woah! You appear to be trying to run flutter as root.
```

うわー！ と言われる。

- doctor で CocoaPods 入れ直せと言われるが、  

```
CocoaPods installed but not working.
You appear to have CocoaPods installed but it is not working.
This can happen if the version of Ruby that CocoaPods was installed with is different from the one being used to invoke it.
This can usually be fixed by re-installing CocoaPods.
To re-install see https://guides.cocoapods.org/using/getting-started.html#installation for instructions.
 ```

pod の設定周りは問題なし、何度試しても同じ。

# 解決
[Why is Flutter only running as Superuser?
](https://stackoverflow.com/questions/51354351/why-is-flutter-only-running-as-superuser)で色々協議されているが、
私の場合、
`/Users/yourUser/.config`
と
`/Users/yourUser/.flutter`
を`chown`すると、上記2点の問題は解決した。

# おまけ
M1 Mac で Android Studio の エミュレータはまだダメっぽい。なんか面倒臭そう。。

    Unable to install Intel® HAXM
    Your CPU does not support VT-x.
    Unfortunately, your computer does not support hardware accelerated virtualization.
    Here are some of your options:
     1) Use a physical device for testing
     2) Develop on a Windows/OSX computer with an Intel processor that supports VT-x and NX
     3) Develop on a Linux computer that supports VT-x or SVM
     4) Use an Android Virtual Device based on an ARM system image
       (This is 10x slower than hardware accelerated virtualization
mentaを触ってみた所感

スキルを売りたい人、買いたい人をマッチングするサービス
ココナラなどの競合
強みはスコア、サブスク、返金制度と思った

##スコア
活動を元にランク付けされて、検索順位に影響する仕組み。LINEでお金を貸す時の与信と同じ考え方で良い
返信率、返信までの時間、契約数、評価が影響する( らしい。非公開なので不明)
これにより、MENTAプラットフォーム上での誠実さ、コミュニケーションを活性化させ、販売者(メンター)、購入者(メンティー)を増やす好循環を作っていくベースになるよい仕掛け

##サブスク
ココナラなどには無い
基本プロジェクトごとの単発利用が多い
メンターなので数年単位での契約を前提としている

##返金制度
販売者、購入者とも気に入らなかった場合にキャンセルができる仕組み
購入者が気に入らなかったために悪い評価をつけられるくらいなら返金して自身の高評価を維持しようという思想が生まれてくる
これにより評価の信頼性が増して、安心して利用できる仕組みになる
よくあるAmazonや楽天のサクラレビューは起こりにくい仕組みになっていると思われる

以上がMENTAの強みと感じた

##登録者について
メンターはベンチャー系の新しい言語を使ったエンジニアやデザイナーが多くを占めている(vue.js、他、、、など)
既に上位数%が2000〜3000のスコアを叩き出して、10〜20人程度のメンティーを抱えているはず
1人月1万円として10〜20万円程度、サロンのようなコミュニティを作れれば自身のかかわりは１日１〜２時間程度と見積もれば美味しい
しかし上位数%へは参入障壁が高く、募集を書いて釣り糸を垂らしておくだけでは購入者には表示されないため見向きもされないと思われる
売りを出すには何かエッジの効いた強みをアピールポイントとして訴えて、定期的に購入者にアプローチしていく必要がある
一方で上位者にも抱えれるメンティーの数は限られている(目が届くのは多くても20-30が限度ではないか)ため、今後は単価が上昇すると共に今までの中位層が中上位層への引き上げがなされるはず
しかし単価が上がったとしても後述のように購入者は社会人数年の20代が中心でありリターンが見合わなくなるため一定のところで打止めになるだろう
そう考えると客単価は1万〜2万円が頭打ちで、利用者数を増やしていく事がサービスプラットフォーマーの利益スケールにつながると考える
もし単価を上げるようなメンタープランを考えるとしたらメンター育成メンターのようなものだろう


##メンティーに目を向けてみる
募集をざっと見てみると、プログラミングやデザインを有料サービスで学習中で、分からないことが多いという方が多いようだ
基本的には単発相談が多い印象
最初は調べ方がわからない、進め方がわからないというのは自身の過去の経験からも納得出来るので、そういう方には有用だと感じた
さて、メンティーとしても登録してみたが自身の学びたい分野と関係するメンターがレコメンドされる
500円のクーポン付きでメンター募集もかけられる
しかしいざ契約しようとするとクレジットカードの登録が必要
ここのハードルが高い
継続課金の仕組みなのでクレジットカードが一番だが、ウェブマネーなども使えると障壁が下がるのではと思う
または返金制度をもっと打ち出すのも一案かと
ここはサブスクをベースとするための作者の拘りなのかもしれない(情報収集してないので推測です)

##とりあえず
素晴らしいサービスだと思うので1ヶ月程度使ってみようと思う
メンターとして複数のスキルを組み合わせでの独自性を持ったプラン＋メンティー募集への声かけで2人サブスク契約、コミュニティ構築が出来ないかトライしてみようと思う

いずれにしてもこのサイト一つでは副業でのお小遣い稼ぎと認識するのが良い
余りスケールするものでは無いと感じている
バッチファイルで拡張子「.zi_」「.zi」「.ex_」「.ex」をリネームする(PPAP)

Y子です。
[昨日のバッチファイル](https://qiita.com/koiwaiwaiko/items/4444a0929c8676a7d355 "バッチファイルで拡張子「.zi_」を「.zip」にリネームする(PPAP)")を改良してみました。
[__PPAP__](https://ja.wikipedia.org/wiki/PPAP_(%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3))の添付ファイルって、zipファイルなら「`.zi`」で来ることもありますし、自動解凍書庫の「`.ex_`」や「`.ex`」で来ることもありますもんね。これらも判別するようにしました。

# 概要・用途
以下の通り、拡張子をリネームするバッチファイルです。
　「`.zi_`」「`.zi`」　→　「`.zip`」
　「`.ex_`」「`.ex`」　→　「`.exe`」
拡張子をバッチファイルに関連付けしておくと、ファイルをダブルクリックするだけでリネームされます。とても便利です。
バッチファイルにドロップしても、同じ動作になります。

# コード
```bat:rename_ppap.bat
@echo off

rem 引数がない場合は終了する
if "%~1"=="" (
  echo 引数がありません
  pause
  exit /b
)

rem 変更前のファイル名(フルパス)「C:\work dir\test.xxx」
set str_oldfile=%~1

rem ファイル名分割…拡張子なし「test」と、拡張子のみ「.xxx」
set str_old_fn=%~n1
set str_old_ex=%~x1

rem 与えられたファイルの拡張子が「.zi_」「.zi」「.ex_」「.ex」なら
if "%str_old_ex%"==".zi_" (
  call :renamefile "%str_oldfile%" "%str_old_fn%.zip"
) else if "%str_old_ex%"==".zi" (
  call :renamefile "%str_oldfile%" "%str_old_fn%.zip"
) else if "%str_old_ex%"==".ex_" (
  call :renamefile "%str_oldfile%" "%str_old_fn%.exe"
) else if "%str_old_ex%"==".ex" (
  call :renamefile "%str_oldfile%" "%str_old_fn%.exe"

rem 想定外のファイルなら
) else (
  echo なにもしませんでした
)

pause
exit /b

rem ====サブルーチン====
rem ファイル名変更サブルーチン
:renamefile
  set str_old=%~1
  set str_new=%~2

  ren "%str_old%" "%str_new%"
  echo ファイル名を「%str_new%」に変更しました
exit /b
```

# 説明
バッチファイルの`if`文には`or`が使えないので、`else if`で同じ処理を複数書くことになります。
冗長になるので、リネーム部分はサブルーチン替わりのラベルを使いました。
サブルーチンの引数には、`ren`コマンドに与える「変更前」「変更後」のファイル名を渡しています。

# おわりに
サブルーチンを使うと、一気にプログラムっぽくなりますね！
ひとつのツールでいろいろなことができると、お得感があってうれしくなります。

ではまた！
権限セットとライセンス

#権限セットで付与できる権限
デフォルトでは

- Einstein Analytics Platform ユーザ
- Einstein Analytics Platform 管理者
- Einstein Analytics Plus システム管理者
- Einstein Analytics Plus ユーザ

Discoveryの違いです。

|権限の名前|説明|
|---|---|
|Analytics アプリケーションの作成|Analytics アプリケーションを作成します。|
|Analytics ダッシュボードの作成と編集|Analytics ダッシュボードを作成および編集します。|
|Analytics データのダウンロード|Einstein Analytics ユーザインターフェースからデータを表形式でダウンロードします。「登録メールの使用」権限が付与されている場合、ダウンロードしたテーブルデータを登録メールに添付できます。|
|Analytics データフローの編集|Analytics データフローを編集し、関連するエラーを表示します。|
|Analytics テンプレートアプリケーションの管理|使用可能なアプリケーションテンプレートに基づいて、アプリケーションを作成および管理します。|
|Analytics テンプレートアプリケーションの使用|使用可能なアプリケーションテンプレートに基づいて、アプリケーションを参照します。|
|Analytics のカスタム地図を管理|対応付けの追加、編集、削除や、geoJSON のアップロードを行います。|
|Analytics の管理|すべての Analytics 機能にアクセスします。|
|Analytics の使用|アクセス権のあるアプリケーションを実行したり、データセット、レンズ、ダッシュボードを表示したり、レンズを作成および編集したりします。|
|Analytics リモート接続を追加|外部データを複製するための接続を追加します。|
|Analytics 非公開アセットを管理|他のユーザの [私の非公開アプリケーション] で Analytics データセットを削除します。|
|Einstein Discovery ストーリーを共有|ユーザが Einstein Discovery ストーリーを Quip にエクスポートできるようにします。|
|Einstein Discovery ストーリーを作成、更新する|Einstein Discovery ストーリーを作成および更新します。|
|Einstein Discovery データセットを作成、更新する|Einstein Discovery データセットを作成および更新します (非推奨)。|
|Einstein Discovery のおすすめを表示|Einstein Discovery モデルからの予測とおすすめを表示できます。|
|Einstein Discovery を管理|すべての Einstein Discovery 機能にアクセスします。|
|Einstein Discovery を使用|Einstein Discovery を使用して、共有されているストーリーを表示します。|
|Einstein Discovery 予測をリリースおよび管理可能|Einstein Discovery の予測とモデルをリリースおよび管理します。|
|Einstein データインサイトを実行可能|レポートの分析を実行できます。|
|データセットからストーリーを作成する場合に述語を無視|述語でデータセットから Einstein Discovery ストーリーを作成する必要があります (非推奨)。|
|データセットレシピを編集|ソースデータセットを使用してデータセットレシピの作成と編集を行います。|
|外部データの Analytics へのアップロード|Analytics ユーザインターフェースから外部データファイルをアップロードし、データセットを作成します。|
|通知メール: 受信者を追加|Einstein Analytics ダッシュボードウィジェットからメール通知の受信者を指定します。すべての受信者のメールアドレスがメールの [宛先:] 項目に表示されるため、受信者はユーザ共有設定に関係なく互いのメールアドレスを表示できます。|
|登録メールを使用|Einstein Analytics レンズおよびダッシュボードウィジェットを登録すると、スケジュールされた状況の更新がメールで送信されます。テーブルを登録するには、「Analytics データのダウンロード」権限も必要です。組織のデータはサードパーティサービスにより保存または処理される場合があります。Salesforce は、ユーザが Salesforce の外部に送信することを選択したデータに関して責任を負いません。|

#ライセンスで付与できる権限

こんだけあります。ライセンスで選べる権限セットを選択して、権限セットを使用する

- Analytics Platform
- Event Monitoring Analytics Apps
- Field Service Dispatcher
- Field Service Mobile
- Field Service Scheduling
- Field Service Standard
- Identity Connect
- Orders Platform
- Sales Analytics Apps
- Sales Console User
- Salesforce CPQ AA License
- Salesforce CPQ License
- Service User
- Tableau CRM Plus

#アプリケーションレベルの共有

前提とししてアプリケーションのセキュリティなので、アプリケーション内で何ができるのかを設定している
だから、もちろん適切な権限セットがないと機能しない

|アクション|閲覧者|エディタ|マネージャ|
|---|---|---|---|
|アプリケーションでダッシュボード、レンズ、およびデータセットを表示する|●|●|●|
|アプリケーションへのアクセス権を持つユーザを表示する|●|●|●|
|アプリケーションのコンテンツを、ユーザがエディタまたはマネージャアクセス権を持つ別のアプリケーションに保存する|●|●|●|
|既存のダッシュボード、レンズ、データセットへの変更をアプリケーションに保存する ||●|●|
|アプリケーションの共有設定を変更する|||●|
|アプリケーションの名前を変更する|||●|
|アプリケーション内のアセットの表示を更新する||●|●|
|アプリケーションを削除する|||●|
HoloLensマルチユーザーチュートリアルをやりました

# はじめに
HoloLens2の[マルチユーザー機能のチュートリアル](https://docs.microsoft.com/ja-jp/windows/mixed-reality/develop/unity/tutorials/mr-learning-sharing-01
)をやりました。

実機がない環境だったのでAzure Spatial Anchorsは行っていません。

# 開発環境
- Windows 10 Enterprise
- Unity 2019.4.1f1
- MRTK 2.5.3
- PUN 2 Free 2.29

# 1章 はじめに
環境設定等を行います。
Azure Spatial Anchors は今回使用しないのでimportしませんでした。

# 2章 Photon Unity Networking の設定
ここではPhoton2を導入します。
チュートリアル通りに行うことで問題なく導入することができました。

# 3章 複数のユーザーの接続
この章では複数ユーザーの場所を同期します。
チュートリアル通りに行うことでユーザーの同期ができました。
## HierarchyViewの説明
###SharedPlayground
GenericNetworkManager.cs で ゲーム開始時にPhotonServerに接続する関数を呼び出しています。

### NetworkLobby
PhotonLobby.cs にコールバック用の関数が定義されており、
接続成功時にランダムなRoomに入室するように設定されています。

### NetworkRoom
PhotonRoom.cs ゲーム開始時にプレーヤーを表すGameObjectを作成し、入室時にそれを共有しています。

ドキュメント
https://doc-api.photonengine.com/ja-jp/pun/v2/index.html

# 4章 複数のユーザーとオブジェクトの移動を共有する
この章ではユーザーに加えて初級チュートリアルで作ったプロジェクトの共有を行います。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">HoloLensのマルチユーザーチュートリアルをやりました！<a href="https://twitter.com/hashtag/HoloLens2?src=hash&amp;ref_src=twsrc%5Etfw">#HoloLens2</a> <a href="https://twitter.com/hashtag/MRTK?src=hash&amp;ref_src=twsrc%5Etfw">#MRTK</a> <a href="https://t.co/sGog1MjloL">pic.twitter.com/sGog1MjloL</a></p>&mdash; 原口昂弥 (@k_haraguchi55) <a href="https://twitter.com/k_haraguchi55/status/1379740171013251075?ref_src=twsrc%5Etfw">April 7, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 5章 Azure Spatial Anchors の共有エクスペリエンスへの統合
保留
APIってなーに？？

#API
OSとアプリケーションをつなぐ部分になります。
アプリケーションはハードウェアに指示を出さなくてはなりません。そのため、OSに指示を出します。
OSは自分がもつ機能をアプリケーションから呼び出せる仕組みを用意しています。
APIは正式名所をAplication Program InterFaiceといいます。
Inter Faice　▶　コンピュータで、異なる機器・装置のあいだを接続して、交信や制御を可能にする装置やソフトウェア。(つなぐもの)といいます。

イマイチ納得できないので、ざっくりいうと、多分こんな感じですかね？？

OSじたいにはユーザーと接触する手段がないので、通り抜けループ（API）を使ってアプリケーションとコミュニケーションをとる感じです。
おお、なんとなく理解できそうですよね！！

ただ、このAPI簡単に使わせてくれないので、頼まないといけません。

使わせてくださいとリクエストを送り、いいですよ！もしくはいえ、今回はすみませんとレスポンスが返って来ます。
いいですよ！であれば晴れて通り抜けループを使うことができます。
ここに使用方法や内容などがあるので、しっかり守って行きます。
そしてユーザー登録をしてAPIキーを取得します。
指定されたURLにAPIキーを入力すると使うことができます。
流れは以上のようです。
AWS EC2 SSH接続後にやったことまとめ

やったこと

1. ソフトアップデート
sudo yum update

2. Apache インストール
sudo yum install httpd

3. Apache 起動
sudo systemctl start httpd

4. Apache 停止
sudo systemctl stop httpd.service




ソース
https://www.youtube.com/watch?v=xXuAX1ctg9E
SIer→WEB系キャリアチェンジのポートフォリオ作成①

※メモ書きのため後から整形予定

作るもの

・転職用ポートフォリオとして、AWS　DOCKER　CircleCIを使用したrailsアプリケーションを作成

作る人

・30代後半
・SES歴２年
・業務系案件でJavaとかC#とかちょいちょい触った程度
・CICD環境は1案件（数ヶ月）だけ経験
・railsは独学　rails-tutorial終えた程度
・AWSも独学　SAA-02なんとか取得程度

やること

AWSアカウントの再作成　無料枠大事
DOCKERを使用したCICD開発環境作成
アプリケーション作成・デプロイ
達人に学ぶSQL徹底指南書の演習問題(随時更新）

#概要
- 勉強のために演習問題で考えたコードを保存します。
- 動作確認は、Fiddle (http://sqlfiddle.com/ )でおこないました。
- 全ての問題はやりません。
- 元データはこちらからダウンロードして使いました。 (https://www.shoeisha.co.jp/book/download/9784798157825/detail)

# CASE式のススメ
- CASE式はどこにでもかける
- HAVINGで書くのではなくSELECT句で分岐させるのがプロ
- 縦持ちから横持ちにも可能

## 演習問題1-2
行もちから列持ちへの水平展開の練習

```sql:ensyu1-2
SELECT
SUM(population) AS 'ALL',
SUM(CASE pref_name  WHEN '徳島' THEN population ELSE 0 END) as '徳島',
SUM(CASE pref_name  WHEN '香川' THEN population ELSE 0 END) as '香川',
SUM(CASE pref_name  WHEN '愛媛' THEN population ELSE 0 END) as '愛媛',
SUM(CASE pref_name  WHEN '高知' THEN population ELSE 0 END) as '高知',
SUM(CASE WHEN pref_name  IN ('徳島' ,'香川','愛媛','高知' ) THEN population ELSE 0 END) as '四国'
FROM PopTbl2
GROUP BY sex;
```

# 自己結合の使い方
- INNER JOINの結合の条件を帰ることで組み合わせを色々と操作できる。

## 演習問題3-1 重複組合せ
- P44 のProductsテーブルを作る

```sql
CREATE TABLE Products
    (`name` varchar(3), `price` int)
;
    
INSERT INTO Products
    (`name`, `price`)
VALUES
    ('りんご', 50),
    ('みかん', 100),
    ('バナナ', 80)
;
```

```sql
SELECT P1.name as name_1, P2.name as name_2
FROM Products P1 INNER JOIN Products P2
ON P1.name >= P2.name;
```
Dockerで立てたMySQLのDBコンテナにローカルから接続する

単純なDockerのDB接続で少しハマったのでメモ。
「コンテナ内では正常にDBは使えるけど、ローカルのクライアントツールで接続できない」という事象の対処です。
## やろうとしていること
- Dockerを用いてLEMP（Linux+Nginx+MySQL+PHP）環境を構築(済)
- Laravel6.8を導入(済)
- DBクライアントツールTable PlusでローカルからDBに接続　←ここの話

|項目|バージョン等|
|:-|:-|
|OS|MacOS Big Sur 11.2.1|
|Docker|Docker Desktop 3.3.2|
|DBMS|MySQL 5.7|
|DBクライアント|TablePlus 3.12.6|

## 背景
Laravelをインストール後にmigrateやDBクライアントツールでの接続時に、

```
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
```
や

```
Mysql2::Error::ConnectionError: Unknown MySQL server host 'db' (0)
```
や

```
php_network_getaddresses: getaddrinfo failed: Name or service not known
```
などなど、いろいろとトラブルが起きて試行錯誤。どうすれば繋がるかだけメモ的に簡潔に記します。

## 準備と手順
### docker-compose.ymlの記述
Dockerfileは作成できている前提で、docker-compose.ymlのDBコンテナの部分の記述を確認していきます。

```dockerfile:docker-compose.yml
#docker-compose.ymlのバージョン
version: '3.8'

#docker volumeの設定
volumes:
  docker-volume:

#コンテナ(サービス)の設定
services:
  #Webコンテナ
  web:
    image: nginx:1.19
    ports:
      - '80:80'
    depends_on:
      - app
    volumes:
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
      - .:/var/www/html
  #アプリケーションコンテナ
  app:
    build: ./docker/php
    volumes:
      - .:/var/www/html
  #データベースコンテナ
  db:
    image: mysql:5.7
    ports:
      - '3333:3306'
    environment:
      MYSQL_DATABASE: test_db
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - docker-volume:/var/lib/mysql
```
ポイントなのは上記の`db:`の`ports`のところで、ローカルの`3333`ポートで接続(ポートフォワーディング)していること。MySQLのデフォルト`3306`が他のプロジェクトと競合したので、今回は適当なポート番号にしています。(別に違う値でも良い)
あとはMYSQL_USERは`root`は使えないのでそれ以外を指定することでしょうか。MYSQL_PASSWORDの方は`root`でOKです。

### .envファイルの設定
アプリケーション側の環境ファイルも併せて確認していきます。(今回はLaravelの場合)

```:.env
~前略~
DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=test_db
DB_USERNAME=user
DB_PASSWORD=password
~後略~
```
docker-compose.ymlの記述ときちんと合わせます。
DB_HOSTは`127.0.0.1`ではなくDBコンテナのサービス名の`db`とします。
<br>
そして.envファイルを変更したら、

```
$ docker-compose exec app bash
```
でコンテナに入り、

```
$ php artisan config:cache
```
で設定を取り込む。(Laravelの話ですが)

### TablePlusの設定
最後にdocker-compose.ymlや.envで設定した内容でローカル側の接続設定をします。
こちらのHostは`127.0.0.1`、Portはローカル側に設定した`3333`としています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/fe7ffdd7-3e00-914a-aba5-e5eeda724992.png" width=65%>
設定後、TablePlusの場合はこのようにフォームが緑色になっていれば"接続成功"です..！
【Vue.js】vue-click-outsideを使ってドロップダウンメニューを実装


#vue-dropdown-menuとは

vue-dropdown-menuは、ドロップダウンメニューを実装できるコンポーネントライブラリです。
マウスホーバーでメニューを展開したり、マウスを離したときに開閉するまでの時間を設定できたりなどが可能です。

#vue-dropdown-menuの導入方法

以下のnpm、yarn、CDNを使ってインストールします。

- npm
 
```
npm i @innologica/vue-dropdown-menu --save
```

- yarn

```
yarn add @innologica/vue-dropdown-menu
```

- CDN

```html:CDN
<script src="https://cdn.jsdelivr.net/npm/@innologica/vue-dropdown-menu@0.1.3/dist/vue-dropdown-menu.umd.min.js"></script>
```

#vue-dropdown-menuの導入手順

###1.ライブラリの取り込み

(1)webpack等の場合 [注意]モジュール版は未検証です。

```
import DropdownMenu from '@innologica/vue-dropdown-menu'
```

（2）WEBページの場合

```
const DropdownMenu = window['vue-dropdown-menu'].default;
```

###2.メソッドを設定

上記で取得したDropdownMenuを Vue.use に取り込みます。

ドロップダウンメニューのプロパティ設定をdataに持たせます。


```javascript
Vue.use(DropdownMenu);
 
new Vue({
  el: '#app',
  data: {
    show: false,
    right: false,
    hover: false,
    interactive: false
  }
});
```

###3.テンプレートを準備

**dropdown-menu**を設置します。

```vuejs:header.vue
<template>
  <header class="header">
    <router-link to="/" class="header-ttl">
      <span class="header-ttl-color">C</span>inemary</router-link
    >
    <ul class="header-menu">
      <li>
        <router-link to="/signup" class="header-link" v-if="!authenticatedUser"
          >新規登録</router-link
        >
      </li>
      <li>
        <router-link to="/signin" class="header-link" v-if="!authenticatedUser"
          >ログイン</router-link
        >
      </li>
      <li>
        <span
          class="dropdown-menu"
          v-click-outside="clickoutside"
          @click="toggle"
        >
          <a class="header-link"
            >マイページ<span class="dropdown-arrow"></span
          ></a>
          <ul class="dropdown" :class="{ isOpen }">
            <li class="dropdown-items">
              <router-link to="/mypage" class="dropdown-link"
                >マイページ</router-link
              >
            </li>
            <li class="dropdown-items">
              <router-link to="/profile" class="dropdown-link"
                >プロフィール編集</router-link
              >
            </li>
            <li class="dropdown-items" v-if="authenticatedUser">
              <button
                class="dropdown-link"
                @click="signOut"
                v-if="authenticatedUser"
              >
                ログアウト
              </button>
            </li>
          </ul>
        </span>
      </li>
    </ul>
  </header>
</template>
```
このままではどこをクリックしてもメニューが開閉してしまうかと思います。

###4.ドロップダウンメニュー外をクリックしたらメニューを閉じる

```javascript
clickoutside: function() {
      //this.isOpenがtrue場合、this.toggleは何も起きない。
      //要するにドロップダウンメニューが開かれている時は、クリックしても何も起きない。
      if (this.isOpen) {
        this.toggle();
      }
    },
  mounted() {
    this.popupItem = this.$el;
  },
  //directivesオプションを使用することにより、ローカルディレクティブに登録されるため、機能を使える
  directives: {
    ClickOutside,
  },
};
```

上記を追記することでメニュー外をクリックしたら閉じるようになったかと思います。
golang.org/x/tour/gotour has moved to golang.org/x/tourを解決する

# go tourをローカルで実行したい

## 環境
MacOS: 10.15.7

```
$ go version
go version go1.15.3 darwin/amd64
```


## tour of Goのサイト

https://go-tour-jp.appspot.com/list

## go tourのインストール
途中でgotourのインストールを促されるが、移動したと表示されてしまい、動作しない

```
$ go get github.com/atotto/go-tour-jp/gotour
$ gotour
golang.org/x/tour/gotour has moved to golang.org/x/tour
```

インストールしたものを削除して、移動したところからインストールする

```
$ go clean -i github.com/atotto/go-tour-jp/gotour
$ go get golang.org/x/tour
```

ローカルで起動してみる

```
$ tour
```

無事に動作した
自動テストもstagingでも動いたのにproductionでは動かなかったRailsのAPIの話


## 発生した障害
Railsのapiサーバーのstagingサーバーでもデプロイ時の自動テストもちゃんと動くのに、productionでは動かないという一番困るエラーがおきました。新たに追加したAPIだけが動かないのです！

## 環境差分が引き起こした問題のトラブルシューティング

今回のケースはローカルのテストもstagingのテストも、自動テストも通っているので環境差分でなにかまずい事が起きていることが明白です。

環境差分の場合、概ね3つのケースが考えられます。


1. 環境変数やその他、環境に注入している環境依存の値がおかしい
1. DBのmigrationがproductionとstagingとlocalで一致していない、テーブルの設定に差異がある
1. 間に挟まっているミドルウェア（LB,nginx,redis...etc)


が多いです。

1つ目はそのままですね、環境毎に変えたい値を入れるための値が環境変数なので。（私は環境変数はグローバル変数と同じだと思っているので別で管理したい派ですが手軽なのでついつい増えがち）
今回はAPI、1つだけが動かず、環境変数も利用していないのでこいつには該当しません。

2つ目のDBに関しては、migrationに差分があるケースと、tableのdefaultのcharsetやcollationがlatinになっていて検索結果が一致しないなど。今回はそういう類のエラーではないのでこいつじゃない。

つまり
3つ目のミドルウェアに違いない。
とたどり着きました、というのは嘘で、既にRails側にログが出力されていことから薄々通信がどこかで止まっているな？と思っていました。なんならエラーの内容がRailsが出すエラーじゃなかったのでどいつだ？このエラーを出しているのは？！っとなっておりました。


吐いていたエラーがこちら。

```
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
	<title>503 first byte timeout</title>
</head>
<body>
	<h1>Error 503 first byte timeout</h1>
	<p>first byte timeout</p>
	<h3>Guru Mediation:</h3>
	<p>Details: cache-3xxxx2-TYO 1yyyy31 xxzzzz74</p>
	<hr>
	<p>Varnish cache server</p>
</body>
</html>
```
Varnish cache serverだと！

## 実は本番とstagingで違うミドルウェアが使われていた

cashe serverなんて使っていたかしら？うーん？と考えてみるとそういえば本番だけ

`fastly`

が入っていました。でも何でエラーを返しているんだろう？？timeout errorってことはなにか処理が重いのかな？と思い、APIの中の処理を本番環境で実行してみます。

すると本番だけ異常に遅いSQLが1つ見つかりました。indexが効いていないせいでおそおそSQLが。そのSQLが20秒近くかかっており、インフラ担当にfastlyのtimeoutの設定どのぐらいにしている？と聞くと15秒...こいつじゃん。


## 結局ミドルウェアじゃなかった
ミドルウェアはちゃんと仕様通りにエラーを出していただけで原因じゃなかった。原因は遅すぎるSQLでした。Index貼り直して解決。
オチをまとめると、ミドルウェアがRailsにログを出させないせいでSQLが遅すぎて死んでいるということに気づくのが遅れたということでした。たまにはリアルな障害対応を書いてみようと思い記しました。 #屍踏んでけ
SQLDWHでテーブル作成から統計情報更新まで

SQLDWHでテーブルを作成し、ハッシュ分散させる方法

## テーブルの作成

まずは下記コードを書く

```sql
CREATE TABLE TEST_SCHEME.TEST_TABLE
(
	ID VARCHAR(10) NOT NULL,
	FLG VARCHAR(1) NOT NULL
)
WITH
( 
	DISTRIBUTION=HASH(ID),
	CLUSTERED COLUMNSTORE INDEX
)
```

最初のカッコはテーブルのカラムを定義する。

順にカラム名、データ型、NULL制約（NULLを許可するか否か）

WITH句以降はSQLDWHにて分散処理するための構文。

HASHの中に分散キーを指定する。

## テーブルへデータを挿入する

続いて、作成したテーブルにデータを挿入する。

**①SELECT文を使った挿入**

```sql
INSERT INTO TEST_SCHEME.TEST_TABLE
SELECT
	COL_1,
	COL_2
FROM ...
```

**②値を入力する挿入**

```sql
INSERT INTO TEST_SCHEME.TEST_TABLE
--ここにカラム名を書くのが正式。全入力の場合は面倒なので省略。ただし順番は間違えないこと
(COL1_VAL, COL2_VAL, COL3_VAL, ...)
```

**③CSVで入力する方法**
コマンドプロンプトを起動し、下記を入力

```bash
bcp dbname.TEST_SCHEME.TEST_TABLE in test_data.csv -e error_test_data.log -S SERVER_NAME -q -U USER_NAME -P PASSWORD  -t , -c -C 932

/*
注意事項
・ファイルはカンマ区切り（タブ区切りの場合　-t ,　を削除）
・ファイルの文字コード：shift-jis　（UTF-8の場合　-c -C 6500　に変更）
・引用符なし（引用符ありで実行すると引用符も文字列として取り込まれます）
・ヘッダーなし
*/
```

## 統計情報の作成と更新＆カラム圧縮

テーブル作成後に統計情報を作成する

初めてテーブルを作った時は下記コードで統計情報を作成＆カラム圧縮

```sql
/* データ追加後テーブルの統計情報取得とカラム圧縮*/
--統計情報取得（初回：統計情報未取得の場合）
CREATE STATISTICS STAT_TEST_SCHEME_TEST_ID ON TEST_SCHEME.TEST_TABLE(ID)
CREATE STATISTICS STAT_TEST_SCHEME_TEST_FLG ON TEST_SCHEME.TEST_TABLE(FLG)

--カラム圧縮
ALTER INDEX ALL ON TEST_SCHEME.TEST_TABLE REBUILD
```

統計情報の更新は下記コードで

```sql
--統計情報取得（統計情報取得済の場合）
UPDATE STATISTICS TEST_SCHEME.TEST_TABLE 
```

## （参考）ハッシュ分散以外の分散方式

ハッシュ分散以外に下記の分散方式がある

ラウンドロビン分散

```sql
CREATE TABLE TEST_SCHEME.TEST_TABLE
(
	ID VARCHAR(10) NOT NULL,
	FLG VARCHAR(1) NOT NULL
)
WITH
( 
	DISTRIBUTION=ROUND_ROBIN,
)
```

分散方式に関しては下記参照

[https://blog.engineer-memo.com/2015/09/27/sql-data-warehouse-を使ってみる-その-3/](https://blog.engineer-memo.com/2015/09/27/sql-data-warehouse-%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B-%E3%81%9D%E3%81%AE-3/)
flask.request 自分用チートシート

# この記事は？

通常、Flask において受け取ったデータの取得には` request `を用います。
公式ドキュメントを見ても良いのですが、メソッドが多くて目的のものがすんなり見つからないことも多々。
そこで、自分が主に使用するメソッドをメモ。

公式ドキュメントはこちら。
[API — Flask Documentation (1.1.x)](https://msiz07-flask-docs-ja.readthedocs.io/ja/latest/api.html?highlight=request#flask.request)

# 動作確認方法

```flask-app.py
from flask import Flask, request

app = Flask(__name__)
@app.route("/", methods=["POST"])
def rt_recv():
  request.<any_method> # 本記事ではこの行のみ記述
  return ""

app.run(host="0.0.0.0", port=8080, debug=True)
```

```http-post.sh
#!/bin/sh

curl "http://localhost:8080" \
     -H "Content-Type: text/plain" \
     -d "<body>"
```

HTTP リクエストを送る方法は自由ですが、` curl `コマンドが手軽ではないかなと思います。

# ヘッダ

## 全ヘッダを表示/取得

```py
# 単に表示させる
print(request.headers)

# 辞書型で取得する
dict(request.headers)
```

## 特定のヘッダを取得

```py
# get メソッドで取得
request.headers.get("<Key>")

# 辞書型的に書いてのヘッダ取得も可能
request.headers["<Key>"]
## ただし get() とは違い、存在しないキーの場合エラーとなる
```

` get() `メソッドを用いた場合、指定したキーのヘッダが存在しない場合は None オブジェクトが返されます。
一方、辞書型の記述での取得の場合、存在しないキーを指定するとキーエラーとなります。

# ボディ

## ボディを単に文字列として取得

```py
request.get_data()
```

単に` string `型としてボディを取得します。

## JSON 形式の取得 (全てを辞書型で取得)

```py
# いずれでも可
request.get_json()
request.json
```

` Content-Type `ヘッダが` application/json `以外であった場合、None オブジェクトが返されます。

## Form 形式の取得

### 全てを辞書型で取得

```py
# いずれでも可
request.form.to_dict()
dict(request.form)
```

` Content-Type `ヘッダが` application/x-www-form-urlencoded `以外であった場合、空の辞書型オブジェクトが返されます。

### 特定のキーを取得

```py
# get メソッドで取得
request.form.get("<Key>")

# 辞書型的に書いてのヘッダ取得も可能
request.form["<Key>"]
## ただし get() とは違い、存在しないキーの場合エラーとなる
```

(` headers `と同様。)

## URL パラメータの取得

### 全てを辞書型で取得

```py
# いずれでも可
request.args.to_dict()
dict(request.args)
```

(` form `とほぼ同様。URL パラメータは、リクエストヘッダとかは関係無し。)

### 特定のキーを取得

```py
# get メソッドで取得
request.args.get("<Key>")

# 辞書型的に書いてのヘッダ取得も可能
request.args["<Key>"]
## ただし get() とは違い、存在しないキーの場合エラーとなる
```

(` headers `と同様。)
HoloLens 2 のAppDataにテキストを書き込み、デバイスポータルでダウンロードする


# 環境
- Unity 2019.4.22f1
- MixedRealityFeatureTool-1.0.2103.2- Beta
- MRTK 2.6.0

# 概要

- HoloLens 2でテキストファイルを書き込み、デバイスポータルでダウンロードする
![ss01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083912/61d1f6cd-5aa2-b5fb-fc8d-b3d2f8776b70.png)

![ss02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1083912/a142a391-fbbf-f2bb-284a-0eaf44dff828.png)

# AppDataにテキストデータを保存するC#のコード

- Application.persistentDataPath でパスを取得する

```csharp
var path = Application.persistentDataPath + "\\my_file.txt";
using (StreamWriter sw = File.AppendText(path))
{
    string s = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    sw.WriteLine(s);
}
```

# 保存先のパス

```dosbatch
C:\Data\Users\{USER_NAME}\AppData\Local\Packages\{APP_NAME}_{ID}\LocalState
```

# 用途

- アプリケーションの動作ログの保存（UnityのDebug.Logは、Debugビルドに時間がかかる＆処理負荷が高い）
- 設定ファイルの読み込み（デバイスポータルからアップロードしたファイルを、HoloLens 2 で読み込むこともできる）

# github

```
https://github.com/hachicomb/HoloLens2WriteTextExample
```
docker-composeでSSHコンテナを作ってみた

## 概要
Webアプリケーションから別のサーバーへSSH接続またはSFTP接続することを想定した、開発環境用のdocker構成をご紹介します。

## 環境
- macOS Big Sur バージョン11.2.3
- Docker version 20.10.5, build 55c4c88
- docker-compose version 1.28.5, build c4eb3a1f

## 構成
```sh
├── docker
│   ├── storage
│   │   └── Dockerfile
│   └── web
│       └── Dockerfile
├── docker-compose.yml
├── storage
└── web
    └── index.html
```

## 手順
### 1. キーペアの作成
キーペアを保存するためのディレクトリを作成して、その中にキーペアを作成します。

```sh
mkdir -p docker/.ssh
cd docker/.ssh
ssh-keygen -t rsa -b 4096 -N "" -f test_rsa
```

`docker/.ssh`の中に、`test_rsa`と`test_rsa.pub`ができていればOKです。

### 2. docker-compose.ymlの作成
今回は検証用に２つのコンテナを用意します。

```docker-compose.yml
version: '3.8'
services:
  web:
    build: ./docker/web
    volumes:
      - ./web:/var/www/html
      - ./docker/.ssh/test_rsa:/root/.ssh/test_rsa
    ports:
      - '80:80'
  storage:
    build: ./docker/storage
    volumes:
      - ./storage:/storage
      - ./docker/.ssh/test_rsa.pub:/root/.ssh/authorized_keys
    ports:
      - '10022:22'
```

`web`コンテナ: SSH元のホスト想定です。秘密鍵`test_rsa`をマウントしています。
`storage`コンテナ: SSH先のホスト想定です。公開鍵`test_rsa.pub`をマウントしています。

### 3. Dockerfileの作成
`docker`ディレクトリに`storage`と`web`ディレクトリをそれぞれ作成します。

#### webコンテナのDockerfile
`docker/web`にDockerfileを作成します。

```dockerfile
FROM php:7.4-apache

RUN apt-get update && apt-get install -y openssh-server
```

#### storageコンテナのDockerfile
`docker/storage`にDockerfileを作成します。

```dockerfile
FROM ubuntu:20.04

RUN apt-get update && apt-get install -y openssh-server
RUN mkdir /var/run/sshd

RUN sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config

RUN sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd

ENV NOTVISIBLE "in users profile"
RUN echo "export VISIBLE=now" >> /etc/profile

EXPOSE 22

CMD chmod 0600 ~/.ssh/authorized_keys &&  \
    /usr/sbin/sshd -D
```

### 4. webアプリケーションを作成
webアプリケーションの処理の中でSSH接続したり、SFTPでファイルをアップロードやダウンロードする想定ですが、めんど...お時間の都合上(笑)、とりあえずはwebページが表示できるだけのものにしています。
`web`の中に、`index.html`を作成します。(作らなくてもいいです。)

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>テスト</title>
</head>
<body>
<p>Webアプリケーションコンテナ</p>
</body>
</html>
```

### 5. dockerの起動
いつも通りのコマンドです。

```sh
docker-compose up -d
```

### 6. 確認
SSHユーザーは`root`になります。

#### ローカルホストからのSSH接続
`localhost`の`10022`が`storage`コンテナの`22`と繋がっているので、以下のコマンドで接続できます。

```sh
# 秘密鍵を置いているディレクトリに移動
cd docker/.ssh

# SSH接続
ssh -i test_rsa root@localhost -p 10022
```

#### webコンテナからのSSH接続
コンテナ間の通信の場合は、ホスト名はコンテナ名を利用するようになりますので、以下のコマンドで接続できます。
ちなみに、portは22なので、オプションの指定はなくても問題ありません。

```sh
# webコンテナに入ります
docker-compose exec web bash

# 秘密鍵を置いているディレクトリに移動
cd /root/.ssh

# SSH接続
ssh -i test_rsa root@storage -p 22
```

## 参考
[Docker | 公開鍵でコンテナにssh接続する出来るようにするDockerfileの例](https://qiita.com/YumaInaura/items/7509061e4b27e03ea538)
1000LGTM記念に自分のqiita記事を分析してみた

こんな記事を開いて頂きありがとうございます。
そして私の拙い記事にいいねして頂いたみなさんありがとうございます。
つい嬉しくなったので自分の記事を少し分析してみました。

1記事で数千LGTMの方とか本当にスゴイ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224453/e6e576f2-492d-3437-04a2-fdc1a58eec63.png)

# LGTMランキング (5位まで)

|順位|title|views|LGTM|レート(LGTM/views)|
|---|---|--:|--:|--:|
|1|[pipでアップデートするときのコマンド pip update](https://qiita.com/HyunwookPark/items/242a8ceea656416b6da8)|291,702|383|0.001312984|
|2|[github private public 変更](https://qiita.com/HyunwookPark/items/1d24972dd71612eb81c9)|42,256|175|0.004141424|
|3|[slack リマインダーの設定整理 /remind](https://qiita.com/HyunwookPark/items/0e7d81d34307d0acea6c)|50,196|108|0.002151566|
|4|[Linux tar.gz tar 圧縮 解凍](https://qiita.com/HyunwookPark/items/047ba2da9ef16bcac356)|156,853|76|0.000484530|
|5|[Java屋さんの私がPythonをはじめる理由](https://qiita.com/HyunwookPark/items/68d029dd33807fc69ca4)|22536|71|0.003150515|

# レート(LGTM/views)平均

マーケティングでいうところの「セールスファネル」ですね。

**0.001409129倍**

1000viewsあたり1.4LGTMぐらい:relaxed:

# LGTM 0 の割合

* 投稿数 73
* LGTM0の投稿数 25

**34.2%**:stuck_out_tongue_closed_eyes:

# 月間アクセス数

平日しか見られない！:laughing:
でも思った以上に普段から見られているのはビックリ！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224453/91f3f185-561b-8f9e-143a-fccd9cbde9ac.png)

# アクセス時間帯

仕事時間しか見られない！:laughing:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224453/75598c07-1be3-e5bd-f9a2-a2836d8c1ae7.png)

# 投稿の仕方

### 最初は数行のメモ書きを投稿した

開発や勉強で気づいたことやちょっとした失敗を解消したことなどをメモとして投稿しました。

例えば↓この記事はエラー内容と動いたときのコードを貼っただけの記事です。
ちょっと調べれば誰でもわかるものです。
[Python UTF-8のCSVファイル読み込み (UnicodeDecodeError対応)](https://qiita.com/HyunwookPark/items/ebc963f82355c837c488)

「こんな記事どこにでもある。自分が書く必要なんてない。」とよぎりますが、
人と同じような内容だろうが気にせずに書きました。
もちろん人の記事のコピーはいけませんが、自分なりにまとめたりすれば十分価値があると思います。

### 公式サイトの整理

公式サイトを見ながら試したことやよく使いそうなポイントをまとめて整理した記事もよく投稿しました。
自分がよく使いそうなので公式サイトで毎度ページを探すのが面倒だからという発想でした。

まとめ内容は結構便利なようで記事のアクセス数も多いですね。

### よく見られる投稿は更新した

記事が古くなってくるとqiitaはこの記事は古いよというアラートを記事の先頭に表示されます。
古い記事はまず疑ってかかるのがエンジニア。あまりに古いものは閉じてしまう場合もあります。

よって、よく見られるものを中心に記載ミスや追記をしてメンテナンスをしました。

### 記事のコメント

qiitaの利用者は紳士淑女ばかりなのか変なコメントはほとんどありませんでした。
間違いの指摘やアドバイスや意見などを頂けるのでとても勉強になります。

頂いた意見はできるだけ記事に反映するようにしました。

と言っても、コメントはほとんどありません。
まだまだ炎上した記事もないし投稿数も少ないからでしょう。
最初はこのあたりを恐れる必要はなさそうです。

ちなみに、
弊社の経験の浅い女性メンバーが投稿したときはすぐに間違いの指摘などのコメントがあったので、
誰が書くかは大きいかもしれません。

このときすぐにコメントがあった原因が
「記事に間違いが多かった？」「初心者だから？」「女性だから？」「たまたま？」のどれなのかはわかりません。

### 気をつけていること

* 考える前に投稿する（まずはアウトプットの量をこなす）
* 画面イメージやコードをできるだけ貼る

# 最後に

結局この記事も自分のメモで、
完全ネタ記事ですが誰かの投稿の参考になれば幸いです。
SQL Server 基準日までの期間取得

# 実行日を基準に特定の日付を取得する
SQL Serverでクエリ実行日を基準日として特定の日付を取得する方法です。

## コード

```SQL
--抽出月の前月1日
CONVERT(NVARCHAR,DATEADD(MM,DATEDIFF(MM,0,GETDATE())-1,0),112)
--抽出月の前月末日
CONVERT(NVARCHAR,DATEADD(MM,DATEDIFF(MM,0,GETDATE()),-1),112)
--抽出月の前月から1年前の1日
CONVERT(NVARCHAR,DATEADD(MM,DATEDIFF(MM,0,GETDATE())-12,0),112)
```
##解説
コードの意味を解説していきます。
#### GETDATE関数：今日の日付を取得

```SQL
GETDATE()
```

#### DATEDIFF関数：2つの日付の差分を取得する
```SQL
---DATEDIFF(datepart, startdate, enddate)

DATEDIFF('MM',-1,GETDATE())
```
datepartは
yy：年
MM：月
dd：日
等を指定します。

startdateに数値を入れると基準日（1900年1月1日）からの差で日付指定できる。例）-1は1989年12月31日

#### DATEADD関数：日付を加える
```SQL
--DATEADD(datepart, number, date)
--前月1日
DATEADD(MM,DATEDIFF(MM,0,GETDATE())-1,0)
--前月末日
DATEADD(MM,DATEDIFF(MM,0,GETDATE()),-1)
--前月から1年前の1日
DATEADD(MM,DATEDIFF(MM,0,GETDATE())-12,0)
```

#### CONVERT関数：日付を文字列に変換する
```SQL
--CONVERT ( data_type [ ( length ) ] , expression [ , style ] )
CONVERT(NVARCHAR,DATEADD(MM,DATEDIFF(MM,-1,GETDATE())-1,-1),112)
```
styleの112はyyyymmdd形式に変換の意味。その他の形式は下記を参照
[https://docs.microsoft.com/ja-jp/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver15](https://docs.microsoft.com/ja-jp/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver15)
SharePoint のファイル更新日時を日本時間にする

# 作成済みサイトのタイムゾーン設定
サイトの設定をするには、**サイトの管理者権限** が必要です。
## サイト情報メニュ表示
1. SharePoint の該当のサイトにアクセスし、画面上部の「設定」ボタンをクリックします。
2. 表示されたメニューの **サイト情報** をクリックします。
![サイト設定](https://github.com/Kakimoty-Field/TechnicalMemo/raw/master/sharepoint/img/100.png)

## すべてのサイト設定を表示

表示された 「サイト情報」メニューの下部 **すべてのサイト設定を表示**　をクリックします。
![すべてのサイト設定を表示](https://github.com/Kakimoty-Field/TechnicalMemo/raw/master/sharepoint/img/110.png)

## サイト設定

表示された「サイトの設定」画面から **サイトの管理** 項目にある **地域の設定** をクリックします。
![サイト設定](https://github.com/Kakimoty-Field/TechnicalMemo/raw/master/sharepoint/img/120.png)

## タイムゾーン設定

1. **タイムゾーン** が [米国およびカナダ] になっているので、`(UTC+09:00) 大阪、札幌、東京` にします。
2. **ロケール** が [英語(米国)]になっているので、`日本語`にします。
3. 画面下部の「OK」ボタンをクリックして変更を確定します。

![タイムゾーン設定](https://github.com/Kakimoty-Field/TechnicalMemo/raw/master/sharepoint/img/130.png)

# 新しく作成するサイトの既定のタイムゾーン設定

既定のタイムゾーンを設定するには **Microsoft 365 の管理者アカウント**である必要があります。

## SharePoint 管理センターを表示

管理者アカウントでログインしたら、**Microsoft 365 管理センター** メニューから **SharePoint** を選択します。
![管理センター](https://github.com/Kakimoty-Field/TechnicalMemo/raw/master/sharepoint/img/010.png)

## SharePoint 設定を表示

「SharePoint 管理センター」のメニューから **設定** を選択します。
![SharePoint管理センター](https://github.com/Kakimoty-Field/TechnicalMemo/raw/master/sharepoint/img/020.png)

## タイムゾーン設定

1. 「設定」画面で **サイトの作成** を選択します。
2. 「サイトの作成」画面で **既定のタイムゾーン** を `(UTC+09:00)大阪、札幌、東京` にします。
3. 設定が完了したら画面下部の「保存」ボタンをクリックします。

###「設定画面」
![サイトの作成](https://github.com/Kakimoty-Field/TechnicalMemo/raw/master/sharepoint/img/030.png)

###「サイトの作成画面」
![サイトの作成のタイムゾーン設定](https://github.com/Kakimoty-Field/TechnicalMemo/raw/master/sharepoint/img/041.png)
PuppeteerでサイトをクロールしてURLリストを作るツール

はじめまして。Webサイトのリニューアルとか新規構築とかやってる世にいうWebディレクター？PM？的な役割をよくやるおじさんです。

おじさんはよく数百から数千ページのWebサイトの新規構築・リニューアルPJを担当したりします。提案やら見積もりの断面から参画することが多く、だいたいサイト規模を図るところから入るんですが。担当者によっては適当にサイト規模の見積もりをして、後から**こんなでかいサイトだと思わなかった**なんて話になります。

古のツールとして[Website Explore](https://www.vector.co.jp/soft/winnt/net/se247055.html)なんてものがあり、未だにこれ使ってページ数算出している人もよく見かけます。

今どきキラキラしたheadless chromeとか色々あるんだからもっとスマートにできないの？と思っていたところ、まとまった自分の時間が取れたので便利そうなのを作ってみました。そしてgithubで公開してみました。

https://github.com/youji/URLsalvager

# 前提として

おじさんは前時代的なWebサイト構築の時代を生きてきて、本格的にコードを書かなくなって約10年。アラフォーでなんか色々間違っていると思うんですが何事も経験なので、こうしてQiita記事も初めて書いてみました。

では、何を作ったのか書きますね。と思ったんですがめんどくさいのでREADMEをほぼそのまま載せますね。

# これは何？

Web サイトリニューアルの際等に現行サイトに sitemap.xml が無く、サイト規模が全くわからない場合があります。そんな時に使うツール。
任意の URL から a タグ href 属性を辿り、存在する URL（a タグリンクが繋がっている URL） をリスト化します。

- puppeteer を使って a タグを巡り URL をリスト化、エクセルファイルとして出力
- title,description,charset 等基本的なページ情報もついでに取得してエクセルファイル内に出力
- クロールした際の HTML コードもついでにテキストファイルとして出力
- puppeteer 越しに headless chrome の描画結果 a タグを走査するので、js 等で出力されている a タグリンクも巡ることが可能

※対象 URL にはそれなりのアクセス負荷がかかるので、サイト管理者の許可なく本ツールを使用しないでください

# 実行方法

下記で node パッケージインストール

```
npm i
```

conf.yaml を設定したうえで下記の様に実行

```
node salvage.js
```

conf.yaml に記載の startUrl を起点として a タグリンクを辿り、url リストを作成する。URL リストはエクセルファイルとして出力されます。

# conf.yaml 設定内容

- startUrl
  - クロールの起点となる URL の指定
  - 通常はサイトのトップページ等を指定する
  - 複数指定可能
- allowDomain
  - a タグを辿る際に許可するドメイン
  - 基本的には startUrl に指定した URL のドメインを指定する
  - 複数指定可能
- basicAuthentication
  - クロール対象サイトに BASIC 認証がかかっている際にコメントアウトを解除し使用
- loadBlockFileExtention
  - クロールをする際にロードをブロックするファイル
  - クロール速度を高速化するために画像等の読み込みをブロックすることを推奨
- emulateDevice
  - UA 判定等でスマホ UA を使いたい場合等はコメントアウトして使用
    （https://github.com/puppeteer/puppeteer/blob/main/src/common/DeviceDescriptors.ts のデバイスを指定可能）
  - コメントアウトした状態では puppeteer デフォルトの UA でアクセスする（chrome）

# 結果出力

result ディレクトリ内に結果が出力されます

## 出力例

```
result
└── 20210407151539483
    ├── htmlcode
    │   ├── https:__example.com_.txt
    │   ├── https:__example.com_business.html.txt
    │   ├── https:__example.com_en_.txt
    │   ├── https:__example.com_en_business.html.txt
    │   ├── :
    │   └── https:__example.com_about.html.txt
    └── result.xlsx

```

## result.xlsx の出力内容

- url
  - URL
- status
  - クロールした際の HTTP ステータスコード
  - conf.yaml の loadBlockFileExtention に指定された拡張子の URL は"SKIP"として出力
- title
  - title タグ内文言
- description
  - meta[name="description"]の content 値
- keywords
  - meta[name="keywords"]の content 値
- canonical
  - link[rel="canonical"]の href 値
- viewport
  - viewport 指定
- charset
  - document.charset の値


# おわりに

- それなりにサーバ負荷かかると思うので、100ページ位のサイトでしか試せてない
- 誰か実戦投入として大きめのサイトで試してみてほしい（もちろんサイト管理者に許可とったうえで）
- ソースコードはネット上の色んなとこからコピペしたりしているので、全然きれいに書けてない。
- 誰か今どきなキラキラしたコードに書き換えてほしい
- あとスクショ撮ったり各ページで読み込んでるアセットファイルとかをまるっとダウンロードするツールとか作りたいなー
フィッシング詐欺を助長しないための対策

先日投稿したこちらの記事の続編です。

https://qiita.com/sasshi_i/items/f6057f70e9aea80e4662

https://qiita.com/sasshi_i/items/be58e4c084618a732e65

本日は`フィッシング詐欺への対策`についてまとめていきます。

# 教材
教材はIPAが出している安全なウェブサイトの作り方を使っています。
https://www.ipa.go.jp/security/vuln/websecurity.html



# フィッシング詐欺とは？
>フィッシング詐欺とは、悪意のある人が、金融サイトやショッピングサイト等を装った偽のウェブサイトを 作成して、利用者を巧みにそこへ誘導して、利用者の認証情報やクレジットカード番号等を不正に取得 するものです

引用：
https://www.ipa.go.jp/security/vuln/websecurity.html
安全なウェブサイトの作り方　改訂第7版


# 対策
## EV SSL証明書を取得し、サイトの運営者が誰であるかを証明する
>EV SSL 証明書を用いると HTTPS 通信でアクセスした際、ブラウザのアドレスバーに組織名が緑色で表示されます

引用： 
https://www.ipa.go.jp/security/vuln/websecurity.html
安全なウェブサイトの作り方　改訂第7版


### SSLサーバ証明書の種類
* ドメイン認証
* 企業実在認証
* EV 認証


> ドメインの所有者と運営組織の法的実在性の確認の他、組織の所在地や申し込み意思と権限を確認し、発行される証明書です。
EV認証は世界標準の認証ガイドラインがあり、サーバ証明書の中で最も厳格な審査が行われます。

引用：https://jp.globalsign.com/ssl-pki-info/ssl_beginner/types-of-ssl.html

# リダイレクト先や子フレームのURLを外部パラメータから取得しないようにする。
例えば下記のようにリダイレクト先をクエリストリングで指定している場合、クエリストリングの値を変えたURLが投稿されると悪意のあるサイトにリダイレクトされてしまいます。
`https://hogehoge.jp?url=https://fugafuga.jp`

このように外部パラメータからURLを取得してしまうと思わぬ脆弱性を注入してしまいます。

参考：https://yamory.io/blog/about-open-redirect/
【CSS】 画面サイズが大きい端末にて、子要素の間隔の広がりを防ぐ




##問題
・width:100%;にすると端末に合わせて幅が変わります。 
しかし、大変大きい画面幅の場合、width:100%;のままだと
中の子要素を並べて表示していると、要素同士の間隔が開き過ぎてしまいます。



##対策
```:css
.対象のクラス名 {
 max-width:widthを指定する画面幅の上限px;
  width: 100%;
}
```

##解説
max-widthで画面幅の上限の指定ができる。
これによって2000pxのような画面幅の端末でも、指定した画面幅の上限で
witdth:100%;を指定ができるので、
子要素（並べた画像など）の幅の広がりを防ぐことが可能。

##例
・2000pxの画面幅の端末にて、widthを1100pxの上限とする

```:css
.test {
 max-width:1100px;
  width: 100%;
}
```
テンプレート文字列

テンプレート文字列について学習していきます。

テンプレート文字列とは、バックティックで囲んだ文字列の事を言います。

```
`${ 変数 }`
```
上記のように記述します。

できることは、以下になります。

①　変数の値を文字列に埋め込むことができる
②　改行をすることができる
③　式を文字列に埋め込むことができる

# ①　変数を文字列に埋め込む事ができる

```index.js
<script>
    'use strict'

    let language = "こんにちわ";

    document.write(`皆さん${language}!!`);
  </script>
```

![スクリーンショット 2021-04-07 17.16.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1033397/67f017f9-2136-1705-03e0-be9d9bf98153.png)

# ②　改行をすることができる

```index.html
<script>
document.write(`あいうえお
  かきくけこ`);
</script>
```
![スクリーンショット 2021-04-07 17.48.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1033397/46a92181-be79-37a3-720d-beeb247ba567.png)


#　③　式を文字列に埋め込むことができる

```index.html
<script>
let name = "とんかつ定食";
   let price = 400;

   let msg =`今日の${name}は${price * 1.08}円です！！`;
   document.write(msg);
</script>

```
![スクリーンショット 2021-04-07 17.42.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1033397/f1b98871-f307-925a-5212-32b217eec103.png)
Rails初学者によるRailsチュートリアル学習記録① 第0章

##目次
- [1. はじめに](#1-はじめに)
- [2. 第0章とは](#2-第0章とは)
- [3. これまでの学習内容](#3-これまでの学習内容)
- [4. 今後の学習計画](#4-今後の学習計画)

##1. はじめに
- この記事は、Rails初学者の工業大学三年生がRailsチュートリアルの学習記録を<br>つけるための記事です。
- 筆者自体がRailsやWebについて知識が少ないので、内容の解釈などに<br>間違いがある可能性があります。(その時はコメントで指摘してくださると助かります！)

##2. 第0章とは
この記事の題名になってる第0章とは、Railsチュートリアルを手を動かしながら進めているわけでなく、
電子書籍版のRailsチュートリアルを読み進めている段階であるというところからきています。

記事を書いている段階で、全14章で構成されているチュートリアルの半分の第7章まで読みました。
読み始めたときは、全部読み終えてからQiitaに読んだ内容をまとめた記事を投稿して
学習の2周目に入ろうと思っていたのですが、思っていたより時間がかかっているので、
一度ここで記事を投稿します。

##3. これまでの学習内容
本文の中で7章から徐々に難易度が上がっていくと書かれており、
実際に、6章まではProgateのWeb開発パス+αくらいの知識量の私でもスラスラ内容を理解しつつ
読み進められていたのですが、7章から実際に手を動かしてみないと
理解しにくいだろうなと思う内容も増えてきました。

このまま読み進めていってもよいのですが、読み終えることには前半部分の記憶がスッキリなくなってそうなので
第7章までの内容をキーワードレベルでも見返せるようにしておきます。

#####▶第1章 -ゼロからデプロイまで
- Railsチュートリアルを進める上での前提知識の確認
    - チュートリアルで用いる言語
        - Ruby, Rails, HTML, CSS
        - コマンドライン
        - JavaScript, SQL(若干)
    - あると良い経験
        - Progate(Web開発パス)
        - Railsチュートリアル初心者シリーズ

- Railsチュートリアルを進める上での前提知識の確認
    - Hello_appというアプリの作成
        - Railsチュートリアルのテーマは熟練<br>→チュートリアルで躓いてもそれが成長につながるよみたいな意味
        - Cloud9というIDEの準備
        - Git, GitHubの使い方
        - Herokuを使ったデプロイのやり方

#####▶第2章 -Toyアプリケーション-
- Scaffoldという手法を使ったアプリ開発
    - シンプルなSNSアプリをScaffoldを使って開発、デプロイする
    - RailsのMVCについて
    - Railsの組み込み関数について
    - Railsコンソールの使い方

#####▶第3章 -ほぼ静的なページの作成-
- Sample_appというアプリの開発
    - コントローラの作り方・使い方
        - アクションとビューの関係
    - ビューへのRubyの埋め込み
        - ページタイトルを動的に切り替える
    - 自動化テストの作り方
    - ルーティングの設定

#####▶第4章 -Rails風味のRuby-
- Rubyという言語の仕様について
    - ヘルパー(組み込み・カスタム)
        - 準備されてる関数の使い方、関数の作り方
    - 文字列で使えるメソッド
    - Rubyのオブジェクト、クラスとは

#####▶第5章 -レイアウトを作成する-
- 静的ページの作り方
    - Bootstrap, Sassの使い方
    - アセットパイプラインとは
    - 名前付きルートの使い方
    - 統合テストの作り方

#####▶第6章 -ユーザのモデルを作成する-
- ユーザ登録ページの作り方
    - Userモデルの作り方
    - データベースのマイグレーションについて
    - モデルの値のバリデーション
        - 存在性、長さ、一意性、フォーマットのバリデーション
    - セキュアなパスワードにするための準備

#####▶第7章 -ユーザー登録-
- ユーザー登録機能の追加
    - ユーザ一覧の表示
    - ユーザ登録フォームの作成
        - ユーザ登録の成功、失敗それぞれの挙動
    - SSLを導入してデプロイする方法

##4. 今後の学習計画

#####▶計画の要約
- 残りの第8章～第14章を1週間以内に読み終わらせる
- 2周目に入り、第6章までは1週間に2章のペースで3週間で終わらせることを目指す
- 第7章以降は、最低でも1週間に1章のペースを保つ
- 毎週または、2週間に1回はQiitaへ学習記録を投稿する

これまで第6章まで読み進めてきたので、ひとまず第14章まで読んでいきます。
電子書籍版だと約840ページまであり、第8章が420ページくらいから始まっているので
内容的にも、ページ数的にも半分読み終わっていることになります。
ただ、ここまで読むのに10時間くらいかかってしまっているので、難易度がここから上がっていくことを
考えると15時間くらいを目安にしようと思います。
期間にして1週間を目標にしていきたいです。

私の最終的な目標は、自分自身でWebアプリを開発することなので、
2周目もスピード感を意識できればと思っています。
手を付けて見ないとどれほどのスピードで進められるか、どれくらいの学習時間が取れるかが
分かりませんが、今のところは第6章までは最低でも1週間で1.5章分、
できれば2章を目安に進めていきたいです。
7章以降は難易度が正確につかめていないのですが、
Railsチュートリアル終了後の自作アプリ開発のことを考えると1週間で1章は進めたいところです。

Qiitaへの学習記録の投稿は自分の理解を深めたり、モチベーションを維持するために、
毎週または、2週間に1回のペースで続けていきたいと思います。
次の投稿はRailsチュートリアルを読み終わった段階で、今回と同じように
読んだ内容を記録しておく記事になると思います。
Java Word 空白行を一気に削除する

<p><strong>今回はSpire.Doc for Javaという無料のライブラリを利用して、Word 空白行を一気に削除する方法を紹介します。こうすれば、ワードのページをもっと美しくするので、ぜひ身につけるスキルだと思いますよ。</strong></p>
<p><strong>下準備</strong></p>
<p><strong>1.E-iceblueの公式サイトから<a href="https://www.e-iceblue.com/Download/doc-for-java-free.html">Free Spire.doc for Java</a>無料版をダウンロードしてください。</strong></p>
<p><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/l/lendoris/20210407/20210407172021.png" alt="f:id:lendoris:20210407172021p:plain" title="" class="hatena-fotolife" itemprop="image" /></p>
<p><strong>2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにあった相応しいSpire.doc.jarを参照に追加してください。</strong></p>
<p><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/l/lendoris/20210407/20210407172033.png" alt="f:id:lendoris:20210407172033p:plain" title="" class="hatena-fotolife" itemprop="image" /></p>
<h4><strong>元のファイル</strong></h4>
<p><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/l/lendoris/20210407/20210407172058.png" alt="f:id:lendoris:20210407172058p:plain" title="" class="hatena-fotolife" itemprop="image" /></p>
<h4><strong>サンプルコード</strong></h4>
```JAVA
import com.spire.doc.*;
import com.spire.doc.documents.DocumentObjectType;
import com.spire.doc.documents.Paragraph;

public class DeleteBlankParas {
    public static void main(String[] args) {
        //Wordファイルをロードします。
        Document doc = new Document();
        doc.loadFromFile("sample.docx");

        //Sectionをループします
        for(int i = 0; i < doc.getSections().getCount();i++)
        {
            //sectionを取得します。
            Section section = doc.getSections().get(i);

            //sectionのオブジェクトをループします。
            for (int j = 0;j < section.getBody().getChildObjects().getCount();j++)
            {
                //オブジェクトのタイプを取得します。
                Object object = section.getBody().getChildObjects().get(j).getDocumentObjectType();

                //段落をループします。
                for(int z = 0 ; z < section.getParagraphs().getCount();z++)
                {
                    //段落を取得します。
                    Paragraph paragraph = section.getParagraphs().get(z);

                    //オブジェクトのタイトルが段落かどうかを判定します。
                    if(object.equals(DocumentObjectType.Paragraph))
                    {
                        //段落の中身があるかどうかを判定します。
                        if(paragraph.getChildObjects().getLastItem() == null)
                        {
                            //空白の段落を削除します。
                            section.getBody().getParagraphs().remove(paragraph);
                            z--;
                        }
                    }
                }

            }
        }

        //保存します。
        doc.saveToFile("DeleteBlankParas.docx",FileFormat.Docx_2013);
        doc.dispose();
    }
}
```
<h4><strong>実行結果</strong></h4>
<p><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/l/lendoris/20210407/20210407172148.png" alt="f:id:lendoris:20210407172148p:plain" title="" class="hatena-fotolife" itemprop="image" /></p>
<p> </p>