OmniAICreator/Qiita-1.07M huggingface dataset:
Laravel Excel から出力した csv のレコードが重複してしまっていた問題を解決した話

## はじめに

あるプロジェクトで、 `Laravel` を使って構築したシステムの DB のデータを csv で出力する要件があり、[`Laravel Excel`](https://laravel-excel.com/) を導入して実現していました。

しかしあるとき、出力した csv のレコードが重複しているという報告があり、実際に全く同じレコードが2件重複して出力されていることがわかりました。
さらには、出力されるはずのデータが含まれていないことも判明しました。

この記事では、この問題を解決するまでの過程を紹介し、自らへの戒めと教訓としたいと思います。

### Laravel Excel とは

https://laravel-excel.com

`Laravel` で Excel ファイルの操作を行うためのライブラリです。
`PhpSpreadsheet` というライブラリが元になっていて、 `Laravel` での操作に特化しています。
今回のプロジェクトでは、この `Laravel Excel` を使って、主に csv ファイルの入出力を行っています。

## 環境

- `PHP : 7.3.5`
- `Laravel : 5.8`
- `PostgreSQL : 11.3`
- `Laravel Excel : 3.1.9`

## 発生した問題
- 約4万件のデータを抽出した際に、20〜30件ほど重複している
- CSVを出力するたびに、重複するデータが異なる
- データが重複した分だけ、本来であれば存在するデータがなくなっている
- データの総件数は毎回変わらない

## 犯人探し

### PostgreSQL

#### `LEFT JOIN` のやり方が間違っている？
とある関係で `Eager Loading` ではなく `LEFT JOIN` を使用しているので、これが真っ先に原因かと思ったのですが、単純なクエリだったのでこれは問題なさそうでした。

#### `SELECT` 文の書き方が間違っている？
`SELECT` でテーブル名を指定しておらず、意図しないテーブルの `id` などのデータが出力されてしまっているのかとも思いましたが、テーブル名を指定する書き方をしているのでこれも大丈夫そうでした。

#### PostgreSQL の バグ？
こんなバグがあったらもっと叩かれてそう。。

### Laravel Excel

#### Github の issue で同じような問題を探してみる
次にライブラリである `Laravel Excel` のバグかと思い、 Github で同じような報告がないか探してみました。
が、これは特に見つかりませんでした。

#### ライブラリの中身をみてみる
中でどのような処理が行われているのかわからなかったので、中のコードを読んでみました。
今回は `Laravel` の `QueryBuilder` から csv を作成していたので、途中で `chunk` メソッド[(リファレンス)](https://laravel.com/api/5.8/Illuminate/Database/Concerns/BuildsQueries.html#method_chunk) が使用されていました。
これはDBから取得するデータを小分けにすることで、メモリ使用量を抑え、大量のデータでも出力できるようにしているのだと思います。


## 再度考察

ここまでで具体的な原因はわからなかったので、再度原因を考察してみることにしました（実際は結構頭抱えてました）。

#### 改めて重複しているデータを眺めてみる
よくよく見てみると、何故か重複データは2000行目や5000行目など、1000行目単位の付近で発生していました。
この 1000 という数字が何かのヒントだと思い、調べてみると、`Laravel Excel` の設定ファイルに `chunk_size` という項目があり、これが 1000 になっていました。
これを試しに 500 にしてみると、重複が1500行目や4500行目など、500の区切りでも発生するようになりました。

#### `chunk` によって発行されているクエリを調べてみる
```sql
SELECT * FROM 'テーブル名' LIMIT 1000 OFFSET 0;
```

#### そうか、わかったぞ！（cv：高山みなみ）

## 原因は...

**`ORDER BY` に、ユニークになるカラムを指定していないこと** でした。
今回のクエリでは、ユニークでないカラムを `ORDER BY` に指定していたため、このカラムが同じ値の場合、**並び順がDBによって決定**されてしまっていました。
これと `LIMIT` および `OFFSET` が合わさることにより、**クエリを発行するたびに並び順が変更され、境目付近のデータが重複してしまっていた**のだと考えられます。

この現象については、下記のブログに詳細が記載されていました。
https://blog.mmmcorp.co.jp/blog/2018/03/22/sql_order_by/

同じように `LIMIT`、 `OFFSET` を使用しているペジネーションなんかでも注意が必要ですね。

そして `ORDER BY` にユニークとなるカラムを設定したところ、見事に重複がなくなりました。
これで解決ですね。


## さいごに

というわけで結局犯人は自分だったわけですが。。
原因がわかるとすっきりしますが、わからないうちは辛く苦しかったです。。笑
ライブラリを使っているときに予期せぬことが起きた場合には、ライブラリの中身を見ることが大事ですね。

では最後にまとめ
### 並び順の権を DB に握らせるな！！
Tensorflow 2.x+TuringGPUでの環境作成方法

#状況
Tensorflow 2.x+TuringGPU環境で、標準のnvidia-containerを利用すると以下のような表示となり、GPUが利用されなかった。

```
tensorflow/stream_executor/platform/default/dso_loader.cc:44] Successfully opened dynamic library libnvinfer.so.6 
tensorflow/stream_executor/platform/default/dso_loader.cc:55] Could not load dynamic library 'libnvinfer_plugin.so.6'; dlerror: libnvinfer_plugin.so.6:  cannot open shared object file: No such file or directory 
tensorflow/compiler/tf2tensorrt/utils/py_utils.cc:30] Cannot dlopen some TensorRT libraries. If you would like to use Nvidia GPU with TensorRT, please make sure the missing libraries mentioned above are installed properly.
```

#解決策
tensorflowのコンテナを利用すれば簡単に動作させることができた
```
docker run -it --gpus all --name containername -itd --shm-size=16g tensorflow/tensorflow:latest-gpu 
```

TensorRTのモジュールの追加インストールをおこなっても解決できなかった。
Windows 10 Application popup EVENT ID 56のトラブル

Yutubeを巡回させているPCが突然落ちるトラブルが発生(2020/12/15 08:33)
電源再投入をしても同じ現象が起きるのでイベントログを調べると以下のエラーが怪しいログを発見
 
- Application popup, Event ID(イベントID） 56

12/14に「Windows 10、バージョン 1909 の機能更新プログラム」を適用していたので、これをアンインストール
とりあえず、問題の回避に成功した模様

- Windows 10、バージョン 1909 の機能更新プログラム
-
【Stereo Depth】 RTS2Net : AnyNetをSemantic Segmentationで精度UP

<h1>Real-Time Semantic Stereo Matching</h1>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/534f2eaa-3c5b-dd6c-87f0-c31061ec594f.png)
AnyNetにSemantic Segmentationを追加してさらに物体に対してのSmoothingを行うネットワーク。
AnyNetと基本的なSemantic Segmentationが分かれば難しい事はなかったので、結果だけを見たいと思う。

<h1>結果</h1>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/76fe9cec-afbb-4c7b-e581-41b65c93270d.png)
すべてのstageに置いてRTS2の方がAnytimeより精度がよくなってる。速度が遅くなるのは仕方ないか。
ちなみに約10Hzの所に赤字になっているのを比べるとRTS2Netの方が同じ速度で精度が良いという事を主張したいらしい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/482094/a07b29b2-6ab8-28a0-c8d7-dced78c0d5df.png)
RealTime System(10Hz)の中では一番精度が良い。

<h1>結論</h1>
・Semantic Segmentationを使用する事でStereo Depthの精度が上がる事がわかった。

<h1>参考文献</h1>
Real-Time Semantic Stereo Matching
https://arxiv.org/pdf/1910.00541.pdf
サムネール画像を生成する（コピペOK）

jpegファイルのサムネール画像を生成します。

①以下のコードを書きます
②mkthumbs.phpのサブディレクトリとして pics と thumbs を作成します
③picsにjpeg画像を置きます
④コマンドラインで
　　% php mkthumbs.php
 と入力します
⑤index.htmlをブラウザで開きます


```php:mkthumbs.php
<?php
$dir = opendir("pics");
$pics = array();
while($fname = readdir($dir)){
  if(preg_match("/[.]jpg$/", $fname))
    $pics[] = $fname;
}
closedir($dir);

foreach($pics as $fname){
  $im = imagecreatefromjpeg("pics/$fname");
  $ox = imagesx($im);
  $oy = imagesy($im);

  $nx = 100;
  $ny = floor($oy * (100 / $ox));

  $nm = imagecreatetruecolor($nx, $ny);

  imagecopyresized($nm, $im, 0, 0, 0, 0, $nx, $ny, $ox, $oy);

  print $fname." のサムネールを生成します...\n";

  imagejpeg($nm, "thumbs/$fname");
}

print "index.html を生成しています...\n";

ob_start();
?>
<html>
<head><title>サムネール</title></head>
<body>
<table cellspacing="0" cellpadding="2" width="500">
<tr>
<?php
$index = 0;
foreach($pics as $fname){
?>
<td valign="middle" align="center">
<a href="pics/<?php echo($fname); ?>"><img src="thumbs/<?php echo($fname);
  ?>" border="0" /></a>
</td>
<?php
$index += 1;
if($index % 5 == 0) echo("</tr><tr>");
}
?>
</tr>
</table>
</body>
</html>
<?php
$html = ob_get_clean();
$fh = fopen("index.html", "w");
fwrite($fh, $html);
fclose($fh);
?>
```
OpenWeatherMapのapiの復習

今回のapiの部部に関わっているのはここ

```js
let xhr = new XMLHttpRequest(); //1
    
    xhr.open('GET', requestUrl);//2
    
    xhr.send();
    
    xhr.onreadystatechange = function () {//3
        if (xhr.readyState == 4) {
            ShowTodayWeather(xhr.responseText);//4
        }
    }
    
    function ShowTodayWeather(response) {
        let obj = JSON.parse(response) //5
        let weather = obj.weather[0].description; //6
        let city = obj.name;
        let temp = obj.main.temp;
        let humi = obj.main.humidity;
        let icon = obj.weather[0].icon;
    
        console.log("現在" + city + "の天気は" + weather);
        console.log(`気温は ${temp} 度です`);
        console.log(icon)
    
        const Tem = document.getElementById("tem");
        const Hum = document.getElementById("hum");
    
        Tem.innerHTML = temp
        Hum.innerHTML = humi
        Icon.setAttribute('src','http://openweathermap.org/img/w/'+icon+'.png')   
    }
``` 

mdnでは[こちら](https://developer.mozilla.org/ja/docs/Learn/JavaScript/Client-side_web_APIs/Introduction)

1つずつ見ていく

1ではXMLHttpRequestオブジェクトを作成
newに関しては別でも勉強してまとめた。

2ではGETメソッドを使ってローカルサーバ上のurlを指定している。
情報を取得する時にはGET
情報を送信する時にはPOSTメソッドを使う。

3では1での処理の状況を監視している。
ここの```readyState```が4なのは4がリクエスト処理の完了を表すからである。


4の```responseText```はxhrのサーバーから受け取ったテキストを返している。
>mdn
https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest/responseText

5の```JSON.parse```はjsonデータで受け取った天気情報をjavascriptで扱えるようにするメソッドである。

もしJavascriptからjsonデータにしたいときには```JSON.stringify```メソッドを使う。

これらをデコード・エンコードと言う。

6では日本語で取得するために最後```description```とつけている。

それ以下は[公式ドキュメント](https://openweathermap.org/current#parameter)を参考にデータを取得した。

>参考サイト
https://www.sejuku.net/blog/47716
https://reffect.co.jp/html/xmlhttprequest-basic
[Jupyter][Python] matplotlib の hist で x軸の目盛と棒の中心線のずれを直す（1刻みでカウントする場合用）

``matplotlib.pyplot``の`hist`でヒストグラムを描画した時、x軸の目盛と棒の中心線のずれを`range`を指定する事で直します。ただし下のコードは1刻みでカウントする場合の`range`です。

```python
import pandas as pd
import matplotlib.pyplot as plt

s = pd.Series([1,2,2,4,4,4,4])
bins = (s.max()-s.min()+1)

# 何もしない場合
plt.hist(s, bins=bins)
plt.show()

# 修正した場合
plt.hist(s, bins=bins, range=(s.min()-.5, s.max()+.5))
plt.show()
```
![スクリーンショット 2020-12-15 午前9.03.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178570/08378a2f-1a5f-5d39-dd80-1037df09d122.png)
UE4.26でのHoloLensのHolographicRemotingでできることまとめ


[Unreal Engine 4 (UE4) その3 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/ue4_tre)の15日目です。

今回もいつも通りUE4でのXRネタです。
最新のUE4.26.0でHoloLensの各種機能は、ストリーミングでどこまで動作するかまとめをします。

## 背景

先日UE4.26がリリースされ、MR関連も色々と更新がありました。
HoloLens関連でのアップグレード要素は[ここ](https://docs.microsoft.com/ja-jp/windows/mixed-reality/develop/unreal/unreal-porting)にまとめられています。

また、UE4ではHoloLensのHolographicRemotingによるストリーミングに結構力も入れられているようで、
EpicGames公式の大型サンプル、[MissionAR](https://www.unrealengine.com/marketplace/ja/product/missionar)もストリーミング動作前提だったり、UE4.25.1からはパッケージ化されたPC向けアプリ(exeファイル)からのストリーミングにも対応していたりします。
([MissionAR解説ドキュメント](https://docs.unrealengine.com/ja/Resources/Showcases/MissionAR/index.html))

そこで、UE4.26.0で更新された各種HoloLensの機能で、ストリーミングではどこまで動作させられるかを確認していきたいと思います。
(UE4.26でHoloLensのこの機能を使うにはどうしたら良い等は基本的に[MSのドキュメント](https://docs.microsoft.com/ja-jp/windows/mixed-reality/develop/unreal/unreal-development-overview)に全て載っているので。)
UE4でのHoloLensのストリーミング手順は[こちら](https://docs.microsoft.com/ja-jp/windows/mixed-reality/develop/unreal/unreal-streaming)。

(ストリーミングで動作しないものについては、[こちらのドキュメント](https://docs.microsoft.com/ja-jp/windows/mixed-reality/develop/platform-capabilities-and-apis/holographic-remoting-troubleshooting)に記載されているようですが、機能面目線からどうなのかは判りにくいかなというのもありましたので。)

※AzureSpatialAnchorsは以前[スライド](https://www2.slideshare.net/naoakiyamaji/ue4azurespatialanchors)でも記載しましたが、手元でうまくいっていないので、それ以外の各種機能について確認しています。(一部うまくいっていませんが、UE4.25.3ではHoloLens 2のストリーミングで動いていそうでした。)


## 結論

手元で動作させたところ、以下の状態のようでした。

| 機能 | ストリーミング【HoloLens 2】 |  | その他補足等 |
| ----- | ----- | ----- | ----- |
| アイトラッキング | ○ | | |
| ハンドトラッキング | ○ | | |
| ジェスチャ入力 | ○ | | |
| 空間マッピング | ○ | | |
| 空間オーディオ | ○ | | |
| ローカルアンカー | ○ | | PC向けの設定有 |
| 音声入力 | ○ | | |
| MixedRealityCapture | × | | |
| QRコード読み取り | ○ | | |
| UMGによるシステムキーボード | △ | | UMGは問題ないが、仮想キーボードはストリーミングではでない |
| レイトレーシング | × | | レイトレーシング有効時はストリーミングの接続が失敗する |

※あとでHoloLens 1(初代のHoloLensでの情報も追記します。)

WinRTの機能呼び出しは呼び出し先機能にもよるかもなので、今回は試していません。


##### ハンドトラッキング
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214053/9180080a-df95-88e2-5ecb-ae54da440b76.png)

##### 空間マッピング
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214053/5e746f11-c119-1b82-622b-e90b25ae5345.png)


### その他補足等

あとで検証に使用した各機能を単体でチェックできる簡単なサンプルプロジェクトをあげますので、
AzureSpatialAnchorsとWinRT機能呼び出し以外のUE4での各種機能を簡単にテストしたい時などに使ってもらえたらと思います。

ぷち情報として、ストリーミングでHoloLensを試す場合はHoloLens向けのパッケージ化を行わないため、エンジンのインストールオプションで対応プラットフォームのHoloLens 2にチェックをつけていなくても動作させられます。


## さいごに

UE4ではHoloLensはパッケージストリーミングで動作が確認できることが多く、HoloLens 2ではPC向けにパッケージ化したexeファイルからもストリーミング接続ができるので、
UE4ストリーミングとHoloLens 2を組み合わせたものを実運用するというのが現実的に可能そうに思います。
そうすることで、HoloLens上のみの限られたマシンリソースではなく、PCというよりパワーのあるマシンリソースを使用できるため、より品質の高い体験ができたり、見た目に拘ったものの表示ができたりします。

開発においても、わざわざパッケージ化してインストールする時間を短縮して実機で確認もできますし、
デバッグ等も実機のみで行うより、PCを経由した方が行いやすいことも多いかなと思います。

なので、UE4でHoloLensの開発を行う際は、HolographicRemotingを使用したストリーミングでの運用の検討や、開発フローでの積極的なストリーミングの利用を行うと良いと思います。
Python学習 基礎編　~文字列リテラルとは？~

こちらはPython学習の備忘録です。
プログラミング初心者や他の言語にも興味、関心をお持ちの方の参考になれば幸いです。
* * *
###文字列リテラルとは？


Python学習を初めて日の浅いわたしには、「**文字列リテラル**」という言葉がはじめてだったので、「**文字列リテラル**」ってなんだろうと思い調べてみました。
※ string literal (英) 

『**文字列リテラルとは、プログラミング言語などにおいて記述される、0文字以上の連続した文字列を示す定数のこと。**』


以前記事にした内容と若干かぶりますが、Pythonのプログラムの中で文字列を定義する場合の、複数の文字の集まりをシングルクォーテーション (')、またはダブルクォーテーション (")で囲むことだったんですね。
以前から文字列リテラルを定義していたというわけですよ。

###文字列の中でシングルクォーテーション、またはダブルクォーテーションを使う時は？

文字としてシングルクォーテーション (')、またはダブルクォーテーション (")を使う場合は注意が必要です。

例えば、「Bob's toy」のようなシングルクォーテーションを含む文字列リテラルを下記のように定義すると、最初と2番目のシングルクォーテーションで囲まれた「'Bob'」は文字列リテラルと判断されるが、「s toy'」が構文的に間違った記述となるため、結果エラーとなる。

```py:script.py
print('Bob's toy')
```
このように文字列の中で文字としてシングルクォーテーションを使いたい場合は、ダブルクォーテーションで文字列を囲みます。

```py:script.py
print("Bob's toy")
```

逆に文字列の中で文字としてダブルクォーテーションを使いたい場合は、シングルクォーテーションで文字列を囲む。

```py:script.py
print('私は彼女に"おはよう"と言いました')
```

なお、エスケープシーケンスを使用する方法もありますが、別記事として投稿したいと思います。

* * *

参考記事
[文字列リテラルを記述する](https://www.javadrive.jp/python/string/index1.html)
プログラミングの基礎の基礎まとめpart1

#プログラミングとは？
「コンピューターを動かすにはプログラムが必要」をもっとわかりやすく解説出来る人はどれくらいいるんでしょうか？

####コンピューターって何？
→計算機

####プログラムって何？
コンピューター（計算機）が何をしたらいいのか細かく書いた指示書

#####コンピュータはプログラムに書いてある仕事を忠実に行う機械

##プログラムには何がかかれているのか？
「他の解釈の余地がないところまでかみ砕いた指示」です。

人間であれば
「それを貸して」
と声をかけられたときに

**その人の前にはメモ用紙がある
右手でボールペンを指している
・・・という事はそれはボールペンである**

と考えることできる
しかしコンピューターは「それ」がわからないのである。
だからコンピューターには

**机の上にボールペンがある
ボールペンをとって
私の前に置きなさい**

と指示しなければならないコンピューターには理解してもらえないのです。


##プログラムを書くコツ
いざプログラムを書こうと思ってもどこから手をつけていいか分からない経験をした事ってありませんか？
その時には
###コンピューターにしてほしいことを考える
###プログラミング言語に翻訳する
と2段階で考えるとかなりハードルが下がると思います。
この2つを一緒にやろうとすると頭の中がゴチャゴチャになって脳がエラーを起こすと思います笑



#コンピューターが動く仕組み
コンピューターはハードウェアとソフトウェアの二つが揃って初めて動く

###ハードウェアとは
→パソコン本体やモニター、キーボード、マウス
（実際に触れる事が出来る物たち）

###ソフトウェアとは
→コンピューター内のデータ、僕たちが書こうとしているプログラム
（実際には触れる事が出来ない物たち）

##コンピューターが出来る事
たくさんありそうですが、実際には3つの仕事しかできない
**入力
演算
出力**
だけなんです。

だから例えばサイトを開く時も

**1．サイトのリンクがクリックされた情報を受け取り...入力
2．そのサイトリンクを読み込み...演算
3．データベースから引っ張てきてモニターに表示する...出力**

という流れなのです


##プログラミングの7つのステップ

**l.テーマを決める→何を作るのか
2.あらすじを作る→そのために必要なことを洗い出す
3.シナリオをかく→何をどの順番でどうするか
4.プログラミング言語に翻訳する→日本語をプログラミング言語に翻訳する
5.動かしてみる→実際に動かしてみる
6.確認する→最初に決めたとおりのテーマの通りにプログラムが動く確認
7.バージョンアップ→1~6を繰り返して新しいものを追加**

いざプログラム書こうと思ってもかけなかった人は1~3が欠けていたのではないでしょうか？
どんな凄腕エンジニアになっても1~3を飛ばして書くのは不可能です（笑）

だからこそ因数分解をして1つずつ確実にこなしていくのです。
RDBとmongoDBの用語比較

NoSQLのMongoDBについて調査を行った際、データベースを指し示す用語でわからない物があった。
そのため、RDBと関連付けて整理を行い、記憶定着を促す。
(なお、厳密には異なるらしいです)

MongoDBとRDBの用語比較

| MongoDB | RDB |
|:-:|:-:|
|データベース|	データベース|
|コレクション|	テーブル|
|ドキュメント|	行（レコード）|
|フィールド|	列|
プログラミングは神になれる。

#プログラミングの楽しさについて書きます。

####それはオブジェクト思考がまるで生き物を作る神になった気分になれるところです。
そのコードの例が下記になります。

```
class Human
  @name
  @age
  @wanryoku

  def walk
  end

  def eat
  end

  def age= num
    if num < 0
      puts 
      @age = 0
      return 
    end
    @age = num

  end

end

class SuperMan < Human
  def me_kara_beam
  end
end


human = Human.new
human.walk #ok
human.me_kara_beam #error
spMan = SuperMan.new
spMan.walk #ok
spMan.me_kara_beam #ok
```

これは、ヒューマンは歩くことができるが、目からビームが出せない
スーパーマン(spMan)は歩くこと、目からビームを出すことができる
という処理です。

一見難しそうに見えるプログラミングも、こうみると面白いのかなとおもったり。

日々の学習をドキュメントに残してるので、とっても暇な時みてみてください。
https://www.notion.so/Ruby-Ruby-on-Rails-84f5c88189474fa4aae5e00cba856226
【Swift】型の構成要素〜型のネスト〜

#型のネスト とは

Swiftでは、型の中に型を定義することができ、それを**型のネスト**と言います。

ネストされた型はネストする型の名前を引き継ぐことになるため、
**型名をより明確かつ簡潔にできるというメリットがあります。**


##定義方法
型のネストをするには、型の定義の中に型を定義します。

例として以下のようなコードがあったとします。

スポーツをやっていた人のステータスを表すSports型と、
そのスポーツの種類を表すSportsKind型が存在したとします。

```swift

enum SportsKind {
    case tennis
    case soccer
    case baseball
}

struct Sports {
    var name: String
    var history: Int
    let kind: SportsKind
}
```

この場合、SportsKind型は、Sports型の種類を表していることは推測できますが、
命名で縛っているにすぎません。

次のサンプルコードのように
Sports型の中にSportsKind型をネストしKindにリネームすると、
Sports.Kind型となります。

**Sports.Kintd型は、SportsKind型と比べると、
Sports型との関連性が取り明確になっています。**

また、`let kind: Kind`を見てわかるように、
Sports.Kind型はSports型の内部ではKind型として参照できるため
型名もより簡潔に記述できるようになりました。


```swift

struct Sports {
    enum Kind {
        case tennis
        case soccer
        case baseball
    }
    
    var name: String
    var history: Int
    let kind: Kind   
    
    init(name: String, kind: Kind, history: Int) {
        self.name = name
        self.kind = kind
        self.history = history
    }
    
    func status() {
        // "\n" は改行を表しています。
        print("名前：\(name)\nスポーツ：\(kind)\n経験歴：\(history)年")
    }
}

let humanAKind = Sports.Kind.baseball
let humanBKind = Sports.Kind.soccer
let humanA = Sports(name: "坂本", kind: humanAKind, history: 30)
let humanB = Sports(name: "本田", kind: humanBKind, history: 25)

humanA.status()
print("---")
humanB.status()

実行結果
名前：坂本
スポーツ：baseball
経験歴：30年
---
名前：本田
スポーツ：soccer
経験歴：25年
```

以上で型のネストについての説明を終了します！

型の中に型を入れるだけなの記事のボリュームは小さいですが、
型のネストは結構重要な内容です。

余談ですが**String型やInt型も型の中にさらに型が存在します。**

そのくらい色々なところで当たり前に使われている機能なので、
ぜひ使えるようになってください！

他の記事でも型の構成要素について記載しているものがあるので、ぜひそちらもご覧ください。

・[【Swift】型の構成要素〜型の基本〜](https://qiita.com/onishiApp/items/759c96bdbaebb2303a94)
・[【Swift】型の構成要素〜プロパティ前編〜](https://qiita.com/onishiApp/items/db19c212ce6eeb42ef9e)
・[【Swift】型の構成要素〜イニシャライザ〜](https://qiita.com/onishiApp/items/78a0bd93708384b5e487)
・[【Swift】型の構成要素〜メソッド〜](https://qiita.com/onishiApp/items/dfe5be6ed494ceae1da0)
・[【Swift】型の構成要素〜サブスクリプト〜](https://qiita.com/onishiApp/items/281daff3d84c13bdd586)
・[【Swift】型の構成要素〜エクステンション〜](https://qiita.com/onishiApp/items/c7e407fc6aa6e922a7da)

最後までご覧いただきありがとうございました。
PDF注釈のメモ

PDFを使った校正の際のフィルターについてのメモ

# PDFの注釈をフィルターする
じょうごマークをクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/c4460160-9a3c-53e0-52f3-e27fc707daf6.png)


## 注釈記入者、タイプ、カラーで選択可能

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/c026f15e-988b-afcf-4b41-7e11157724e4.png)

### カラーの選択
テキストの置換などの際に、右上●で色が選べます。変更漏れが起きやすいのだけれど、例えば提出後の修正について色を変えておくと、フィルターで追加の修正が見つけやすくなるといった使い方が出来そうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/9b702e8d-6397-a42c-f516-02594102d4ba.png)
良いことがあったら花火を上げるスラックのスラッシュコマンドを作った話

# 背景
みらい翻訳ではコミュニケーションツールのslackを使っており、様々なbotが動いています。
私も商用サービスの契約数やDAUの情報を投稿するbotを作成して運用しています。
このbotは毎日の契約数を淡々と投稿するだけで、あまり面白味もないので、
**大きな契約など嬉しいことがあった時に、速報として花火が打ち上げある機能があると良い**
と思ったので、それを実装しました！


# 実装までの段階
1. slackbotを導入（slashコマンドを使えるように）
2. Google Apps Scriptと連携
3. Google Apps Scriptの内容を花火を打ち上げるものに変更（今回はこちらを説明）


1., 2.については
https://qiita.com/zama_8722/items/5893d968f120dad90f11
こちらの通りに実装すれば問題ないと思います。


# 3. Google Apps Scriptの内容を花火を打ち上げるものに変更

まずは花火の画像を用意します。
今回は[PAKUTASO](https://www.pakutaso.com/userpolicy.html)の素材を使わせていただきます。
GASに素材URLの画像を書き込むのはダサいので、google spread sheetに素材のURLを入れていおいて、その中からランダムに選ぶようにします。
スプレッドシートの中身は以下のようにしておきます。
<img width="400" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196857/ab2186e8-70af-c5d4-8cac-16b3b89f8975.png">


GASの中身を以下のようにすることでうまく動きます。

```javascript
function doPost(e) {
  // スプレッドシートからの情報取得
  var url = "https://docs.google.com/spreadsheets/d/************/edit";
  var spreadsheet = SpreadsheetApp.openByUrl(url);
  var sheets = spreadsheet.getSheets();
  var sheet = sheets[0]; //シート複数ある場合には変更する必要があります
  var url_num = sheet.getLastRow();
  var row_num = Math.floor(Math.random()*(url_num))+1; //ランダムで選びます
  var sheetdata = sheet.getSheetValues(row_num, 1, row_num, 1)[0][0]; //A列に入っているURLのうちをrow_num行のものを読み込み

  // slack投稿する内容
  post_json = {
    "response_type":'in_channel',
    "text": "<@"+e["parameters"]["user_id"]+">" + "から嬉しいお知らせがあるようです！",
    "attachments": [{
        "image_url": sheetdata
     }]
  };

  out = ContentService.createTextOutput();
  out.setMimeType(ContentService.MimeType.JSON);
  out.setContent(JSON.stringify(post_json));
  return out
}
```
jsonの中身で`"response_type":"in_channel"`としておかないと、スラッシュコマンドを打った人にしか見えないので注意が必要です。[^1]

また、最後の4行もQiita内に良い解説[^2]がありますのでご参照ください。


これによって見事に花火が上がるようになりました！

<img width="400" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/196857/dc528b95-1492-c412-fdfa-515c16b457d7.png">



[^1]: https://qiita.com/nyata/items/d2f4f648103ef41abbe5
[^2]: https://qiita.com/riversun/items/c924cfe70e16ee3fe3ba#web-api%E3%82%92%E3%81%A4%E3%81%8F%E3%82%8B
discord.pyでmembersが取れるようにする

discord.pyではguild.membersでメンバー一覧を取得できます

```
#メンバー一覧を表示
mes = '\n'.join([m.name for m in message.guild.members])
await message.channel.send(mes)
```

しかし、これでは自分自身のbot以外が表示されないこともあります。

discord.pyのバージョンアップに伴い、
メンバーの一覧を取得するにはオプションが必要になりました。

具体的にdiscord.Client()時に以下のようにするといいです。

```
# 接続に必要なオブジェクトを生成
intents = discord.Intents.default()  # デフォルトのIntentsオブジェクトを生成
intents.typing = False  # typingを受け取らないように
intents.members = True  # membersを受け取る
client = discord.Client(intents=intents)
```

typingは「メッセージを入力中です」を受け取らない設定です
多分、普通はいらないはずです
.pyファイルをエクスプローラーのプレビューに表示させる

この記事はTakumi Akashiro ひとり Advent Calendar 2020の15日目の記事です。

「Photoshopエクステンションを作ってみる」を投稿する予定でしたが、
長らくデバッグモードの環境でエクステンションを動かしていたため、
投稿直前に通常環境で動かないことに気付いて、キャンセルしました。

代打として、Windowsのプレビューについて書きます。
レジストリいじるネタなので自己責任でお願いしますね。

## 始めに

みなさまはエクスプローラーのプレビューって使いますか？

自分は……あんまり使ってないですが、たまに欲しくなる瞬間がありますね!


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294868/26128e6e-67cc-76ab-72cd-a7f64a4ff9a6.png)

何のスクリプトやねん……
まあVSCodeでフォルダごと開けばいいんですけど、それすら面倒……

ってときに役に立つのが今日の記事です！

テキストプレビューハンドラーを使って.pyを表示します。

## Pythonファイルにテキストプレビューハンドラーを紐づける

1. まずは、`regedit`を開き、`HKEY_CLASSES_ROOT\.py` を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294868/6cbd3937-a5bb-bed4-61b8-4630df7fa711.png)

2. Python.Fileがセットされてるので、`HKEY_CLASSES_ROOT\Python.File` に飛びます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294868/4d9109a0-9171-bf24-9388-afb2c6acaa21.png)

3. shellExを右クリック > 新規 > キー で作成して、名前に`{8895b1c6-b41f-4c1c-a562-0d564250836f}`をセット。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294868/158a0e47-d0e8-6562-824c-9fd446f1e501.png)


4. 値に.txtで使ってるプレビューハンドラーの`{1531d583-8375-4d3f-b5fb-d23bbd169f22}`をセット。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294868/87d3f191-2a68-f194-075c-668a55a97ebc.png)

PCを再起動すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/294868/ee9c1f18-b7ae-4a55-1c59-912cbe5809e2.png)

わー.pyファイルがプレビュー表示されましたねー
って何も書いてないじゃないか！！ダミーかよ！

## 締め

コンビニでアイスを買ったときについてくるスプーンぐらいには便利ですね。

.xlsxとかプレビューしたくない奴は、ハンドラーを外すと
プレビューが表示されなくなり、エクスプローラーがフリーズすることもなくなるので、
そちらは本当におすすめです。
育児とリモート飲み会を両立する在宅ワークのコツ10個

[アドベントカレンダーではありますまいか Advent Calendar 2020](https://qiita.com/advent-calendar/2020/mba) 15日め。[ママエンジニア1年生が復職の心構えや仲間への感謝を綴ってみた](https://engineer.retty.me/entry/2020/12/14/000000) を読んだ。ので、タイトルはまともにしてみた。

まずこの記事への動機を書こう。「子どもの寝かしつけをしたあとに飲み会に参加する」。1年前はありえなかった。でも昨日それが出来た。上の「ママエンジニア1年生」の記事では

- 産休・育休から復職する人
- 産休・育休から復職する人がいるチームの方々
- 人事の方

に向けて感謝を綴っていたので、4歳2歳を抱える育休復帰2年目、コロナ1年目、私も初心に戻って綴ってみたい。

## 最初に結論

**着替えろ。散歩しろ。働きすぎるな。**

私は結構真剣に、この「コロナ禍」は、IT業界・ソフトウェア業界が今こそ社会を変えるために働くときだと思っている。

医療従事者に感謝を！とか、そんなことは当たり前なのだけれど、目に見えない感謝よりも見える形で社会を良い方向に変える責任が、IT屋さんにはあるとおもう。なぜなら我々こそ在宅ワークにして最大限仕事を達成できるはずの分野の者だから。

弊社の周りに限った話ではあるが、コロナ禍における最大の「恩恵」は皆が「等しく」在宅ワークになって、育児をする者もそうでない者も「等しい条件下」で働けるようになったことだと思っている。本当にありがたい。以下コツを共有することにより今後も業務改善に活かしたいです。


## 在宅ワークで良いこと、続けたいこと
- インプットの時間、アウトプットの時間を、それなりに取れるようになった。
- 集中して作業できるようになった。
- 何より、表題のような、「育児とリモート飲み会」のようなアクロバティックなタイムスケジュールも物理を超えることで達成できる。このチームビルディングに参加できることの有り難さは「育児クラスタエンジニア」には相当心強い（個人の感想だが）。

## 在宅ワークのコツ10個

1. 論理出勤時間、論理退勤時間を宣言する。
1. アウトプットを意識する。
1. 何でもドキュメントに残す。
1. Slackを使いこなす。分報（times）に残す。[分報](https://c16e.com/1511101558/) は便利。
1. 機材を整える。金で解決できるストレスは省く。
1. オンラインで繋がるくふうをする。ウェブ会議に慣れる。相槌を5割増し以上に打つ。
1. 着替える。
1. 散歩する。
1. 働き過ぎない。
1. メリットだけでなく、デメリットもあることを承知する。どうしてもにこだわらない。

以下、詳細。

### 論理出勤時間、論理退勤時間を宣言する
気持ちのスイッチ的要素が強いが、「仕事モードに入りましたよ」、「もう上がりますよ」の宣言。
子どもの世話に専念するためにも、もちろん仕事に専念するためにも、Slackに一言。

### アウトプットを意識する
ソースの形でも。チケットの形でも。ツールで管理できることはともかくする。

### 何でもドキュメントに残す
成果を出したことの記録ももちろんであるし、出来なかったことをどうして出来なかったかを記録することも意識する。

### Slackを使いこなす
分報（times）に残す。[分報](https://c16e.com/1511101558/) は便利。
しょうもないことで良いと思う。このくらい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/e9a80aab-7a16-befe-8864-19ab018574a3.png)

### 機材を整える
金で解決できるストレスは省く。
取り除けるだけのストレスは取り除く。室温や湿度なども。

### オンラインで繋がるくふうをする
ウェブ会議に慣れる。相槌を5割増し以上に打つ。
開始時間にさっと現れ、できるなら雑談から始められるくらいになりたい。

### 着替える
そのままだが、人間として、着替える。

### 散歩する
運動する。太陽に当たり、外の空気を吸う。

### 働き過ぎない
子どもとの時間をとる。

### メリットだけでなく、デメリットもあることを承知する
どうしてもにこだわらない。
「銀の弾丸はない」という奴かもしれない、と承知する。

## 良くないこと、どうにかしたいこと
もちろん以上を持ってして、まだまだ改善したいことはある。
周りのメンバーと「話そうよ」と言わないと話すタイミングがない、等 → 引き続き模索したい。

## 2020.12 最近の時間割
以下、意外と「普通」なことがわかるだろうか。
### 平日 月～金
6:00- 起きる、朝飯、洗濯、ごみ捨て、着替える
8:00- ★娘と息子を保育園に送る or 仕事開始
9:00-12:00 帰宅してお仕事
12:00-12:30 昼飯
12:30-17:00 お仕事
17:00-18:00 ★夕飯したくしつつ仕事 or 娘と息子を保育園にお迎え
18:00 夕飯、風呂掃除
19:00 軽めにお仕事
20:00 風呂、寝かしつけ
21:00- 自由時間、インプット時間、アウトプット時間。寝落ちしたり、その寝起きの勢いでいろいろしたり。

＊星印は曜日により夫とシフト制

### 参考：2019年はこうだった

5:00 ★起床
6:00 朝ごはん準備
6:30 おむつ替えたり保湿剤塗ったり
7:00 朝ごはん
7:15 洗濯物干したり保育園連絡帳書いたり
7:30 着替えさせて着替えたり
8:00 出発
8:30 長女預け
9:15 会社着、長男預け
11:00, 15:00 ★保湿剤塗ったり
17:00 退社
18:00 晩ごはん
19:00 お風呂準備してお風呂
20:00 寝かす
21:00 自分も寝落ちしかけたり自分は起きたり
0:00, 4:00 ★夜泣き対応

＊星印はどう考えても仕事に影響していたタスク。

## まとめ

春頃こんなことを書いていた。「緊急事態宣言」下の話。
[2020年、（3歳1歳と）私のリモートワーク](https://qiita.com/e99h2121/items/c8878d027603745a6426)

当時はとりわけ「子育て」に軸足を置くことにも四苦八苦だったが、どうにか年末。在宅ワークにも慣れ、冒頭のように「育児とリモート飲み会」という次元を超えた2つを同時にこなせるようになった2020年。「ほどほどに」を良い意味で意識しつつ引き続き定期的に振り返って、会社のインフラなどなどにも感謝の気持ちを伝えていきたいです。

書き忘れましたが最後に。平日21時以降はいつでもリモートで忘年会参加できます。今日は以上！
フォルダ内のPDFファイル群から文字列をor検索する


原稿がPDFでゲラになって挙がってきたけど、章ごとにファイルが分かれていたので表記ゆれの対応が面倒だった。調べてみると、フォルダーを検索対象として、or検索ができることが分かったのでメモ

- フォルダー内のpdfを一括検索（サブフォルダを含む）
- 複数の単語を一括検索

フォルダにファイルを追加すると、追加したファイルが検索されないことがある。その際は、フォルダーを改めて選択すると検索されるようになる。

# Acrobat 検索パネルを開く

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/d65d8fc0-448a-340a-53bf-8d06ff114d1f.png)

# 詳細オプションを表示
パネルが開いたら「詳細検索オプション」を選ぶ。これを選ばないとor検索ができない模様

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/400e3ecd-37cf-e643-cb3a-a561d2ccd5c3.png)

# フォルダーを選択
フォルダーを「参照」から選ぶ
「注釈を含める」を選ぶと注釈も検索してくれる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/55940d75-62ca-f983-9911-fa6070c43eca.png)

# or検索　いづれかの語が一致するものを検索
「いづれかの語が一致するものを検索」を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/068c1631-6532-6c88-4df1-3409001198ff.png)

# 検索文字列の入力
or検索する場合は、検索文字列を[半角スペース]で区切ること。検索できないと思ったら[全角スペース]になっていないかを確認する
"検索"ボダンで検索開始
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/c3fd10c8-52bd-045f-b6b7-c6439d942e4b.png)

# 検索結果
ファイル名をクリックすることで、見つかった単語に展開
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/3fcba31a-6e02-7480-e7aa-187822158ee5.png)

単語を選ぶと、該当するpdfファイルが開き、該当する場所にカーソルが移動する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/1f12dd14-8f63-6cbe-8426-4f1b9e6196d6.png)

# 検索のTips

## 表示の順番
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/b325c0a7-a41d-2347-75ae-ef188fbfde69.png)

## ファイルパスの表示
やってみたけど、ちょっと不明
またの機会に
【Swift】型の構成要素〜エクステンション〜

#エクステンションとは
エクステンションは、**すでに存在している型に要素を追加し、型を拡張すること**を言います。
追加できる要素としては、プロパティやメソッド、イニシャライザなどが挙げられます。

##定義方法
エクステンションは`extensionキーワード`で宣言することができ、
{ }内に型を構成する要素を定義します。

```swift

extesion エクステンションを定義する対象の型 {
   対象の型に追加したい要素
}
```

###メソッドの追加
エクステンションで追加したメソッドは通常のメソッドと同様に使用できます。

次のサンプルコードでは、標準ライブラリにある`String型`を拡張しています。

```swift

extension String {
    func printSelf() {
        print(self)
    }
}

let string = "abc"
string.printSelf()

実行結果
abc
```

###コンピューテッドプロパティの追加

エクステンションでは、ストアドプロパティは追加することができませんが、
コンピューテッドプロパティなら追加することができます。

コンピューテッドプロパティを追加すれば、
**アプリケーション内で既存の型に対して頻繁に行われる処理を型自身に定義できます。**

サンプルコードでは、
String型に「」(鍵括弧)で囲んだ値を返すコンピューテッドプロパティを作成しました。

```swift

extension String {
    var kakko: String {
        return "「\(self)」"
    }
}

let title = "吾輩は猫である".kakko
print(title)

実行結果
「吾輩は猫である」
```

###イニシャライザの追加

エクステンションではイニシャライザを追加することも可能です。

**既存の型にイニシャライザを追加することで、
アプリケーション固有の情報から既存の型のインスタンスを生成することも可能になります。**

サンプルコードでBook型を作成しました。
このBook型は、イニシャライザで本のタイトルと登場キャラを引数に指定しています。

`let book1 = Book(title: "NARUTO", char: "日向ヒナタ")`が
既存のBook型でインスタンス化した処理になります。
(ヒナタが好きなのでヒナタにしました。サクラじゃなくてすみません。)

インスタンス化された際にヒナタはイニシャライザでプロパティcharactersに追加されました。
なので`print(book1.characters)`の結果が`["日向ヒナタ"]`になります。

ですが、通常のインスタンス化ではキャラクターを一人しか追加できず面倒なので、
一気に複数人のキャラクターを追加したくなると思います。

そういった場合に、エクステンションでイニシャライザを追加します。
`init(title: String, charArray: [String])`が新しく追加したイニシャライザです。

こちらは、既存のイニシャライザと違い、
Array\<String>型、つまり、String型の配列を引数に指定しています。

なので、事前に配列を作成しそれを引数に渡します。

配列の定義
`var charArray1: [String] = ["ルフィ", "ゾロ", "サンジ", "ウソップ"]`

インスタンス化
`let book2 = Book(title: "ONE PIECE", charArray: charArray1)`

出力結果
`["ルフィ", "ゾロ", "サンジ", "ウソップ"]`

```swift

struct Book {
    var title: String
    var characters: [String] = []
    
    init(title: String, char: String) {
        self.title = title
        self.characters.append(char)
    }
}

extension Book {
    init(title: String, charArray: [String]) {
        self.title = title
        self.characters = charArray
    }
}

var charArray1: [String] = ["ルフィ", "ゾロ", "サンジ", "ウソップ"]
var charArray2: [String] = ["竈門炭治郎", "竈門禰豆子", "冨岡義勇", "煉󠄁獄杏寿郎"]

// 通常のイニシャライザを使用
let book1 = Book(title: "NARUTO", char: "日向ヒナタ")
//エクステンションで追加されたイニシャライザを使用
let book2 = Book(title: "ONE PIECE", charArray: charArray1)
let book3 = Book(title: "鬼滅の刃", charArray: charArray2)

print(book1.characters)
print(book2.characters)
print(book3.characters)

実行結果
["日向ヒナタ"]
["ルフィ", "ゾロ", "サンジ", "ウソップ"]
["竈門炭治郎", "竈門禰豆子", "冨岡義勇", "煉󠄁獄杏寿郎"]
```


以上がエクステンションの説明のなります！

すぐに思いつく簡単なエクステンションの例はこれでしたので、
あまりいい機能を実装できていませんが許してください。

後、ヒナタじゃなくてサクラファンの方も許してください・・・。



他にも、型の構成要素の記事がございますのでぜひご覧ください。

・[【Swift】型の構成要素〜型の基本〜](https://qiita.com/onishiApp/items/759c96bdbaebb2303a94)
・[【Swift】型の構成要素〜プロパティ前編〜](https://qiita.com/onishiApp/items/db19c212ce6eeb42ef9e)
・[【Swift】型の構成要素〜イニシャライザ〜](https://qiita.com/onishiApp/items/78a0bd93708384b5e487)
・[【Swift】型の構成要素〜メソッド〜](https://qiita.com/onishiApp/items/dfe5be6ed494ceae1da0)
・[【Swift】型の構成要素〜サブスクリプト〜](https://qiita.com/onishiApp/items/281daff3d84c13bdd586)
・[【Swift】型の構成要素〜型のネスト〜](https://qiita.com/onishiApp/items/040019f1a00826b5c9e5)

最後までご覧いただきありがとうございました。
Rust のソースファイルを分割する、いちばん簡単な方法

ファイルの分割方法が分からなくて Rust に挫折してましたが、調べたら簡単でした。

追記: こちらの記事 [Clear explanation of Rust’s module system](http://www.sheshbabu.com/posts/rust-module-system/) が分かりやすいです。もっと早く読みたかった。

## やり方

[こちらの issue](https://github.com/rust-lang/book/issues/328#issuecomment-260378152) より、

```main.rs
mod hello;

fn main() {
    hello::print_hello();
}
```

```hello.rs
pub fn print_hello() {
    println!("Hello, world!");
}
```

たったこれだけ。ポイントは、
- 分けたファイルは自動的にモジュールになる
- main.rs に `mod モジュール名;` を列挙する
- 他のモジュールから参照したいものには pub を付ける
- 参照する側は `モジュール名::識別子` とするか、use を使う

特に、lib.rs は不要です。(作りたければ作ってもいいけど。)

## 愚痴

プログラミング (または新しい言語) を勉強する時って、
1. とりあえず一つのファイルに全部書く
2. 適当にファイルを分割してみる
3. 再利用できそうなものをライブラリ化する
4. 最初からライブラリとして設計する
という順番がある (かつ、すべての人が 3 や 4 をやる必要はない) と思うのですが、Rust のドキュメントは 2 をすっ飛ばして、いきなり 3 や 4 に進んでしまう印象…。

(最初「ドキュメントや書籍は〜」と書こうとして、[実践 Rust 入門](https://gihyo.jp/book/2019/978-4-297-10559-4) を確認したらちゃんと書いてありました。が、一つのファイルを複数のモジュールに分ける方がメインになってたので、すっかり見落としてて、ずっと悩んでました。)
CDK for Terraformをやってみた

# 概要
最近インフラに興味が湧いてきたので、terrafromを使って遊んでいました。
すると「CDK for Terraform」なるものを発見👀

AWS-CDKを使ってTypeScriptやPythonなどでTerraformを扱えるとのことなので、さっそく試して見ました！

> ※注意: まだ開発中であり、アルファテスト段階にあるソフトウェアが含まれています。本番環境には使用しないでください🙇‍♂️

[hashicorp/terraform-cdk](https://github.com/hashicorp/terraform-cdk)
[Write Terraform with Typescript and Python](https://learn.hashicorp.com/collections/terraform/cdktf)
[CDK for Terraform: Enabling Python & TypeScript Support](https://www.hashicorp.com/blog/cdk-for-terraform-enabling-python-and-typescript-support)

# コマンド

```bash
# CLIのインストール
$ npm install -g cdktf-cli

# 作成（プロジェクト配下で）
$ cdktf init --template=typescript

# Terraformのコードを生成
$ cdktf synth

# リソースの生成
$ cdktf deploy

# リソースの削除
$ cdktf destroy
```

# S3を作成してみた
以下のコードで簡単に作成できました。

```ts

import { Construct } from 'constructs';
import { App, TerraformStack } from 'cdktf';
import { AwsProvider, S3Bucket } from './.gen/providers/aws'

class MyStack extends TerraformStack {
  constructor(scope: Construct, name: string) {
    super(scope, name);

    new AwsProvider(this, 'aws', {
      region: 'ap-northeast-1'
    });

    // S3 bucket
    new S3Bucket(this, "my_bucket", {
      bucket: 's3-example.com',
      acl: "private"
    })
  }
}

const app = new App();
new MyStack(app, 'aws');
app.synth();
```

```bash
# Terraformのコードを生成
$ cdktf synth

# リソースの生成
$ cdktf deploy
```

# 感想
HCL言語だとオプションがいまいちわからなかったんですが、TypeScriptを用いることでd.tsファイルである程度何が必要で何が使えるかなどがわかりやすかったです。

# さいごに
続きまして、アルサーガパートナーズ Advent Calendar 2020 16日目の記事は、@miumi さんです :tada:
Rubyの8進数と2進数の構文エラー文の違い

# 最初に

[Ruby その2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/ruby2)の16日目の記事です。
昨日は、 shxun6934さんの[他言語の視点で見るRuby](https://qiita.com/shxun6934/items/929e73d08bed6d0b4384)でした。
Part2の方はガラ空きなので、せっかくなので記事を投稿する。

なにか役立つような話ではなくて、Rubyの小話。
ただ、`0o`や`0`始まりは8進数というのは、Rubyに限らずメジャーなプログラミング言語で多い仕様なので知らなかった方は知っておくといいことがありそう。
なお、最近の言語ほど、`0o`始まりのみ許して、`0`始まりは構文エラーにしている印象。

# 8進数と2進数のエラー文の話

## 前提として、2進数や8進数のリテラル

`0b`始まりの数値リテラルは、2進数として解釈し数値を作る。
`0o`、`0`始まりの数値リテラルは、8進数として解釈し数値を作る。
`0x`始まりの数値リテラルは、16進数として解釈し数値を作る。

```rb
p 10   #=> 10
p 0b10 #=>  2
p 0o10 #=>  8
p 010  #=>  8
p 0x10 #=> 16
```

表にまとめると、次のような感じ。

|prefix|n進数|英語|その他|
|:---|:---|:---|:---|
|`0b`|2進数|binary||
|`0o`|8進数|octal|単なる`0`始まりは8進数|
|`0d`|10進数|decimal|`1`〜`9`始まりは10進数|
|`0x`|16進数|hexadecimal||

なお、prefixは大文字でも同じように動く。

## 8進数の構文エラー

8進数は、0～7の8つで構成される数だから、それ以外の8や9があると8進数として解釈できず構文エラーになる。
(※`syntax error`と表示されずわかりにくいが、他のコードを前に書いても実行されておらず、確かに構文エラーである。)

```rb
p 0190
# Main.rb:1: Invalid octal digit
# p 0190
#   ^~~
```

`Invalid octal digit`(無効な8進数)と丁寧に教えてくれる。
しかし、これを2進数で同じようにやると、エラー文がよくわからなくなる。

## 2進数の構文エラー

2進数に0と1以外の数があれば、8進数と同じように
`Invalid binary digit`と表示されて欲しい。

```rb
p 0b12
# Main.rb:1: syntax error, unexpected integer literal, expecting end-of-input
# p 0b12
#      ^
```

試してみると、構文エラーになっているところまでは同じ。
しかし、`unexpected integer literal`(予期せぬ整数値リテラルだ)と怒られる。
よく見ると、エラー文の数値を指す位置も8進数のときと異なる。
8進数が指しているエラーの位置は、最初のprefixの`0`である。
対して、2進数が指しているエラーの位置は`2`である。
どうも8進数と異なり、2進数の場合は`0b1`までで2進数とちゃんと解釈された上で、
その数の真隣に別の整数値が来て構文が成り立たないというエラーらしい。

では、`b`の真隣に`2`という数を持ってきたらどうだろう。
`0b`でも`0b2`でも、2進数になりえない。さっきのエラー文とは違ってきそうである。

```rb
p 0b2
# Main.rb:1: numeric literal without digits
# p 0b2
#   ^~
# Main.rb:1: syntax error, unexpected integer literal, expecting end-of-input
# p 0b2
#     ^
```

`0b`と`2`を別々に解釈して、前者`0b`を`numeric literal without digits`(数字のない数値リテラル)としてエラーをだして、前者と後者が不自然に連続していることに対して｢(`2`は)予期せぬ数値リテラル｣とエラー文をだしているようだ。

8進数は単純なエラーだったのに、2進数リテラルのエラー文はわかりにくい。

## なぜ8進数のエラーは親切なのか

Ruby1.8を実行すると`090`で既に｢Illegal octal digit｣というエラー文がでる。
そして、1.9になると｢Invalid octal digit｣と今の言葉に変化した。
このあたりは昔から親切なエラー文だったようだ。

8進数と他の数値リテラルのエラー文の違いは何なのか。
自分なりに好意的に解釈して推測すると、以下の通りである。
8進数だけ英字なしの`0`始まりで数値を組める。
そして、電話番号や時刻に対して数値を使い10進数のつもりで`0`始まりの8進数リテラルで書いてしまうケースがあり、意図せずに8進数として使われたケースで誤りに気がつきやすいように8進数だけ親切にエラーを教えるようにしたのではないか。

しかし、2進数などのエラーはレアケースかもしれないが、それでもエラー文の内容はわかりにくいし頑張って紐解かないと分からないレベル。自分に直接的な害はないけど、気になってしまう。

# 最後に

8進数のエラー文は親切できっとググれば解決しやすい一方で、逆にいえば2進数などは何を言いたいかすぐにわかるレベルではないだろう。
まあ、人間の作っているものだし、こういう細かいところは手が届いていない感じがある。
理想ではこういう細かいレベルも何とかして欲しいなと感じるのもあり、記事にしてみた。
アドベントカレンダー12日目：SREを読んでみた「分散システムのモニタリング」編②

前回は、モニタリングの必要性や妥当な値とは何かについて、見ていきました。
妥当な値が設定されたモニタリングによって出されるアラートは、ユーザーに混乱を与えず、必要なことを伝え、かつノイズの少ないものになると学びました。
今回は、質の高いアラートを出すための観点から見ていきます。

## 症状と原因
> **モニタリングシステムは、２つの疑問に答えなければなりません。それは、何が壊れたのか、なぜそれが壊れたのか？という疑問です。**<sup>1</sup>

基本的なことかもしれないですが、まずこの観点が重要だと思います。
明確にユーザーに何が原因になっているのかを伝えることが大切です。逆に自分が受けての際は、これに問題があるのだと訴えてきているとしっかり受け取ることが大事だと思いました。
また、認識が間違っていたなと思うことは、エラーが出た際にエラーが原因で動かないというのは、間違っていると思いました。エラーとは、ただの症状です。エラーを見た際に、なぜそのエラーが出ているのかを読みよることが重要だと思いました。

#### 症状と原因の例

| 症状| 原因 |
|:-:|:-:|
| HTTP500もしくは400が返されている  | データベースサーバーの接続拒否  |
| レスポンス速度の低下  | CPUに負荷がかかっているなど  |

また、上の例でレスポンス速度が低下したことの原因をCPUに負荷がかかっているとしましたが、これも直接的な原因ではないです。本当の原因は、アクセスが増えたからであり、アクセスが増えたのは、人気のコンテンツが掲載されたからであり、人気のコンテンツになったのは、メディアで取り上げられたからであり、。。。

## ブラックボックスとホワイトボックス
モニタリングの方法には、ブラックボックスモニタリングとホワイトボックスモニタリングの2種類が存在します。
ホワイトボックスは、ログやHTTPエンドポイントといったシステム内部の機能を調査することができます。
ブラックボックスは、システム内部の構造が見えず、システムの振る舞いや実際に起きている問題について、表現します。
この二つは、それぞれ利点があり、ホワイトボックスの利点は、システム内部を直接みることができるので、内部に隠されてしまっている問題や近々生じることになりそうな問題を検出できます。
ブラックボックスの利点は、それがユーザー目線のモニタリングになるということです。ユーザーに直接影響を与える問題に密接しているため、良いアラートトリガーになります。

## ４大シグナル
次の４つがモニタリングの際に特に重要なシグナルとなります。
ユーザーが直接利用できるシステムで、メトリクスを４つだけ計測できるなら、この４つに集中すべきです。

#### レイテンシ
リクエストを処理して、レスポンスを返すまでの時間です。
この時、成功したリクエストと失敗したリクエストを区別する必要があります。
例えば、データベースなどのクリティカルな接続が失われたことによるエラー（500エラー）は、きわめて高速で返されます。ここで全体のレイテンシをこれを含んでしまうと正確にレイテンシを計測することができません。
また、低速なエラーと高速なエラーでは、低速なエラーの方が悪いことが多いです。
ユーザーへの重要度（解決の優先度）の為、エラーだけでなく、エラーのレイテンシまで区別する必要があります。

#### トラフィック
システムに対するリクエストの量です。
webサービスの場合、通常毎秒のHTTPリクエスト数で計測されますが、静的なリクエスト、動的なリクエストなど、リクエストの性格によって、区別されます。
それぞれのシステムにあった方法で計測する必要があります。

#### エラー
処理に失敗したリクエストのレートです。
失敗には、さまざまなものがあり、サーバー内でエラーが起きたことによる明示的なエラー（500エラー）や200の成功レスポンスが返されているが、内容に誤りがある場合、またポリシーとして、1秒以上のレスポンスはエラーとした場合、エラーになります。
レスポンスコードがすべての状態を表せるわけではないので、障害の状態を追跡するためには、2次的なプロトコルが必要になる場合があります。

#### サチュレーション
サービスの飽和、サービスがどれだけ「手一杯」になっているかを表します。
最も制約になるリソース（メモリが少ない、CPUの性能が低いなど）に重点を置いて、利用率を計測します。たいていの場合、利用率が100%になる前にパフォーマンスが低下します。なので、負荷試験を行い、レイテンシを計測することで、早い段階でサチュレーションをとらえることができます。

##まとめ
* モニタリングでは、ユーザーへ症状と原因を伝える必要がある。
* 4大シグナル（レイテンシ、トラフィック、エラー、サチュレーション）

#### 参考文献
1. Betsy Seyerほか　SRE サイトリライアビリティエンジニアリング　オライリー　61-63
iOS13対応フルスクリーンモーダル

SwiftUIでiOS13含めて対応する場合、UIViewControllerの`present(_:animated:completion:)`で表示するフルスクリーンモーダルに相当する機能が提供されていません。
~~`UIViewControllerRepresentable`を利用してUIKit側にコントロールを渡して実現出来るように思えますが、`modalPresentationStyle`の設定が効いていないらしく結果に反映されません。~~
[出来ました](https://qiita.com/k-motoyan/items/fc3d8414a081fbe44f5d)、勘違いですみません。。。

このため、フルスクリーンのモーダルを利用したい場合、それに似たようなViewを独自に定義する必要があります。
この投稿では、SwiftUIの機能だけで擬似的にモーダルを表示させていきます。

## モーダルを表示させる領域

単純にモーダルのようなViewを表示させるなら、ZStackにViewが被るように表示させればよいだけですが、
この方法を採用した場合、そのモーダルは親Viewの配下にあるという認識をされるため、Viewのライフサイクルが親Viewに依存することになるので上手い方法ではありません。

親とは別のライフサイクルに乗せるためには`overlay`を利用することになります。

```swift
var body: some View {
    NavigationView {
    }
    .overlay(/* Modal */)
}
```

## モーダル本体を作成する

表示領域が決まったので、今度はモーダルのようにアニメーションして表示されるViewを作成します。

```swift
struct ModalView<Content: View>: View {
    @Binding var isPresented: Bool

    let content: () -> Content

    var body: some View {
        if self.isPresented {
            self.content()
                // 画面を覆うためにスクリーンサイズをframeに設定
                .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
                // デフォルトの背景色を設定
                .background(Color.white.edgesIgnoringSafeArea(.all))
                // 大体0.25秒で本家モーダルのようなアニメーションになる
                .animation(.easeOut(duration: 0.25))
                // 下からせり上がるトランジションを設定
                .transition(.move(edge: .bottom))
        } else {
            EmptyView()
        }
    }
}
```

## 便利に使えるようにViewExtensionを作成する

`overlay`に先程作成した`ModalView`を直接指定しても良いのですが、もう少し使いやすくするためにViewExtensionを作ります。

```swift
extension View {
    func fullScreenModal<Content: View>(isPresented: Binding<Bool>, content: @escaping () -> Content) -> some View {
        self.overlay(ModalView(isPresented: isPresented, content: content))
    }
}
```

## 完成

実際に使うには、NavigationBarの扱い方などで調整が必要になりますが、ひとまずはこれで完成です。

```swift
extension View {
    func fullScreenModal<Content: View>(isPresented: Binding<Bool>, content: @escaping () -> Content) -> some View {
        self.overlay(ModalView(isPresented: isPresented, content: content))
    }
}

struct ModalView<Content: View>: View {
    @Binding var isPresented: Bool

    let content: () -> Content

    var body: some View {
        if self.isPresented {
            self.content()
                .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
                .background(Color.white.edgesIgnoringSafeArea(.all))
                .animation(.easeOut(duration: 0.25))
                .transition(.move(edge: .bottom))
        } else {
            EmptyView()
        }
    }
}

struct MyView: View {
    @State var isModalPresented = false

    var body: some View {
        NavigationView {
            VStack {
                Button("Show Modal") {
                    self.isModalPresented = true
                }
            }
            .fullScreenModal(isPresented: self.$isModalPresented) {
                Text("Modal")
            }
        }
    }
}
```

遷移先の画面からもモーダルが閉じれるようにEnvironmentValueなどを提供出来るようにするとより使いやすくなると思います。
Progate React学習

<h1>Reactとは</h1>
Reactとは、Javascriptのフレームワークの一つです。WebのUI(見た目や機能)をつくることが出来ます。

<h1>Reactの基本的な使い方</h1>
`render`というメソッド内にreturnを記述することでプラウザに文字を表示させることが出来ます。

```react
render(){
  return(
    <h1>Hello World</h1>
  );
 }
```
JSXは、HTMLと同じような記述をすることができます。複数の要素がある場合は、<div>タグで囲んで、1つの要素にまとめます。
JSXを`{/* */}`で囲むと、その部分はコメントになります。

```react
render(){
  return(
   <div>
    <h1>h1です</h1>
    <h2>h2です</h2>
    <p>pです</p>
   </div>
  );
 }

{/*この部分はコメントです*/}
```

imgタグは、HTMLでは、`<img src='画像のURL'>`のように閉じタグが必要ありませんでしたが、一方、JSXでは閉じタグが必要になります。`<img src='画像のURL' />`のように、タグの終わりにスラッシュ`/`を記述します。

```react
render(){
  return(
    <img src="URL"/>
  )
}
```
App.jsは以下の要素で構成されています。

```react
import React from "react";

class App extends React.Component {
 render(){
  return (
      <h1>Hello React</h1>
    );
 }
}
 export default App;
```

<h1>最後に</h1>
洗練さんでインターンの課題(Progate 学習コース　PythonⅠ～Ⅲ)です。
[Progate　React](https://prog-8.com/languages/react)

最後に
現在ここでインターンしています。
まだまだ駆け出しですが頑張ります！やる気は人一倍です！
[洗練](https://senren.work/)
DataPortal: 新機能さかのぼり（レポートのフィールド編集の改善）

DataPortalのまだ知らない使えそうな機能を求めて、新機能の一覧を遡ってみました。

レポートのフィールド編集の改善
> データソースの計算フィールドとパラメータを簡単に編集できるようになりました。レポートの編集中に、[使用可能なフィールド] リスト内のフィールドにカーソルを合わせ、fx アイコンまたは @ アイコンをクリックすると、データソースのそのフィールドを編集できるようになります。

早速試してみます。

### フィールド
元々データソースに存在するフィールドにマウスカーソルを合わせてみます。
カーソルがパーの形となり、ドラッグはできますがクリックはできません。
![スクリーンショット 2020-12-15 1.34.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/4365ff6f-4704-c7f4-dba3-823fc03e6a13.png)

### 計算フィールド
計算フィールドにマウスカーソルを合わせてみます。
人差し指で指す形となり、クリックできるようになっています。
![スクリーンショット 2020-12-15 1.35.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/2c15891f-5a8b-8838-d807-577dfa1755a3.png)
クリックするとそのまま編集できます。
![スクリーンショット 2020-12-15 1.35.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/096c9d25-1eee-5d50-4a60-78d048e6214a.png)

### パラメータ
パラメータにマウスカーソルを合わせてみます。
こちらもクリックできるようになっています。
![スクリーンショット 2020-12-15 1.36.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/d51fa512-1e11-7ce7-ac7c-a32306763bd8.png)
クリックするとそのまま編集できます。
![スクリーンショット 2020-12-15 1.36.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423506/31ec06f4-af39-04f5-71e1-d3eac59b510d.png)


従来の手順だと、
`リソース > 追加済みのデータソースの管理 > 編集 > "fx" or "@"`
というステップを踏まないといけない作業が、ワンステップでできるようになりました。
気づいていなかった変更ですが、これは結構便利だなと思いました。
Ubuntu on Windows Community Preview

アナウンス原文:

https://discourse.ubuntu.com/t/announcing-ubuntu-on-windows-community-preview/19789

日本語訳したので、まずはこちらを読んでください:

https://gist.github.com/matarillo/5f6cf47846f64c3a4bc9715a5f81698c

というわけで、Ubuntu on Windows Community Previewというものが出ます。

３行でまとめると、

* 機能プレビュー版を試してくれる**人柱**募集中！
* **不具合は**あって当然だからどんどん**報告**してね。
* **普段使いには向いてない**からそのつもりでね。

です。

アウトオブボックス体験として、初回起動時の設定画面、というのがあるのですが、これは **Windows Terminal**（またはその他のターミナルエミュレーター）を使うかどうかで言語の選択肢が変わりますので注意。

どういうことかというと、実際の画面を比べるとわかります。

まず最初は普通にスタート画面に追加されるショートカットから起動した場合はこちら。

![コマンドプロンプト](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2079/115cc8fb-6baf-6971-7534-34d67effa5bf.png)

「レガシーなWindowsコンソールが検出されたので、フォールバックモードになります」とありますね。言語は米語、英語、スペイン語、エスペラント語、フランス語の5つからの選択。

一方、**スタート画面に追加されるショートカットを無視して、Windows TerminalのPowerShellかCMD.exeを使い、そこでwsl.exeまたはubuntupreview.exeを起動した**場合はこちら。

![Windows Terminal](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2079/50355721-0344-d0e0-f2f9-7ec544e852b1.png)

ここで日本語を選ぶと、以降の設定画面が一部日本語化されます。（正直、そんなにありがたくはない。）

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2079/7028485f-79f9-543b-b512-89e4c049eaa6.png)

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2079/842845cf-6f21-b5e8-aded-2b3a4331b512.png)

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2079/772110a6-5c90-4e9c-efca-9a16a0c28ad4.png)

設定画面が終わると、ログインです。「今日のひとこと(Message of the day)」としていろいろ表示されます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2079/b413a8d7-6511-b651-e194-f56967aaacf7.png)

設定画面の最後にしれっと表示されていたように、一度再起動しないと、`apt update`などもうまくつながらなかったりするので注意。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2079/70c88c4d-3ba2-8303-17fa-ce0bfb4bcbf7.png)
さくらVPSでRedmine4.1を立てる

# さくらVPSでRedmine4.1を立てる

さくらVPSを借りてRedmineを立てたのでメモ

## きっかけ

* 個人的なタスク管理やスケジュール管理をちゃんとしたい
* 無料or安価なグループウェアを探すがいいものがない
* ならVPS借りてRedmineでも立てるか

## 環境
|項目|内容|
|---|---|
|プラン|1G|
|メモリ|1 GB|
|ストレージ|SSD 50 GB|
|CPU|2コア|
|||
|OS|Ubuntu Server 20.04|

プラン詳細
https://vps.sakura.ad.jp/specification/

## さくらVPSを借りる

ブログのためにレンタルサーバーを借りていたので、会員登録はパス。
VPSの申し込みは案内の通りに情報を入力していくだけで完了。
SNS認証必須なので、電話番号が古くないか事前に確認しておくこと。
実家の固定電話が登録されていて、ちょっと焦った。

## Ubuntuのインストール

普通のUbuntuは自動インストールできるが、UbuntuServerはインストールが自動インストールが存在しなかったので、ISOを用意して手動インストール。
IPアドレスは割り振られたアドレスを固定にする必要があるので注意。
管理コンソールで割り振られたIPアドレスを確認できる。

2コアってどうかなって思ったけど結構パワーあってびっくりする。

## Redmineを立てる
こちらを参考に作業した。
ちょっと古い記事だったがまあ問題なし
http://blog.redmine.jp/articles/4_1/install/ubuntu/

## 立てた後の話

### こまごまとした設定
公開絡みとか結構色々やった方がいい
https://www.kagoya.jp/howto/rentalserver/redmine/
https://qiita.com/YKEI_mrn/items/625c75de8f87445206d4

### ポート変更

以下２つのファイルにあるポート番号を適当な番号に変更する
/etc/apache2/ports.conf
/etc/apache2/sites-enabled/000-default.conf

### basic認証

```
# cd /var/lib/redmine/public
# htpasswd -c -b .htpasswd test testpw
# chown apache:apache .htpasswd
# ls -al
# nano /etc/apache2/conf-enabled/redmine.conf

## 以下のLocationを追加
<Location />
 AuthType Basic
 AuthName "Please UserID and Password"
 AuthUserFile "/var/lib/redmine/public/.htpasswd"
 Require valid-user
</Location>

# systemctl restart apache2
```

## 使用メモリなど
立てた直後でこれ。
512MBのプランでも良かったかも。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558673/05b95b0c-c920-4d2b-e489-adbc2d53cc71.png)

## プラグイン入れたい
この辺参照
https://qiita.com/kooohei/items/3c86554af93d12ad9322
https://qiita.com/will_meaning/items/97f5ff6b1a3a66fcf975
https://qiita.com/bezeklik/items/ca5644f62e8a951cf1b2

## テーマも入れたい
この辺参照
http://blog.redmine.jp/articles/farend-bleuclair-theme/

## SSL関連(2020/12/16追記)

### certbot
インストールして--standaloneとかで証明書を作っておく
メールアドレスの共有はしなくてもいいらしい
[このへん](https://certbot.open-code.club/#certbot%20certonly%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%E8%A8%BC%E6%98%8E%E6%9B%B8%E5%8F%96%E5%BE%97(standalone%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3))参考

### apacheの設定
ssl有効化して

```
sudo a2enmod ssl
```

前述したポート設定のファイルにSSL用ポートを追加

```bash:/etc/apache2/ports.conf
<IfModule ssl_module>
        Listen 2643
</IfModule>
<IfModule mod_gnutls.c>
        Listen 2643
</IfModule>
```
```bash:/etc/apache2/sites-enabled/000-default.conf
<VirtualHost *:2643>
  ServerName {domain}
  DocumentRoot /var/lib/redmine/public
  SSLEngine on
  SSLCertificateFile /etc/letsencrypt/live/{domain}/fullchain.pem
  SSLCertificateChainFile /etc/letsencrypt/live/{domain}/chain.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/{domain}/privkey.pem
  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
```

### 証明書の更新

cron に以下のコマンドを登録しておけばいい感じに更新されるっぽい。
今は確認できないので3か月後が楽しみ

```bash:cron
0 4 * * * certbot renew --post-hook ”systemctl restart apache2”
```