OmniAICreator/Qiita-1.07M huggingface dataset:
ユーザー指定のコマンド実行

自動化のスクリプトで、ユーザ指定でスクリプトを実行したい場合はどうしたらいいだろう？自動化なので、インタラクティブがあってはいけない。

* [【sudo】Linuxで指定したユーザーでコマンドを実行する](https://uxmilk.jp/53095)

どうやら普通に`sudo`を使えばよいらしい。

```bash
sudo --help
sudo - execute a command as another user

usage: sudo -h | -K | -k | -V
usage: sudo -v [-AknS] [-g group] [-h host] [-p prompt] [-u user]
usage: sudo -l [-AknS] [-g group] [-h host] [-p prompt] [-U user] [-u user] [command]
usage: sudo [-AbEHknPS] [-r role] [-t type] [-C num] [-g group] [-h host] [-p prompt] [-T timeout] [-u user] [VAR=value] [-i|-s] [<command>]
usage: sudo -e [-AknS] [-r role] [-t type] [-C num] [-g group] [-h host] [-p prompt] [-T timeout] [-u user] file ...

Options:
  -A, --askpass                 use a helper program for password prompting
  -b, --background              run command in the background
  -C, --close-from=num          close all file descriptors >= num
  -E, --preserve-env            preserve user environment when running command
      --preserve-env=list       preserve specific environment variables
  -e, --edit                    edit files instead of running a command
  -g, --group=group             run command as the specified group name or ID
  -H, --set-home                set HOME variable to target user's home dir
  -h, --help                    display help message and exit
  -h, --host=host               run command on host (if supported by plugin)
  -i, --login                   run login shell as the target user; a command may also be specified
  -K, --remove-timestamp        remove timestamp file completely
  -k, --reset-timestamp         invalidate timestamp file
  -l, --list                    list user's privileges or check a specific command; use twice for longer format
  -n, --non-interactive         non-interactive mode, no prompts are used
  -P, --preserve-groups         preserve group vector instead of setting to target's
  -p, --prompt=prompt           use the specified password prompt
  -r, --role=role               create SELinux security context with specified role
  -S, --stdin                   read password from standard input
  -s, --shell                   run shell as the target user; a command may also be specified
  -t, --type=type               create SELinux security context with specified type
  -T, --command-timeout=timeout terminate command after the specified time limit
  -U, --other-user=user         in list mode, display privileges for user
  -u, --user=user               run command (or edit file) as specified user name or ID
  -V, --version                 display version information and exit
  -v, --validate                update user's timestamp without running a command
  --                            stop processing command line arguments
```

確かにそもそもsudo はそのためのコマンドだ。普段は、`sudo`が使えるユーザが、Root権限がいるときに使っていたが、そうでないケース、今回だと、`root`で実行しているスクリプトを`azureuser`で動かしたい。`chmod`とかもしてもいいけど、面倒だ。

これに従うと`-u`のフラグで行けそうだ。

```bash
sudo -u hoge cat sample.txt
Password:
```

そうだ、普段はパスワードを聞かれる。`root`から他のユーザに移るときはどうだろう？パスワード必要なのはつらい。

```bash
$ sudo su
[sudo] password for ushio: 
# sudo -u ushio ./std_in_out.sh 
stdout
stderr
```

うむ。rootからだと、パスワードは不要らしい。じゃあ、完全に自動化できるので、問題なし！
技術同人誌を「書く」意義


この記事は [「技術同人誌 Advent Calendar 2019」](https://adventar.org/calendars/3999) の15日めの記事です。

昨年・今年と、技術書典に3回参加しました。いずれの回も、技術同人誌の執筆・売り子としての参加でした。

色々と感じることがあり、書いてみます。


## 参加した理由とモチベーション

自分が技術書典に参加したのは

技術書典6、7 (いずれも共著本の執筆、売り子)
技術書典8 (委託販売と売り子手伝い)

です。

「最近 (2018-19) 盛り上がりを見せている、技術同人誌イベントに参加したみたい。」

というのが、参加した理由です。

このようなイベントの熱量は、実際に現場に行ってみないと分からない。ネット経由の情報では、技術書典の面白さといったものがわからないと思い、参加を決めました。

どうせ参加するなら、自分でも何か同人誌を書いて参加したほうが楽しいのではないか、と思い、知人の同人誌執筆プロジェクトに参加。共著本に参加して、実際に同人誌を執筆・販売してみました。

同時に

・自分の知識や経験を形に変えてみたい
・自分自身の自己アピールにもなるかもしれない

という思いもありました。

実際に参加した感想は

「面白い」

でした。

自分が感じた「面白さ」について、少し詳しく書いてみます。

## 技術同人誌即売会に感じた魅力

技術書典で感じた「面白さ」を一言でいうと、「思わぬ技術書との出会い」です。「セレンディピティ」と言い換えても良いでしょう。

自分がなにかの本をAmazonで買おうとすると、自分の見知った情報の範囲に留まりがちです。自分の興味の範囲でしか手が届かないことが多くあります。

技術書典では、「初めて聞く技術」が次々に視界に入ります。自分の興味・関心という壁の向う側にある情報が沢山入ってきて、見るだけでも楽しいのです。

さらに、実際に買って読むことで、それまで想定していなかったような技術に触れることができます。

このような偶然の出会い＝セレンディピティこそが、技術同人誌の最大の面白さでした。

加えてや「以前から気になっていたけど、これまで手を出していなかった技術」の道案内のような本にも出会います。

仕事上やプライベートで使う機会がなかったけど、ちょっと気になっていた技術 (自分の場合、Firebaseなど) を、同人誌がきっかけで触る機会ができました。

これもセレンディピティの一つと言えるでしょう。

## 技術文書を書く意義

技術同人誌を書く意義はどこにあるか。

最も大きいのは、「頭の中の棚卸し」です。知識や思考を言語化しようとすると、どこまでを正しく理解できていて、どこが曖昧か、整理できます。

文章は恐ろしいほどに「自分」が出ます。思考や癖、キャラクターなど。
同じテーマで文章を書いても、筆者によってびっくりするほど内容が違うことがよくあります。

自分はどんなことを考え、何を知っているかが再確認できるのも、技術同人誌の楽しさの一つと言えるでしょう。

## 販売書籍と同人誌の違い

商業出版として販売されている書籍と同人誌、違いは何でしょうか。

個人的な感想ですが、一般的に、出版社を経由して販売されている商業出版書籍のほうが、出版物としてのクオリティは高いと考えています。

個人レベルの文章と違い、複数の担当者による内容チェック、文章の校正、見やすいレイアウトなど、「売る」ための工数をかけた書籍は、やはり完成度が違います。

同じ著者が同じ内容で書籍を作ったとすれば、同人誌よりも商業書籍のほうがクオリティは高くなることでしょう。

それはそれとして、「商業書籍のほうが良い、同人誌はだめ」ということにはなりません。

言ってみれば、商業書籍は「コース料理」、同人誌は「屋台の料理」のようなものです。

皆さんは、お祭りで食べた焼きそばがとても美味しく感じたことはないでしょうか？

同人誌即売会のようなお祭りで手に入れた同人誌は、「お祭りの屋台の焼きそば」のようなものです。同人書籍を買うという行為は、イベントに参加した「体験」や、売り手と会話、偶然あった知人との出会いなど、全てを含めた「体験」込みの価値があります。

お店で食べるコース料理も、屋台で食べる食べ物も、どちらも美味しいことには変わりありません。

体験も含めて、技術同人誌を作る、売る、買うのはとても楽しく、価値があると言ってよいのではないでしょうか。
Expo(Rect Native)でAinplitude（分析ツール）を使ってみるメモ


ExpoではFirebase Analyticsが使えない。Firebase利用者としては少しつらいが、標準でAmplitudeに対応しているというので利用してみました。

ネットの記事は非常に参考になりますが、2019年12月現在、少々仕様が変わってたりするのでメモ。

## 事前に知っていれば楽なこと

事前に知っていたほうが楽だったことを記す。

### Expo関連

Amplitudeに限らず、expoさんは機能別にモジュールを分ける傾向にある。以前は追加インストールなく'expo'で使えたみたいだが、今は追加インストールが必要みたい。

* importの先は'expo-analytics-amplitude'に変更されている
* さらっとExpoの[関連ドキュメント](https://docs.expo.io/versions/latest/sdk/amplitude/)をみるといいかと思います

### Amplitude関連

画面にLoginメニューはあるがSignUpが無い。。。一旦Demo利用者として登録してからUpgradeするみたい。

* 普通のサービスみたいにいきなりサインアップできない
* Demoユーザーとして登録してから（Freeユーザー含め）Upgradeするみたい
* API Keyがあれば利用できる
* API Keyはプロジェクト単位で生成・利用するみたい


## 利用

利用方法は（一見）簡単。

```js:App.js
import * as Amplitude from 'expo-analytics-amplitude';

export default function App() {

  //Amulitude初期化
  Amplitude.initialize('228e5b5a79ae9070e24xxxxxxxxxx');

  //ユーザー設定
  Amplitude.setUserId("hoge");

  //プロパティー設定
  Amplitude.setUserProperties({
    gender:'female',
    area:'Tokyo'
  })
  //ログ送信
  Amplitude.logEventWithProperties("Hello");
  // Amplitude.logEvent("Hello"); //プロパティなしで送ることも可能
  
  return (
    <View style={{flex:1,justifyContent:'center'}}>
      <Text>Open up App.js to start working on your app!</Text>
    </View>
  );
}
```

実行すると、Envetやユーザーが確認でき、ユーザーには設定したプロパティも追加される。

![スクリーンショット 2019-12-16 6.24.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55188/64778d7e-4b11-0893-af0a-c4ec98b0feea.png "スクリーンショット 2019-12-16 6.24.35.png")


## 課題

* Amplitudeの各分析機能を利用するために、どうlogEvent()を仕込むのがいいか探求が必要。。
* いちいちinitialize()はできないので、別ファイルに分けたりする必要がある。

## 参考

* https://tech.maricuru.com/entry/2018/06/30/082240
* https://qiita.com/kaba/items/03bb6c9453b3231c514c
1111memo

HTML

```HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <link rel="stylesheet" href="css.css">
　<meta charset="UTF-8">
　<title>Document</title>
</head>


<body>

  <div> <button id="DigitalCLOCK" class="clock-btn" onload="showTime()"></div>

<p class="mb1"></p>

  
<textarea class="ta" name="textarea1" id="" cols="30" rows="30"></textarea>
<textarea name="textarea2" id="" cols="30" rows="30"></textarea>
<textarea name="textarea3" id="" cols="30" rows="30"></textarea>

  
<!-- ライブラリの読み込み -->
<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<!-- JavaScriptを記述する -->
<script>
// 時刻表示
// 今日の日付情報を取得し変数dTodayに格納する
function showTime(){
    var date = new Date();
    var y = date.getFullYear();
    var mo = date.getMonth()+1;
    var d = date.getDate();
    var h = date.getHours(); 
    var m = date.getMinutes(); 
    var s = date.getSeconds(); 
    
    if(h == 0){
        h = 12;
    }
    
    
    h = (h < 10) ? "0" + h : h;
    m = (m < 10) ? "0" + m : m;
    s = (s < 10) ? "0" + s : s;
    
    var time = y + "年" + mo + "月" + d + "日" + h + "時" + m + "分" + s + "秒";
    document.getElementById("DigitalCLOCK").innerText = time;
    document.getElementById("DigitalCLOCK").textContent = time;
    
    setTimeout(showTime, 1000);
    
}
 
showTime();
// ClipBoard.js関連
var clipboard = new ClipboardJS('.clock-btn', {
      target: function() {
          return document.querySelector('div');
      }
  });

  clipboard.on('success', function(e) {
      console.log(e);
  });

  clipboard.on('error', function(e) {
      console.log(e);
  });



  var clipboard = new ClipboardJS('.btn', {
      target: function() {
          return document.querySelector('div');
      }
  });

  clipboard.on('success', function(e) {
      console.log(e);
  });

  clipboard.on('error', function(e) {
      console.log(e);
  });
</script>

</body>
</html>
```

CSS


```CSS
.clock-btn {
    width: 500px;
    text-align: center;
    display: block;
    padding: 15px;
    text-decoration: none;
    color: #fff;
    background-color: grey;
    border: none;
    font-family: 游ゴシック;
    font-size: 30px;
    border-radius: 5px;
    margin: 0 auto;
    transition: 1s;
}
.clock-btn:hover {  
    transition: 0.5s;
    background-color: black;
    width: 500px;
}
.mb1 {
    padding: 10px;
}

.ta {
    border: none;
}
.ta:hover {
    transition: 10s;
    border: 1px solid black;
}

```
【Django】Truncate Table(テーブルのデータを全て削除)を行う

Djangoでモデルを変更する際に、中身のデータを全て削除したいケースがある。

# pythonスクリプトで行う

```shell
python manage.py shell
```

でshellにログイン後

```py

from * import models.テーブル

テーブル.objects.all().delete()

```

# SQL文で行う

```shell

python manage.py dbshell
```

でdbshellにログインした後、```TRUNCATE TABLE```を発行


# 参考
* [How do I drop a table from SQLite3 in DJango? - Stack Overflow](https://stackoverflow.com/questions/2286276/how-do-i-drop-a-table-from-sqlite3-in-django/2286712)
* https://docs.djangoproject.com/en/1.7/intro/tutorial01/#playing-with-the-api
* https://docs.djangoproject.com/en/dev/ref/django-admin/#dbshell
UserPickerで楽にユーザー選択を実装する

どうもこんばんは、みかんです。

**！！！！とつぜんですがたいせつなおしらせ！！！！**

2020年1月27日
App Maker の提供終了に向けた対応のご案内
https://support.google.com/a/answer/9682494?p=am_announcement&visit_id=637157414793613981-426262357&rd=1


??!???!?!?!???!?

残念ながら2021年1月19日には終了してしまうそうです(涙)
移行先はAppSheetかApp EngineかGoogleフォームだそうです・・・

消すのは忍びないので記事は残して置きますが、何かの間違いじゃ無い限り役に立つことはないでしょう・・・


**！！！！おしらせはおわりです！！！！！**

今回は「UserPickerで楽にユーザー選択を実装する」方法を書いてみます。
下記ステップでやっていきます。

* 1 下準備
* 2 User Pickerの配置
* 3 動きの確認
* 4 valueIsRecordとは？

# 1 下準備

ちょうどいいサンプルは無いので、「Blank Application」で見ていきます。

|![ap01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/460b09c9-c72f-d1f4-9526-7ce73f81d720.png)|
|:-:|

準備らしき準備はこれだけですね。。

# 2 User Pickerの配置

|![ap02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/0a4f3f6d-45e5-630c-a7f6-e4eccb636a4b.png)|
|:-:|

さっそくUser Pickerを配置してみようと思います。これをドラッグアンドドロップすると・・？

|![ap03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/140bf6f9-5e61-3113-d30e-6796a4bc89a6.png)|
|:-:|

「このウィジェットを使うにはディレクトリモデルが必須です。作っていいですか？」
と問われています。

|![ap04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/defc8c49-db57-8eb0-fe00-dce3a4c8a3a7.png)|
|:-:|

CREATEを押して見るとこのような感じに。確かにDATAのところにDirectoryモデルが作成されています。
このUserPickerはG Suiteのユーザーから候補を出してくれるウィジェットになるわけですが、その候補データはきっちりDirectoryモデルにて取得するような仕組みになっているというわけですね。

で、このままだと見づらいので色々追加で配置してみます。

|![ap05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/ea15a613-b92b-e162-b0a7-82588c6a61e4.png)|
|:-:|

こんな感じになりました。「ユーザー選択」と書いてあるところがUserPickerです。

# 3 動きの確認

とりあえずPreviewしてみましょう。

|![gif001-compressor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/fb0624d1-3913-03b3-8c5f-b67990f2f136.gif)|
|:-:|

それぞれ、メールアドレスを最後まで打ち込まなくても、候補に表示されたカードをクリックすることで、入力値が反映されています。
基本的にはこれだけです。選択された時どのような情報を取れるかというバリエーションがありますので、次で解説します。

# 4 valueIsRecordとは？

UserPickerのプロパティを確認すると、**valueIsRecord**というものがあることがわかります。

|![ap06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/bc0f7682-7f33-a3c8-1ec4-b25f425e2358.png)|
|:-:|

ちょっとピンと来ない設定名ですが、名前の通り「値がレコードか否か」という設定になります。値はメールアドレスの文字列で、レコードはディレクトリモデルのレコードです。

|![ap07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/be0f7655-bf02-c3c9-49ef-6fd935777c53.png)|
|:-:|

上のカードのUserPickerはこれをオフにしているので、「値」が取れる状態です。それを「Email」テキストボックスからバインディングで参照しています。なので、ユーザーを選択するとそのままメールアドレスが入ります。

一方、下のカードのUserPickerはこれをオンにしているので、「レコード」が取れる状態です。ただ、なぜだかわかりませんがvalueプロパティには文字列(アドレス)が入っていて、ただレコード型なので素直にテキストボックスにはバインディングできませんでした。

ではどのようにレコードの値を参照するのかというと、イベントを利用します。

|![ap08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/a9960c7f-287a-7069-3bb0-74faf3f12737.png)|
|:-:|

**onValueChange**はユーザーを選択したタイミングで呼び出されます。
他にも2つ似たようなイベントがあり、それぞれ下記の通り呼び出されます。
onValueEdit: 入力後フォーカスが外れた時
onInputChange: 入力する度

onValueChangeのnewValueには、ディレクトリモデルのレコードが入っています。メールアドレス以外にも、フルネームやサムネールのURLなどが手に入ります。ここでは画像ウィジェットも用意して、urlプロパティに入れてみたりしました。

基本的にはメールアドレスで事足りるとは思いますが、アプリのUIとしてユーザーの画像も入れ込みたいなと言う時は是非valueIsRecordをオンにして使ってみましょう！

以上です。良いApp Makerライフを!
SQLAlchemyのDB別接続方法まとめ

##SQLAlchemyをもっと便利に使いたい
Pythonを使っているならDB処理に例外なく**SqlAlchemy**を使っていると思います。
しかしSQLAlchemyは独特すぎていったんプロジェクトから離れてしまうと文法を忘れてしまいがちです。

今回はそうならないようにするために、いつでもこのページに戻ってすぐにSQLAlchemyに取り掛かれるようなページにしていこうと思っています

##SQLAlchemyのDB別接続方法

[元ネタのURL](https://docs.sqlalchemy.org/en/13/core/engines.html)

基本的な文法は以下です。(PostgreSQLの場合)

``` python
from sqlalchemy import create_engine
engine = create_engine('dialect+driver://username:password@host:port/database')
```

以下が一覧になります。ユーザー名、パスワード、データベース名は仮です

|#|DB名|Database URL|
|---|---|---|
|1|MySQL|mysql://scott:tiger@localhost/foo|
|2|PostgreSQL|postgresql://scott:tiger@localhost/mydatabase|
|3|SqLite|sqlite:////absolute/path/to/foo.db'|
|4|Microsoft SQLServer(ODBC)|mssql+pyodbc://scott:tiger@mydsn|
|4|Microsoft SQLServer(Pymssql)|mssql+pymssql://scott:tiger@hostname:port/dbnam|
|5|Oracle|oracle://scott:tiger@127.0.0.1:1521/sidnam|

##更新履歴
- 2019/12/16 新規作成
IchigoJamで円周率を推定する

# 何をするのか

点をランダムでサンプリングし、円の中に入った点の割合から円周率の値を推定します。 (モンテカルロ法)

# 原理

以下のような、正方形の中に円の4分の1の部分が入った図形を考えます。
この正方形の面積は、$1\times 1 = 1$です。
半径$r$の円の面積は$\pi r^2$なので、この図形の中の円の部分の面積は$\pi / 4$です。($r = 1$)
![正方形と円](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/8fc113ce-1a44-3319-a9c4-362002718924.png)
したがって、この正方形の中に一様ランダムに点を打っていくと、各エリアに入る確率は各エリアの面積に比例し、
円の中に入る確率は$(\pi / 4) / 1 = \pi / 4$となります。
これは、大量に点を打っていくと、(円の中に入った点の数)÷(打った点の数)≒$\pi / 4$となるということです。
この性質を用いると、円周率の値が(円の中に入った点の数)÷(打った点の数)×4で推定できます。

# 実行結果

YouTube動画 (6分24秒)
[![実行結果(YouTube)](http://img.youtube.com/vi/mC9FgoCXB6A/0.jpg)](http://www.youtube.com/watch?v=mC9FgoCXB6A)

# ソースコード

```
10 'ﾓﾝﾃｶﾙﾛ ﾎｳ
20 CLS:FOR I=0 TO 399
30 POKE#900+I/20*32+I%20,#80
40 NEXT:FOR Y=0 TO 39
50 B=1600-(Y+1)*(Y+1)
60 X=1:P=#900+Y/2*32
70 IF X*X>B GOTO 120
80 D=Y%2*3+1
90 IF (X+1)*(X+1)<=B D=D*3
100 POKE P,PEEK(P)|D
110 X=X+2:P=P+1:GOTO 70
120 NEXT:P=#900:D=0
130 C=0:FOR T=1 TO 3000
140 X=RND(10000):U=X/250
150 Y=RND(10000):V=Y/250
160 POKE P,PEEK(P)^D
170 P=#900+V/2*32+U/2
180 D=1<<(V%2*2+U%2)
190 POKE P,PEEK(P)^D
200 A=X/100:B=X%100
210 E=Y/100:F=Y%100
220 G=(B*B+F*F)/100
230 H=2*A*B+G:K=H/100
240 L=(H%100+2*E*F)/100
250 IF A*A+E*E+K+L<=10000 C=C+1
260 LC 0,21
270 ?C;"/";T;" PI=";C*4/T;".";
280 R=C*4%T:FOR I=1 TO 4
290 R=R*10:?R/T;:R=R%T
300 NEXT:?
310 NEXT
```

# 解説

ある点$(x, y)$が中心$(0, 0)$、半径$r$の円の周または内部(以下、円の中)にあるかは、
$x^2 + y^2 \leq r^2$を満たすかどうかで判定できます。
前半ではこの性質を利用し、右下が円の中にある点を白く塗っています。

後半がモンテカルロ法の本体で、この性質を利用してランダムに打った点が円の中に入ったかを判定しています。
上の原理では$1\times 1$の正方形を使いましたが、図形を拡大しても面積比は変わらないので、
プログラムでは$10000 \times 10000$の正方形を利用しています。($r = 10000$)

ここで、IchigoJamの変数は32,767までの数しか扱うことができないのに対し、$0 \leq x, y < 10000$であり、
$x^2 + y^2$は最大$2 \times {10000}^2 = 200,000,000$程度になるため、工夫が必要です。
今回は、以下のような工夫をしました。

まず、$x$および$y$を以下のように2桁ずつに分割します。
$$\begin{eqnarray*}
x &=& 100a + b \hspace{1em} (0 \leq a, b < 100)\\\\
y &=& 100c + d \hspace{1em} (0 \leq c, d < 100)
\end{eqnarray*}$$
すると、$x^2$および$y^2$は以下のようになります。
$$\begin{eqnarray*}
x^2 &=& {(100a + b)}^2 = 10000a^2 + 100\cdot 2ab + b^2 \\\\
y^2 &=& {(100c + d)}^2 = 10000c^2 + 100\cdot 2cd + d^2
\end{eqnarray*}$$
さらにこれらを足すと、
$$x^2 + y^2 = 10000(a^2 + c^2) + 100(2ab + 2cd) + (b^2 + d^2)$$
となります。
$0 \leq a,b,c,d < 100$より、$a^2 + c^2$、$2ab$、$2cd$、$b^2 + d^2$は$20000$以下なので、そのまま計算できます。
これらの値を用いて、以下の図のように下位から上位へ計算していきます。
![計算のようす](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99656/f65c5d62-dec4-35d9-64cd-c521dfa6eee5.png)
図中の2個つながった四角形は、右の四角形が十進数の下位2桁、左の四角形が残りの部分を表します。
値が確定した下位を切り捨てていくことで、扱える値の範囲で計算を行います。
最終的に、$a^2 + c^2$に下位からの繰り上がりを足した値が10000以下であれば点は円の中に入っている、
10000を超えていれば点は円の中に入っていない、と判定できます。


# IchigoJamとは

2,000円前後で購入することができ、BASICによるプログラミングが可能なパソコンです。

[こどもパソコン IchigoJam - はじめてのプログラミングパソコン(1500円)](https://ichigojam.net/)

また、プログラムをWebブラウザ上で実行できるサービスもあります。

[IchigoJam web by WebAssembly](https://fukuno.jig.jp/app/IchigoJam/)

※「IchigoJam」はjig.jpの登録商標です
JupyterじゃなくてJupyterLabを勧めるN個の理由

## はじめに
こんにちは、myjlabのmarutakuです。12/15の枠が空いていたので急遽記事を書いています。
今回は、自分がよく使っているJupyterLabというツールについて説明をします。個人的にこのツールは鬼のように使いやすいツールなのでたくさんの人に使ってもらいたいと思っています。

## この記事で話すこと
* JpyterLabについて
* Jupyter notebookとの違い
* 導入方法


## この記事で話さないこと
* 拡張機能
    * 僕があまり使わないから

## JupyterLabとは
JupyterLabとは、Jupyter(iPython notebook)をベースにしたインタラクティブな開発環境です。Jupyterでできることはそのままに、さらに使いやすく色々な機能を追加したツールになります。

↓ こんな感じ

![こんな感じ](https://i.gyazo.com/204381af02a81d2b502db167fa81b41f.png)


かなりメンテナンスがされていて、

👦「Jupyter notebookではできたのにJupyterLabではできないじゃん！キーーーー！！」

ということは今のところありません
ここから先は、特に自分が使いやすいと思った点についてお話しします。

## Jupyter notebookとの違い
個人的には３つの点でJupyterLabはJupyter notebookよりも優れていると感じています。

### 1.タブが増殖しない
これが正直一番でかいです。Jupyter notebookを使っていてもっともイラついたのは、Chromeのタブが増殖しまくって見づらくなることでした。本当はこまめに消していけば何の問題もないのですが、ズボラな僕にはそんな高度なことができないため、無限にタブが増えていきました。増えていくと、わからないことが発生してググった時に、ググったタブと問題が発生したNotebookのタブが離れていってしまう問題が発生していました。

JupyterLabでは、１つのタブの中でタブを切り分けてくれるのでChromeのタブが増殖しません。これによって、ググったタブとJupyterLabのタブが離れていく問題は解消できました。

### 2.マークダウンファイルが書きやすい
単純に拡張機能を入れろって話ではあるのですが、Jupyter notebookはデフォルトではマークダウンファイルは通常のテキストファイルとして扱われます。そのため、マークダウンのプレビューやシンタックスハイライトなどは行ってくれません。

JupyterLabはデフォルトでマークダウン記法をサポートしています。また、編集している画面を右クリックすることでプレビュー画面を開くことができます。また、編集画面でも見やすいようにハイライトしてくれる優秀ぶりです。神。

![](https://i.gyazo.com/093da29f2f12d03a5212bfd18695f6e0.png)

### 3. セルの入れ替えがドラッグ＆ドロップでできる
JupyterLabではセルの入れ替えがドラッグ&ドロップでできます。シンプルに便利。もうそれ以上に言うことはない。

## 導入方法
一瞬

```bash
pip install jupyterlab
```

## 終わりに
今回はExtensionの話を全くしませんでしたが、JupyterLabにも様々なExtensionが存在します。個人的に気になっているのはこれです。GPUの状態を確認できるのは強い。READMEの画像に出てくるDaskのプログレスバーみたいなやつも気になる...

https://github.com/rapidsai/jupyterlab-nvdashboard

## 参考資料
* https://jupyter.org/
* https://jupyterlab.readthedocs.io/en/stable/getting_started/installation.html
* https://qiita.com/kirikei/items/a1639954ce5ccaf7ac3c
Remove: Unknown Plugins してますか?

この記事は[Maya Advent Calendar 2019](https://qiita.com/advent-calendar/2019/maya)の16日目の記事になります。

突然ですが...

# ちゃんと Remove: Unknown Plugins してますか?

...と思ったのも理由がありまして...

### 不明なプラグインの混入率が高い

仕事の中で自分に回ってくるシーンデータに不明なプラグインが混入している確率が非常に高い...

### 不明なプラグインが混入してると困ること

1. シーンを開くのに時間がかかる
  - プラグイン情報はシーンを開いたときに最初に読み込まれる部分です。<br>ここでMaya環境内に存在しないプラグインを読み込もうとした際にエラーメッセージが表示されます。<br>このエラーを引き起こすプラグインの数が多ければ多いほどシーンを開くのに時間がかかってしまいます。
2. 不明なプラグインの情報は感染します。
    - **継ぎ足しされる秘伝のunknown-plugin**
        - 過去のモデルデータを新しいシーンにインポートしたりした場合に不明なプラグインの情報も引き継いでしまいます。
    - **リファレンスで遺伝するunknown-plugin**
        - リファレンス元のデータに不明なプラグインが含まれている場合、リファレンス先のシーンで不明なプラグインの除去を行っても、再度シーンを開いたときに不明なプラグインの情報が復活してしまいます。
        - モデルアセットの時点で感染している場合、リグ/レイアウト/アニメーション/ライトコンプ/etc...のすべての工程でシーンを開く際にエラーメッセージが表示されることになります。
            - 例えば不明なプラグインによる遅延が平均で5秒かかるとした場合、1話(仮で300カット) x (レイアウト/アニメーション/ライティング/分散レンダリング(仮で10台)) = 5.4時間ほどの無駄な時間を発生させている計算になります。


### 不明なプラグインを除去する方法

#### Maya標準の機能では不明なプラグインを除去することはできない

シーン内の不要なデータを除去してくれる最適化オプションが存在しますが、`Unknown Nodes`のみ除去することができ、不明なプラグインは削除できません。

- `File` > `Optimize Scene Size Options` > `Unknown Nodes`
    - 以下のノード タイプすべてを削除します。
        - unknown 
        - unknownDag
        - unknownTransform 

#### 不明なプラグインを除去するにはスクリプトを書く必要がある

[unknownPlugin](http://help.autodesk.com/cloudhelp/2020/JPN/Maya-Tech-Docs/CommandsPython/unknownPlugin.html)コマンドを使用します。

##### 不明なプラグインの取得
```python
cmds.unknownPlugin(q=True, l=True)
```

##### 不明なプラグインの除去
```python
cmds.unknownPlugin("<Unknown-Plugin>", r=True)
```


#### 誰かが開発した不明なプラグインを除去するツールを使う
- 探してみたものの見つかりませんでした
    - 大手のCG会社であればテクニカル系に強い人がチェックツールなんかにプラグインを除去する機能なんかを搭載していると思いますが、フリーでは見つからない...

- 見つからないなら自分で作ろう

# 不明なプラグインを除去するツール 作成中

というわけでツールを現在作成中です。
記事に間に合えばよかったのですが、思った以上に忙しくツールは間に合いませんでした。
アドベントカレンダー最終日までには公開できるように頑張りますので、公開された際にはご活用いただければと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/124610/3903ace5-1f90-620e-f1bd-b5e53e85cbdc.png)

こんな感じのツールを想定中...

## ダウンロード
ツールが完成したので公開しました。

gumroadにて無料ダウンロードできます。
https://gumroad.com/l/jXoRw

![](https://pbs.twimg.com/media/ESq0v0pXQAAJpxu?format=jpg&name=900x900)


- - - 
##### 参考
- [【Maya】require コマンドについて](https://sproutmel.blogspot.com/2015/09/mayarequire.html)
- [シーン内の不明なプラグインを除去する](https://qiita.com/UnPySide/items/b91fe203ccf587e8cf99)
- [【Maya】不明なプラグインをシーンファイルから消す](https://tm8r.hateblo.jp/entry/2017/06/29/004443)

- - -

2日にわたり記事を書かせて頂き、ありがとうございます。

あまり面白い内容ではなかったかと思いますが、今後のMayaライフにお役立ていただけますと幸いです。

よろしければブログの方もご一読いただけますとうれしいです。
https://rateionn.hatenablog.jp/

明日17日目は[takumi_akashiro](https://qiita.com/takumi_akashiro)氏の[起動中のMayaに対する実用的な外部アプリケーションからの操作]()です。
progateの「Ruby on Railsの環境構築をしてみよう！(Windows)」でサーバー構築につまづいた時の対処法

# サーバー構築につまづいた時の対処法
### Railsサーバーをたてたときのエラー
初めてRuby onRailsの実行環境を行う際にRailsのアプリケーション作成まではうまくいき、さあ次はRailsのサーバーを立てようかとの`rails s`のコマンドを入力したとき、以下のようなエラーを吐かれた。

```
Could not find gem 'sqlite3 x64-mingw32' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.
```

あれ？progateの記事通りにやったのに違う結果になったぞ。と思いいろいろ調べた結果どうもgemのバージョンに問題があるらしい。~~gemとはなんぞや？~~

### Gemfileの更新
Railsアプリケーションを作成したときに生成されたフォルダ直下にGemfileというものがある。
<img width="896" alt="191216 qiita.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545092/b2819236-2eab-f5cf-b588-491cf0e176c0.png">
そのGemfileを適当なエディタ(Atomやサクラエディタなど)で開き、9行目の
```gem 'sqlite3'```を```gem 'sqlite3', '~> 1.3.6'```に変更。
<img width="461" alt="191216 qiita3.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545092/0f8ba478-d455-2ea7-a1b7-56337b623b59.png">


そして、コマンドプロンプトで```bundle update```と```bundle install```を実行。

### あらためてRailsのサーバー構築
コマンドプロンプトで```rails s```を実行。
そして、適当なブラウザ(ChromeやFireFoxなど)のURLに
```http://localhost:3000/```と打って更新して以下のようなページが表示されたら成功。
**Yay!You're on Rails!**
<img width="956" alt="191216 qiita2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/545092/9eba7359-b579-af38-8e20-6208baef2d1f.png">


# 投稿者のRubyの学習状況
- progateでRubyの全5レッスン完了(1周)
- progateの「Rubyの開発環境を用意しよう！（Windows用）」を完了
- progateの「Ruby on Railsの環境構築をしてみよう！(Windows)」のサーバー構築段階
- progate以外でRubyを触った経験はほとんどないです

# 投稿者の実行環境
- ruby 2.4.9p362 (2019-10-02 revision 67824) [x64-mingw32]
- Rails 5.2.3

# 参考記事
[Ruby on Railsの環境構築をしてみよう！(Windows)]
(https://prog-8.com/docs/rails-env-win)
[rails sできない時の対処法 - Qiita]
(https://qiita.com/daichi41/items/fcaea1b2f0a81d4b09f4)
現場で現場で潰しが効くディープラーニング講座：応用数学レポート

##線形代数学
・スカラーとベクトル
　スカラーとは・・・四則演算可能な数、ベクトルの係数になることができる
　ベクトルとは・・・大きさと向きを持ち、矢印で表される
・行列
　行列とは・・・スカラーを表にしたもの
・行列の計算方法
　{2 1} {1 3}  = {2×1+1×3 2×3+1×1}
　{4 1} {3 1}    {4×1+1×3 4×3+1×1}

・逆行列
　行列において逆数のようなものを逆行列と呼ぶ
　{a b}
　{c d} というような行列があった場合、
　　　　　a:b != c:dの時逆行列を持ち、
　　　　　a:b  = c:dの時逆行列を持たない

・固有値と固有ベクトル
　行列Aに対してAx^→ = λx^→というような式が成り立つベクトルと係数λがあり、
　ベクトルを固有ベクトル、係数λを固有値という
・固有値分解
　正方行列において固有値を対角線上に並べた行列Aと固有ベクトルを並べた行列Vについて、以下のような関係となる
　AV＝VA
　これを変換するとA＝VAV^-1となり、行列の累乗の計算が容易になる。
・特異値分解
　正方行列以外の行列における固有値分解のようなものを特異値分解と呼ぶ
　Mv^→ = σu^→
　M^T×u^→ = σv^→
　という特殊な単位ベクトルがあれば、特異値分解ができる。
　M = USV^T
　特異値分解は画像データの容量を小さくするなどに使用される

##統計学
・確率の定義
　確率の定義は「事象Aが起きる数/すべての事象の数」である
・ベイズ則
　事象Aと事象Bがあり、事象Aの元、事象Bが起きる確率と事象Bの元、事象Aが起こる確率は以下の式のように表される
　P(A)P(B|A) = P(B)P(A|B)
・推測統計と記述統計
　記述統計とは・・・集団から一部を標本として取り出し、母集団の性質を推測すること
　推測統計とは・・・集団の性質を要約し、記述すること
・期待値
　期待値とは・・・分布における確率変数の平均の値であり、「ありえそう」な値
・分散と共分散
　分散とは・・・データの散らばり具合であり、各々の値が期待値からどれだけズレているか平均したもの
　共分散とは・・・2つのデータ系列の傾向の違いを表したものであり、この値が正であれば似た傾向、負であれば逆の傾向、0であれば関係性が乏しいということを表す
・自己情報量
　情報の量を表すものであり、以下の式となる
　I(x) = -log(P(x)) = log(W(x))
　対数の底が2のとき、単位は(bit)となり、対数の底がネイピアのeのとき、単位は(nat)となる
・シャノンエントロピー
　シャノンエントロピーとは・・・自己情報量の期待値であり、自己情報量の平均を求めることで求められる
　H(x) = E(I(x)) =-E(log(P(x))) = -Σ(P(x)log(P(x)))
・カルバック・ライブラーダイバージェンス
　カルバック・ライブラーダイバージェンスとは・・・同じ事象・確率変数における異なる確率分布P、Qの違いを表し、それぞれの分布の違いによる情報量の違いを表すもの
Oculus QuestだけでフリーロームVR向けに複数人を位置トラッキングする

# はじめに

本記事はUnity #2 Advent Calendar 2019　の14日目の記事です。
フリーロームVRをマルチプレイヤーで実現するためにはプレイヤーのトラッキングが必要となり割と大掛りな機器が必要となります。
それをOculus Questのガーディアンだけでそこそこの精度で実現できたので紹介したいと思います。

## フリーロームVRとは

フリーロームVRとはプレイヤーがフィールド内を自由に歩き回る事が可能なVRコンテンツで以下のような施設があります(ありました)

* [BIOHAZARD WALKTHROUGH THE FEAR](http://dynapix.jp/am/biohazard7_vr/)
* [近未来制圧戦アリーナ 攻殻機動隊ARISE Stealth Hounds - VR ZONE SHINJUKU](https://vrzone-pic.com/shinjuku/activity/koukaku.html)
* [ゼロレイテンシー](https://tokyo-joypolis.com/attraction/1st/zerolatency/)

# Oculus Questの設定

ホーム画面でルームスケールに設定して歩ける範囲をガーディアンの境界線を引きます。以下の図は境界線とそれを元に生成されたプレイエリア(四角形の領域)です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8995/49231a31-0f13-971c-fd94-cd99db4283c2.png)

この時にマルチプレイで使用するOculus Questでほぼ同じプレイエリアを作るのが位置合わせで重要になってきます。
床の四隅にガムテームなどで`┌`、`┐`、`└`、`┘`のような形で目印を作りそのラインに沿って境界線を引くとほぼ同じプレイエリアができあがります。

# Unityの設定

1. [Oculus Integration](https://assetstore.unity.com/packages/tools/integration/oculus-integration-82022)をインポートします。
1. `OVRCameraRig`プレハブや`OVRPlayerController`プレハブをヒエラルキーに追加します。
1. このプレハブにアタッチされている`OVRManager`を探し、`Tracking` > `Tracking Origin Type`を`Stage`に変更します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8995/a071dbe7-a0d1-1211-e467-7f53a031f83e.png)

`Tracking Origin Type`が`Eye Level`か`Floor Level`ではプレイヤーのXZ座標はヒエラルキーに配置した位置のままですが、`Stage`にするとゲームの原点座標(0,0,0)はプレイエリアの中心になり、プレイヤーのGameObjectはプレイエリアの中心を基準にした位置に移動します。この仕組みによって実際に歩く領域とゲーム内の位置を合わせる事ができます。

# プレイエリアの問題

ルームスケールのプレイエリアで複数台のOculus Questの位置トラッキングができそうですが1つだけ問題があります。
ゲームの原点とプレイエリアの中心を合わせる事はできるのですがY軸が回転してZ軸の向きが原点から4辺のいずれかになり確定することができません。
つまり、同じ領域のプレイエリアを作っても0°、90°、180°、270°のいずれかに回転してしまうので、プレイヤーのTransformを同期しても位置が合わない事があります。

この問題をオペレーションなしに解決して正面方向を合わせる方法として以下の手順を考えました。

1. プレイエリアを4等分する。(十字型でも×でもよい)
1. プレイヤーの初期位置を4等分した領域のいずれかに配置して正面方向も固定する。たとえば(3, -3, 0)を初期位置、このPositionに対してforwardを正面方向とするならback、right、leftを向かないこと。
1. ゲームを開始して**数フレーム以降に**プレイヤーが正面を向いていなければプレイエリアが回転しているので原点を中心にプレイヤーのGameObjectを回転する。プレイヤーが右を向いているとプレイエリアは90°回転なのでプレイヤーのGameObjectを-90°回転する。

ゲーム開始直後だとプレイヤーの位置が反映されていないので`Awake`や`Start`を使えません。ネットワークライブラリで同期する直前あたりでフェードインして回転補正すると無難だと思います。もっと良い方法があればコメントで教えていただけると助かります。

**回転補正前のプレイエリアとプレイヤーの位置と向きのパターン**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8995/dc8c242b-83a0-7bbb-f58d-e4b21da4ca25.png)

# まとめ

Oculus Questのガーディアンすごい！
ROSをUnityとつなぐ　その2　ROS#を使う

# ROS#をセットアップする

まず、Unityは2019.3以降が必要。
今回は、2019.3.0f3を使用した。

AssetStoreから、ROS#で検索して出てくるので、ダウンロードしてインポート。
適当なシーンで、空のGameObjectを作成して、RosConnector.csをアタッチする。
サーバーのアドレスをプロパティに設定する。

![20191216_rosunity.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30257/beaf5f09-7918-e8b6-9033-e4a0c9a337b8.png)

これで実行すると、サーバー側につなぎに行くので、以下のようなログが出ている。

```
2019-12-15 17:27:43+0000 [-] [INFO] [1576430863.775880]: Client connected.  1 clients total.
2019-12-15 17:27:46+0000 [-] [INFO] [1576430866.393603]: Client disconnected. 0 clients total.
```

うまくいった。
Watson Visual Recognitionを用いた画像認識

#はじめに
ご存知の方もいらっしゃるかもしれませんが、実はWatson Studioを使うとプログラムを書かなくても画像認識が可能なんです。今回は「ねこ」と「いぬ」を見分けるモデルを作成して遊んでみようと思います。


#プロジェクトの作成
IBMのWatson Studioを使うための手順は下記のサイトを参考にしてください。
https://qiita.com/nishikyon/items/ba698b638300848b746e

まず、IBM Watson Studioの画面からCreate an empty projectを選択します。
<img width="1440" alt="スクリーンショット 2019-12-16 1.18.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/462e4d52-33db-c1e4-4249-6edc2c19d34f.png">

Nameに任意の名前を入力します。
<img width="1440" alt="スクリーンショット 2019-12-16 1.19.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/0180f1a1-6164-247e-535e-56963f4c2dcc.png">

新しいプロジェクトの画面が登場します。
<img width="1440" alt="スクリーンショット 2019-12-16 1.52.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/c4a2d42b-6018-c3e1-44b2-2d9fc56fa65f.png">

Add to projectをクリックすると
<img width="866" alt="スクリーンショット 2019-12-16 1.24.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/9d8865cd-dcda-99f5-4cca-f5bb892632b5.png">
という画面が出るので、Visual Recognitionを選択します。
<img width="654" alt="スクリーンショット 2019-12-16 1.25.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/b2a43688-31e1-0617-d695-d58c363bc793.png">
hereからこの画面にいきます。
<img width="578" alt="スクリーンショット 2019-12-16 1.26.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/2abb4374-0d86-ff1f-efef-3d5165c3615c.png">
ここでwatson-vision-combined-rfを選択し、Selectをクリックします。

#モデルに学習させるデータを追加
下の画面に飛ぶので、今回はClassify ImagesのCreate Modelを選択します。
<img width="720" alt="スクリーンショット 2019-12-16 1.53.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/b0ed172e-c76a-f3e9-5ff2-a4c0fca0b6a0.png">

この画面でTrainする画像ファイルを追加します。
<img width="1440" alt="スクリーンショット 2019-12-16 1.53.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/0c1bbf81-5c2b-f705-2405-a9e45bdc629d.png">
右側のUpload directoryにzipファイルをDrag&Dropするだけで追加できます。

Trainするデータファイルを追加すると下の画像のようになります。
<img width="1440" alt="スクリーンショット 2019-12-16 1.49.36.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/3a2ef3ca-c7a5-205e-6e19-64445075cb7d.png">
そして、Train Modelをクリックし、データを学習します。

数分かかるので待ちます。

#学習結果をテスト
画面上部にこのメッセージが出てきたら学習終了です。
<img width="670" alt="スクリーンショット 2019-12-16 2.23.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/b42d3265-53d8-1dc0-6ecc-4372fe9ce930.png">
hereから下の画面に飛びます。
<img width="1440" alt="スクリーンショット 2019-12-16 2.23.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/893dce87-f4f9-1d9a-386c-2738ccc6a4a8.png">

Testというタブをクリックし、Testしたい写真をDrag&Dropするとこのように結果が表示されます。
<img width="1440" alt="スクリーンショット 2019-12-16 2.30.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501835/a1e488a5-c68e-2e1e-bd00-c698f8196e0b.png">

#おわりに
Watson Studioを使うと、結構簡単に画像認識が体験できます。今回はLiteというプランを使っているので、作れるプロジェクトやモデルの数に制限がありますが、遊ぶには十分だと思います。画像認識の他にも色々とAssetsがあるので、試してみてください。
VSCodeの.mdのプレビューをかっこいい見栄えにする

# はじめに

VSCode + Markdownでドキュメントを殴り書いていると、どこの項目を書いているかわからなくなるときがありますよね（協調圧力）。
VSCodeの場合、EXPLORER PaneにOUTLINEが表示されるのでそれを見ながら頑張るというのもあるのですが、書いてるときはPreview見てるので、Previewでだいたいの位置がわかってほしい。

自分はPreviewに[shd101wyy.markdown-preview-enhanced](https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced)を使っているので、このCSS拡張をさくっと設定して、見出しに番号を振ってみます。

# 手続き

## markdown-preview-enhancedのインストール

必要に応じて、よしなにインストール。
ひょっとしたらデフォルトのpreviewerにcssカスタマイズできるかもしれない（未検証）けど、自分は[shd101wyy.markdown-preview-enhanced](https://marketplace.visualstudio.com/items?itemName=shd101wyy.markdown-preview-enhanced)を利用。

## CSSのカスタマイズ

`Ctrl-Shift-p` を押して Command Paletteを表示し、`Markdown Preview Enhanced: Customize CSS`を実行。
すると、`style.css`が開くので、以下のようなcssを記述（`/**以下の部分**/` と書いてある場所）。

今回は、とりあえず`h4`までを設定。内容はざっくり以下。

- h1でchapterのcounterをreset
- h2のbeforeでchapterをinclementし、`content:以下略`で出力。
- 以下略

なお、このcssの内容はあとで同じ環境を作りやすくするために適宜更新する可能性がありますのでご了承ください。

```css:style.css
/* Please visit the URL below for more information: */
/*   https://shd101wyy.github.io/markdown-preview-enhanced/#/customize-css */

.markdown-preview.markdown-preview {
  // modify your style here
  // eg: background-color: blue;
  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    font-family: "Yu-Gothic-UI";
    font-size: 1.2em;
  }

  body {
    font-family: "Yu-Gothic-UI";
  }
  [class*="language"] {
    font-family: "MyricaM M";
  }
  .task-list-item-checkbox {
    font-family: "Yu-Gothic-UI";
    font-size: smaller;
  }
  /**以下の部分**/
  h1 {
    counter-reset: chapter;
    font-size: 1.8em;
    border-style: solid;
    border-width: 0 0 1px 0;
  }
  h2 {
    counter-reset: sub-chapter;
    font-size: 1.5em;
  }
  h3 {
    counter-reset: section;
  }

  h2::before {
    counter-increment: chapter;
    content: counter(chapter) ". ";
  }
  h3::before {
    counter-increment: sub-chapter;
    content: counter(chapter) "." counter(sub-chapter) ". ";
  }
  h4::before {
    counter-increment: section;
    content: "(" counter(section) ") ";
  }
}
```

## 結果

こんな感じ。

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126949/8225c4a6-2d5e-0657-14d7-9061263f6e54.png)
ROSをUnityとつなぐ　その1　rosbridge_serverをセットアップする

# UnityとRaspberry Pi上のROSをつなぐには？

ROS#というUnityで動くROSのC#で書かれたコネクタがあるようだが、そのためにはWebSocket経由でROSのパケットを外部とやり取りするrosbridge_serverをセットアップしなければいけない。
だが、インストール方法の説明が、OSのパッケージマネージャーを使う方法しかなく、自前でビルドしたROS環境での方法が説明されていない。
本稿ではソースからビルドして動かした過程を記載する。

# rosbridgeの取得

Githubからクローンする。
ワークスペースのsrcフォルダで、クローンする。

```
$ git clone https://github.com/RobotWebTools/rosbridge_suite.git
```

# 手動で依存関係解決してビルド

これで、catkin_makeすればいいかと思いきや、足りないものがいくつかあるので、手動で依存関係を解決していく。

```
$ git clone https://github.com/ros/common_msgs.git
```

そしてビルド。ワークスペースのルートで。

```
$ catkin_make
```

実行すると、今度はPythonのライブラリがないといわれる。
仕方がないので、さらに手動で依存関係の解決。最初はpipさえ入っていなかったので、インストール。

```
$ sudo apt install python-pip
```

必要なPythonのライブラリなどをインストール

```
$ sudo pip install twisted
$ sudo apt install libffi-dev
$ sudo pip install pyopenssl
$ sudo pip install autobahn
$ sudo pip install service_identity
```

さらに、rosauthがないといわれるので、

srcで、

```
$ git clone https://github.com/GT-RAIL/rosauth.git
```

ワークスペースのルートで

```
$ catkin_make
```

さらに、まだ足りないといわれるので、

```
$ sudo pip install tornado
$ sudo pip install pymongo
```

なお、pymongoは、BSONが必要だったので、bsonをpipでインストールしたところ、エラーで動かず、調べたところ、[このような記載](https://github.com/RobotWebTools/rosbridge_suite/issues/198)を見つけたので、代わりにインストールした。

ここまででやっと実行できるようになった。

# 実行

```
$ roslaunch rosbridge_server rosbridge_websocket.launch
```

# やったー

動いた

```
2019-12-15 17:19:54+0000 [-] [INFO] [1576430394.149245]: Rosbridge WebSocket server started at ws://0.0.0.0:9090
```
iPhoneのSafariで新しいタブを開いた際にそのタブの戻るボタンを無効化する方法

# はじめに
- iPhone ではアンカータグをタップして新しいタブを開いた後、左から右にスワイプ又は戻るボタンをタップすることで、開いているタブを閉じて前のページへ戻ることができる。
- 開く新しいタブの戻るボタンを無効化する

- iPhone(11.4.1) の Safari のみ確認済

# コード
- 戻ることができる条件は、新しいタブを開いた後、前のタブのURLが変化していない場合。
- つまり、タブを開く -> 元のタブのURLを変更　とする必要がある。

```html
<a href="/hoge/foo.html" target="_blank" class="anti_back">CLICK</a>
```

```js


$(function () {
    $('.anti_back').click(function(){
        window.open($(this).prop('href'));
        location.href = "#" + getRandomString(6);
        return false;
    });
});

function getRandomString(num){
    var C = "abcdefghijklmnopqrstuvwxyz";
    var str = "";
    for (var i = 0; i < num; i++) {
        str += C[Math.floor(Math.random() * C.length)];
    }
    return str;
}

```

- アンカータグのクリック拾ってwindow.openで新しいタブを開いた後にURLに適当な文字のURLフラグメント付与
- アンカータグ自体は return false で無効

# さいごに
- 自分はこれしか思いつかなかった。外のサイトはどうやってるんだろう...
Docker+circleCI+ajaxでのいいね機能のRspec/systemテスト

##　この記事で解決すること
ajax(js)を使ったいいね機能のRspec/systemテストの方法


## 参考記事
>
[Rails + Selenium + DockerでSystemSpecの環境構築](https://qiita.com/ngron/items/f61b8635b4d67f666d75)
[Railsでいいね機能を実装。Ajaxを使い非同期対応。で](https://qiita.com/shiro-kuro/items/f017dce3d199f06d1dcd)
[RSpec+Capybaraでajaxとかcssとかを待つ](https://qiita.com/johnslith/items/09bb0e5257e06a4bd948)
[rspec-retryでfeature specを安定させる](http://www.cl9.info/entry/2017/05/10/001424)
[Capybara::ElementNotFound 回避に wait させるか retry させるか](https://qiita.com/tetsuya/items/ac41c53e9e9843ec0dce#4-rspec-retry-%E3%82%92%E5%88%A9%E7%94%A8)
[使えるRSpec入門・その4「どんなブラウザ操作も自由自在！逆引きCapybara大辞典」](https://qiita.com/jnchito/items/607f956263c38a5fec24)
[[RSpec] CapybaraでCSSの●番目の要素を指定したい！（同じ要素が複数あるとき）](https://qiita.com/after4649/items/af11003941c26db30f9b)

##　開発環境
macOS Catalina 10.51.1
Docker version 19.03.4, build 9013bf5
ruby 2.6.0
Rails 5.2.3
RSpec 3.9
capybara 3.29.0
selenium-webdriver 3.142.6

## docker環境でのsystemスペック
下記の記事を参考に構築
この設定をしないとjsを利用したテストでエラーが発生しました
>
[Rails + Selenium + DockerでSystemSpecの環境構築](https://qiita.com/ngron/items/f61b8635b4d67f666d75)


## テストするコード
下記の記事を参考にコードを書きました。
js.erbを使っているのがポイントです
>
[Railsでいいね機能を実装。Ajaxを使い非同期対応。で](https://qiita.com/shiro-kuro/items/f017dce3d199f06d1dcd)


## Ajax(js)を使用しているときのポイント
#### **selenium_chrome_headless**を使用する
→docker環境でのsystemスペックの記事を参考にするとこれは解決

#### jsを使っているとajaxの処理完了まで時間かかるので対策をする
##### ・wait timeを設定する
##### ・テストを繰り返す

## wait timeを設定する
この設定をすることでajaxの処理完了を待つ
設定は下記の記事を参考にしてください
>
[RSpec+Capybaraでajaxとかcssとかを待つ](https://qiita.com/johnslith/items/09bb0e5257e06a4bd948)

## 2.テストを繰り返す
[rspec-retry](https://github.com/NoRedInk/rspec-retry)を利用して非同期処理が終わるまでリトライしてもらいます
設定は下記の記事を参考にしてください
>
[rspec-retryでfeature specを安定させる](http://www.cl9.info/entry/2017/05/10/001424)

## テストコード
```likes_spec.rb
require 'rails_helper'

# js: trueがないとリクエストフォーマットエラーが発生する
# retry:5で失敗したときに何回まで繰り返すかを設定
RSpec.describe "Likes", type: :system, js: true, retry: 5  do 
  let(:user) { create(:user)}
  let(:other_user) { create(:user)}

  describe 'create' do
    scenario 'ログイン済みユーザ/他人の投稿にはいいねできる' do
      post = create(:post, user: user)
      sign_in other_user
      visit root_path
      #今回いいねのリンクでfontawosameを使用していてテキストを指定できなかったのでhave_linkをhave_cssで代用
      expect(page).to have_css('.likes-link-create') 
      expect(page).not_to have_css('.likes-link-delete')
      expect {
        # 同じクラスを持った要素が複数存在したので１つ目を指定。fontawosameを利用しているのでリンクをクラス名で指定
        first('.likes-link-create').click
        # 上記で設定したwait timeのメソッドを使用してajaxの処理完了を待つ
        wait_for_ajax
      }.to change{ post.likes.count }.by(1)
    end
  end
...省略
end
```

##　CircleCIを使用しているときの注意点
**ローカル環境よりもajaxの処理完了時間がかかる**
→ローカルであればwait time・テストを繰り返すのどちらかを設定すればテストは成功していたのですが、circleciでのRspecテストでは片方だけだと失敗してしまうので今回は両方を設定しています
wait timeを伸ばしたり繰り返す回数を増やしましたが解決しなかったので両方を設定
selenium_chrome_headlessを使う上で新しい設定を.circleci/config.ymlには追加してません

## 最後に
今回の記事を書くためにいろんな方の記事を参考にさせていただきました。ありがとうございました!
この記事が同じような課題を直面している人の助けになれば嬉しいです
そしてよりよい方法を知っている方がいればコメントなどで教えていただけると助かります！！
pythonを使った年齢計算

```python
import datetime

def age_now():
    now_time = str(datetime.date.today())
    split_time = now_time.split("-")
    print("生年月日を入力してください")
    b_year = int(input("年:"))
    b_month = int(input("月:"))
    b_day = int(input("日:"))

    age = int(((int(split_time[0]) * 10000 + int(split_time[1]) * 100 + int(split_time[2])) - (b_year * 10000 + b_month * 100 + b_day)) / 10000)
    print("現在の年齢は{}才です".format(age))

age_now()
```

簡単にこんな感じです。int関数使いすぎててよくわからなくなってますね。気が向いたら減らしてみます。

***追記***

```python
import datetime

def age_now():
    time = str(datetime.date.today())
    now_time = int("".join(time.split("-")))
    print("生年月日を入力してください")
    birthday = int(input())
    age = int(((now_time - birthday) / 10000))
    print("現在の年齢は{}才です".format(age))

age_now()

```
こんな感じですね。入力自体はさっきの奴のほうがやりやすかったですかね。まぁ今回はこんなところで・・・
Tag and Layersのレイヤーをスクリプトから設定する

#概要

本記事はUnity #2 Advent Calendar 2019　の16日目の記事です。
今回UnityのTag and Layersをスクリプトから設定する方法を書きます。

#前提
Tag and Layersをスクリプトから設定したいケースがあります。
それは、例えば自作ライブラリを提供するときにそのライブラリが特定のレイヤーを利用していて、そのレイヤーをライブラリを使うプロジェクトに追加で上書きしたい場合です。

え、それってProjectSettings/TagManager.assetを渡せばいいんじゃない？ってなるかもしれませんが、TagManager.assetだとレイヤー情報が上書きされてしまうし、その他のtagやSortingLayerの情報も上書きされてしまうので、レイヤーのみを追加したい場合とかだとスクリプトから設定したくなります。

#TagManagerをロードして上書きする

下記ソースでtagManagerの情報をロードします。

```csharp:
 var tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
```

そして、下記のように上書き設定したいレイヤーのレイヤー名と番号を定義したのちに上書きます。

```csharp:
    //設定したいレイヤーのレイヤー名
    private static string[] requiredLayers =
    {
            "Layer1",
            "Layer2",
    };

    //設定したいレイヤーのレイヤー番号
    private static int[] requiredLayerIds =
    {
            26,
            27,
    };

    /// <summary>
    /// 設定したいレイヤーを上書きする
    /// </summary>
    /// <param name="tagManager"></param>
    private static void CheckLayers(SerializedObject tagManager)
    {
        //layer情報を取得
        var layersProp = tagManager.FindProperty("layers");
        var index = 0;
        foreach (var layerId in requiredLayerIds)
        {
            if (layersProp.arraySize > layerId)
            {
                //レイヤーIDのレイヤーを取得
                var sp = layersProp.GetArrayElementAtIndex(layerId);
                if (sp != null && sp.stringValue != requiredLayers[index])
                {
                    //レイヤー上書き
                    sp.stringValue = requiredLayers[index];
                    Debug.Log("Adding layer " + requiredLayers[index]);
                }
            }

            index++;
        }
    }
```

最後に、上書きした情報を反映します。

```csharp:
 tagManager.ApplyModifiedProperties();
```

# ついでにPhysicsのLayerCollisionMatrixも設定する

下記コマンドでPhysicsのLayerCollisionMatrixの衝突を無視する設定が出来ます。
後はそれぞれ衝突を無視したいレイヤーの組み合わせを網羅すればOK。

```csharp:
 var ignoreLayer1 = LayerMask.NameToLayer("Layer1");
 var ignoreLayer2 = LayerMask.NameToLayer("Layer2");
 Physics2D.IgnoreLayerCollision(ignoreLayer1, ignoreLayer2, true);
 Physics.IgnoreLayerCollision(ignoreLayer1, ignoreLayer2, true);
```

# まとめ

以上のコマンドを[MenuItem]で設定してUnityのツールバー等から実行できるようにして、
渡したライブラリを導入した後に設定させれるようにすると完了。

```csharp:
    /// <summary>
    /// レイヤーを設定する
    /// </summary>
    [MenuItem("Init/InitLayer")]
    static void InitializeLayers()
    {
        SerializedObject tagManager =
            new SerializedObject(AssetDatabase.LoadAllAssetsAtPath(
                "ProjectSettings/TagManager.asset")[0]);

        CheckLayers(tagManager);

        //変更を反映する
        tagManager.ApplyModifiedProperties();

        //LayerCollisionMatrixもついでに設定する
         var ignoreLayer1 = LayerMask.NameToLayer("Layer1");
         var ignoreLayer2 = LayerMask.NameToLayer("Layer2");
         Physics2D.IgnoreLayerCollision(ignoreLayer1, ignoreLayer2, true);
         Physics.IgnoreLayerCollision(ignoreLayer1, ignoreLayer2, true);
    }
```
文字列の空白や改行の削除まとめ

Ruby技術者認定試験のシルバーで狙われる、空白や改行の削除をまとめてみた。

- chomp
- chop
- strip
- lstrip
- rstrip


```ruby
a = "\n \r abc \n \r"

a.chomp
=>"\n \r abc \n " #空白が残っている
a.chomp.chomp.chomp.chomp.chomp
=>"\n \r abc \n "

a.chop
=>"\n \r abc \n "
a.chop.chop.chop.chop.chop
=> "\n \r ab"

a.strip
=> "abc"
a.strip.strip.strip.strip.strip
=> "abc"

a.lstrip
=> "abc \n \r"
a.lstrip.lstrip.lstrip.lstrip.lstrip
=> "abc \n \r"

a.rstrip
=>"\n \r abc"
a.rstrip.rstrip.rstrip.rstrip.rstrip
=>"\n \r abc"
```

またそれぞれ！をつけると破壊メソッドになる

追記
"\r", “\r\n”, “\n”は改行コード1文字とみなされるので

```ruby
p ["foo\r\n", "bar\n", "baz\n\r"].map {|i| i.chomp }
 => ["foo", "bar", "baz\n"]
```
上記のような出力になる
Microsoft Learn 16日目

12月は、毎日 [Microsoft Learn](https://docs.microsoft.com/ja-jp/learn/) を受講していこうという記録。
※受講後に書き込むので公開に間に合わないこと多々あります(アドベントカレンダーの意味ｗ)

12/16 は
**Core Cloud Services - Azure コンピューティング オプション**
 - Azure 仮想マシンを試す

を受講。以下、簡易メモ。

# Core Cloud Services - Azure コンピューティング オプション
## Azure 仮想マシンを試す
VM(Virtual Machine)は、次を必要とする場合に最適な選択肢である。
・オペレーティング システム (OS) の完全な制御
・カスタム ソフトウェアの実行機能
・カスタム ホスティング構成を使用する
Azure VM は、VM を実行する物理的なハードウェアを購入して維持する手間を省き、仮想化がもたらす柔軟性を提供するもの。
事前構成された VM イメージを選択すると、VM を数分で作成してプロビジョニングすることができる。
イメージ＝ VM の作成に使用するテンプレート。

### VM を利用してクラウドに移行する
物理サーバーのイメージを作成し、VM 内でホストできる。
物理的なオンプレミス サーバーと同様、VM には保守管理が必要となる。

### Azure で VM をスケーリングする
テスト、開発、または小規模タスクのために1つの VM を実行することができる。
どのようなアップタイム要件でも満たせるように、以下のようないくつかの機能が用意されている。
#### 可用性セット
計画または計画外のメンテナンス中、アプリケーションの可用性が維持されるよう、2つ以上の VM を論理的にグループ化したもの。
可用性セットにより次のものを取得できる。
・それぞれに専用の電源とネットワークリソースを備えたサーバーラックがある、最大3つの障害ドメイン
・5つの論理的な更新ドメイン(その後最大20個まで増やすことが可能)

可用性セットに料金は発生せず、可用性セット内の VM に対してのみ料金を支払う。

#### Virtual Machine Scale Sets
負荷分散が行われる同一の VM のグループを作成して管理できるもの。
VM を複製した場合、通常複数の Web サイトのインスタンス間で要求をルーティングする追加サービスを構成する必要があるが、Scale Sets で代わりに実行できる。
大量の VM を数分で一元的に管理、構成、更新し、アプリケーションの可用性を高めることができる。

#### Azure Batch
大規模なジョブ スケジューリングとコンピューティング管理を可能にする。
何十～数千もの VM にスケーリングできる。
バッチで次のことが行われる。
・VM のコンピューティングのプールを代わりに開始する
・アプリケーションとステージング データをインストールする
・ジョブと所有するすべてのタスクを実行する
・エラーを識別する
・作業を再度キューに入れる
・作業が完了したらプールをスケールダウンする
Microsoft Learn 15日目

12月は、毎日 [Microsoft Learn](https://docs.microsoft.com/ja-jp/learn/) を受講していこうという記録。
※受講後に書き込むので公開に間に合わないこと多々あります(アドベントカレンダーの意味ｗ)

12/15 は
**Core Cloud Services - Azure コンピューティング オプション**
 - Azure コンピューティングの重要な概念

を受講。以下、簡易メモ。

# Core Cloud Services - Azure コンピューティング オプション
## Azure コンピューティングの重要な概念
### Azure コンピューティングとは
クラウドベースのアプリケーションを実行するためのオンデマンドコンピューティング サービスのこと。
仮想マシンとコンテナーを使用して、マルチコアプロセッサやスーパーコンピューターなどのコンピューティングリソースを提供する。
Azure でコンピューティングを実行する一般的な手法は 4 つある。
・仮想マシン
・コンテナー
・Azure App Service
・サーバーレス コンピューティング

## 自分に適したコンピューティング戦略
クラウドのコンピューティング戦略を選択するときに、"all or nothing" のアプローチを採用する必要はない。
仮想マシン、コンテナー、App Service、サーバーレスコンピューティングにはそれぞれ、長所があり、他の選択肢と天秤にかける妥協点がある。
Power BI Macでも、iPadでも、ブラウザでそれなりにレポートが作れるよ！（かなり制約あるけど・・・）

# 本投稿は

Power BI Advent Calendar 2019 の記事です

# Mac ユーザーのつらいところ・・・

Power BI Desktop 使えないんですよ！！
これホント辛い！
なのでWindows機を別途用意するか、VMでWindowsマシンを用意してそれからPower BI Desktopダウンロードして・・・
みたいなことしなきゃだめなんですよね。

**MacでもPower BI使いたい！**
そんなユーザーめちゃくちゃいるんじゃないかと思うんです。

# ブラウザで・・・

実はブラウザ上でも、レポートやダッシュボードって作れるんですよ。
ブラウザが使えるなら、MacやiPadでもPower BI 作れますよね！
じゃあ実際ブラウザでどこまでできるか？？
ちょっと試してみたのでまとめてみます。

# Power BIをブラウザ上で作成する
## できること

### データセット

* ローカルファイル・OneDrive・SharePointのエクセルファイル、CSVファイルなどを取り込むことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/573de5a2-7fa3-6f5b-e9d2-6c47fb81705d.png)
※注意、読み込みデータがUTF-8以外だと文字化けします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/9b399eac-7f19-51c8-65ec-ca7c52f0b055.png)


* Azure上のSQL ServiceやData WareHouse などを使用することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/81620ed9-f257-a0e8-5136-d5669ade25d5.png)

* ストリーミングデータセットを作成し利用することが可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/cf292797-f7e0-a062-1087-39b592030c2b.png)


### レポート

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269159/f17c3e1a-f649-51d4-3d89-46893bd8cc1b.png)
作成したデータセットを参照させてレポートを作ることは可能です。


## できないこと

### データセット

SharePointのカスタムリストやJSONファイル、Azureの指定されたサービス以外のクラウドデータサービスなどをデータセットとして作成することができません。

### レポート

* クエリエディタが使用できません
* メジャーを作成することができません
* 表を作成したり、編集したりすることができません
* 複数のデータセットを指定することができません
* リレーションを指定できません
などなど

# まとめ

ブラウザ上だとかなり機能制限はされるようですが、簡単なデータセットを元にレポートを作れたりするので
うまく使いこなせば、MacやiPadでもいい感じのレポートは作れると思います。
また、@yugoes1021 さんの[リアルタイムアンケート](https://www.slideshare.net/yugoes1021/ms-forms-ms-flow-power-bi-83167435) は実はブラウザのみで作成可能ですので、iPadでちょちょいと作ってあげることもできます！
是非試してみてください！
Amazon ECSを用いてRuby on Railsを起動し、運用時に起きた課題の解決案

# 書いたこと
- Amazon ECSを用いてRuby on Railsを動かす構成の紹介
- そこで出た課題と解決手法の紹介

# ECSを用いてRuby on Railsを動かす構成の紹介
- Amazon ECSとは
    - Amazon Elastic Container Service
        - コンテナオーケストレーションサービス
- Dockerコンテナ化されたアプリケーションをECSで定義してデプロイ・スケールができる

## ECSでの定義例
大きく分けて以下3つの定義をECS上で定義すれば環境が構築できる
![スクリーンショット 2019-12-14 12.40.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140026/006be512-f5fb-2652-00c8-958ebfb4f5d7.png)

### 1. クラスタの定義
- グループの定義
    - 「本番環境」など

### 2. サービスの定義
- クラスタ内で公開したいサービスの定義
    - Railsで動いている〇〇サービスなど

### 3. タスクの定義
- サービスを実行するためのDockerコンテナと、コンテナを動かすための条件等の定義
    - Nginx + Railsで動かすためにそれぞれのDockerコンテナを用意と指定
    - それぞれの仮装CPUと仮想メモリの指定
    - 必要な環境変数の設定
    - 起動のためのコマンドの指定


## インフラ構成
クラスタ・サービスを指定してデプロイする

```
$ ecs deploy [クラスタ] [サービス] ...
```

そうすると、ECSのエージェントがタスク定義に従ってDockerコンテナをデプロイしていく
![スクリーンショット 2019-12-14 12.39.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140026/53e6fdde-c4e1-8fbf-25b0-ecbc519a2274.png)

# 運用開始、そして・・
- スキーマ変更、その他Rake Task
    - `bundle exec rails db:migrate`
    - などなど、Webアクセス経由とは別の重い処理もRailsのdocker内で行った
- 定期実行
    - 本当に実行されたのだろうか

=>コマンドによってCPU・メモリ負荷上昇・最悪の場合アクセス不能に :innocent:
=> 定期実行の監視の不確実さ :innocent: 

# タスク実行専用のサービスを作ろう

## クラスタの定義例
- 新しいサービスを定義した
    - Webからのアクセスがないサービスなので、Railsのコンテナのみ定義

![スクリーンショット 2019-12-14 14.55.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140026/6850bf95-648d-8c92-d626-6bbae34d1151.png)


## インフラ構成
```
$ ecs deploy [クラスタ] [サービス] ...
$ ecs run [クラスタ] [サービス] -c [コマンド]
```
サービスのデプロイと、コマンドの実行

![スクリーンショット 2019-12-14 14.55.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140026/1aca4bee-7d31-0e01-37cb-8df582c96135.png)

コマンド実行後はサービスは終了する

# 定期実行を行う
- タスクスケジューラーが定義できる
    - どのサービスを・どれくらいの頻度で、もしくはどのような時に・何を実行するかを指定
- 実行時にサービスがデプロイされ、コマンドを実行する
- コマンド実行後はサービスは終了する
![スクリーンショット 2019-12-14 15.10.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/140026/b6a53ce5-906e-794b-a7a9-99bec4afb47b.png)
- 実行ログやトリガーの結果はCloudWatchで確認

# どうなったか
- サービスがWebと別に切り出されたので、重いコマンドを実行してもクラスタ内の別の仮装環境の中で実行されるようになった
    - Web側がコマンドに起因してアクセス不能になることはなくなった
- 定期実行の監視が容易になった
    - CloudWatchイベントの通知を利用し、監視できるようになった
    - Webから独立したCloudWatchログを確認できるようになった

:grin: