OmniAICreator/Qiita-1.07M huggingface dataset:
【JavaScript】fileオブジェクトのファイル名を変更したオブジェクトを取得する

## 概要
JavaScriptではFileAPIを使用することで、クライアントPCからのファイルの読み込みを行うことができます。実装は[JavaScript でファイルを読み込む方法](https://javascript.keicode.com/newjs/how-to-read-file-with-file-api.php)の記事にのっています。
あまり無いケースだと思いますが、この受け取ったfileオブジェクトでのファイル名を変更する場合、どうすれば良いのかというのをメモ書きします。

## 対応
fileオブジェクトのファイル名を直接変更することはできません。
対応の一つとして、[こちら](https://stackoverflow.com/a/46327909)のstackoverflowの記事にある通り、元のfileオブジェクトをベースにファイル名を変更して、新しくファイルオブジェクトを作成する方法が挙げられます。

## 実装サンプル
内容は上記のstackoverflowの記事とほぼ同様ですが、元の拡張子を取得して新しいファイルにも同様の拡張子を付与する実装を以下にのせます。

```javascript
// ファイルはFileAPIで取得している前提
const fileName = inputFile.name;
// ファイルの拡張子を取得
const fileExtention = fileName.substring(fileName.lastIndexOf(".") + 1);
const blob = inputFile.slice(0, inputFile.size, inputFile.type);
// ファイル名称変更後のファイルオブジェクト
const renamedFile = new File([blob], "変更後のファイル名." + fileExtention, {type: inputFile.type});

```
ZAPで出てくる動的スキャン用語まとめ

# セキュリティ勉強

脆弱性の診断の勉強で、ZAPで、脆弱性診断をしています。動的スキャンした際に分からなかった用語、学んだことをまとめていきます。



# zap公式ドキュメントはこちら。
https://www.zaproxy.org/
https://www.zaproxy.org/docs/


# Parameter Tampering
パラメータタンパリング

・タンパリングとは「いたずらする」「勝手に変える」という意味。
・URL内に埋め込まれたパラメータ値を改ざんしてしまう。


# buffer overflow
バッファ オーバーフロー

バッファ
→コンピュータで、データを一時的に記憶する場所。

・攻撃するコンピュータに、許容するより多くのデータを送りつけ、誤作動を起こさせてしまう。


#Path Traversal
パストラバーサル
#Directory Traversal
ディレクトリトラバーサル

同じ。
・「../」を利用してディレクトリを遡り、本当なら、アクセスが禁止されているディレクトリにアクセスしてしまう。


#File include
ファイルインクルード

・include　
→ファイルから読み込む記述。
・プログラムの中で他のファイルをincludeしている場合、攻撃者が意図的にそのファイル名を修正して不正にファイルを処理させてしまう。



#Directory listing
ディレクトリリスティング

・Webサーバ（Apacheなど）には、URLで指定するとディレクトリに含まれるファイル一覧を表示する機能がある。
・ディレクトリリスティングされた時にindex等のファイルが置かれていないと、ファイル全容が第三者に知られてしまう。
Typoraをノート風エディタにする

# ■ 概要
## 背景
+ 手書きノート風に書けるノートアプリ、メモアプリ、エディタが無いか色々と探していたが、目ぼしいものがなかった。
+ もともと使用していた「Typora」に、<u>独自でcssをいじることができる仕組み</u>があることを思い出した。<br>=> もしかして、ノート風に似せることができるのではと気づいた。

## デモ画像
![typora1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100765/06bb2a31-8fa9-8ea7-c672-331f0dcb0e58.png)



:::note warn
文字の大きさが、h1,h2の時は2行分、それ以外の時は1行分の大きさになってます。
:::

## 技術要件
|技術|雑な説明|URL|
|:-:|:-:|:-:|
|Typora|Markdownエディタ|[リンク](https://typora.io/)|
|あんずもじ|手書き風フォント(ここはお好みで)|[リンク](https://fontdasu.com/141)|

## 前提
+ Typoraがインストールされていること
+ Windowsであること(Macも本手順の一部を読み替えていただければ可能です。)

# ■ 手順
## 1. 以下のcssを配置する
以下のコード(css)を「base.user.css」というファイル名で、以下のファイルパスに保存する
> <u> %USERPROFILE%\AppData\Roaming\Typora\themes\base.user.css </u>

```css
// ここはフォントの種類によって適宜変更してください。
@font-face {
    font-family: 'APJapanesefont';
    src: url('./anzumoji/APJapanesefont.ttf');
}

#write, #write * {
  font-family: 'APJapanesefont' !important;
}

// 
#write {
  background-color: #fff !important;
  background-image: linear-gradient(180deg, #ccc 1px, transparent 1px) !important; 
  background-size: 100% 2em !important; 
  line-height: 2em !important;
  padding-bottom: 1px !important;
}

*:not(#typora-sidebar) {
  font-size: 1em !important;
  line-height: 2em !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}

h1:not(#typora-sidebar),
h2:not(#typora-sidebar) {
  font-size: 1.5em !important;
  line-height: 2em !important;
  margin-top: 0 !important;
  margin-bottom: 0 !important;
  padding-top: .25em !important;
  padding-bottom: .40em !important;
}

h1:not(#typora-sidebar) {
    text-decoration: underline;  
}

h3:not(#typora-sidebar) {
    text-decoration: underline;  
}

h3:not(#typora-sidebar)::before {
    content: "■ ";  
    text-decoration: none ;
    display: inline-block;
}

h4:not(#typora-sidebar)::before {
    content: "〇 ";  
}

h5:not(#typora-sidebar) {
    text-decoration: underline;  
}

h5:not(#typora-sidebar)::before {  
    content: "    ";  
    text-decoration: none ;
    display: inline-block;
}


h6:not(#typora-sidebar)::before {  
    content: "    ";  
}

img:not(#typora-sidebar) {
    border: .7px solid black !important;
    border-radius: 3px;
}

table, table * {
  border: none !important;
}
```

## 2. フォントを配置する
フォントをダウンロードし、以下に展開して配置する
> <u>%USERPROFILE%\AppData\Roaming\Typora\themes\anzumoji</u>

## 3. Typoraを起動/再起動する

## 4. テーマを白背景のものにする
Typoraのメニューバーの「テーマ」を押下して、白色のテーマを選択する
※ ここでは、「Pixyll」を採用してます
![typora2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100765/2fb58a60-6e9c-7f19-33c1-567bddbdfad4.png)
ROS×PCL　ROS　Install

今回はROSをインストールします
## Requirement
- OS: Ubuntu18.04
- ROS version: melodic

## Install
以下に従ってインストールしていきます！

http://wiki.ros.org/melodic/Installation/Ubuntu

#### Ubuntu repositoriesの確認

Ubuntu repositoriesの"resiricted", "universe", "multiverse" が許可されてるか確認します
以下を参照してください

https://help.ubuntu.com/community/Repositories/Ubuntu

#### source.listの登録
```
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
```

#### Keyの設定
```
sudo apt install curl # if you haven't already installed curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add -
```

#### Installation
Debian Packageのアップデート

```
sudo apt update
```

次がいくつかあるんですが基本はRecommendedのDesktop-full Installがいいので，それをインストールします

```
sudo apt install ros-melodic-desktop-full
```
なお利用可能なパッケージは以下で確認できます！

```
apt search ros-melodic
```

#### Environment setup
ROSの環境変数をターミナル開くごとに自動的に読み込んでもらえるようにするため，以下のコードを実行しておくとよいです

```
echo "source /opt/ros/melodic/setup.bash" >> ~/.bashrc
source ~/.bashrc
```

#### Install dependencies
ROSをBuildするのに必要なパッケージをインストールします

```
sudo apt install python-rosdep python-rosinstall python-rosinstall-generator python-wstool build-essential
```

##### rosdepの初期化
ROSのいろんな機能を使うためには，```rosdep```を初期化する必要があります
install

```
sudo apt install python-rosdep
```
初期化

```
sudo rosdep init
rosdep update
```

以上でインストール完了です～
おつした！

## next

https://qiita.com/kosei1515/items/1e2d22aa5b23eec4a7b4

## previous

https://qiita.com/kosei1515/items/15b80472055dbc47a967

## references

https://qiita.com/kosei1515/items/bbd73d71aa9bdc6a4467
【Vue.js】監視プロパティ（ウォッチャ）の基本

#はじめに
こんにちは！
今回は【Vue.js】監視プロパティ（ウォッチャ）の基本についてアウトプットしていきます！

#監視プロパティとは
監視プロパティとは、**特定のデータまたは、算出プロパティの状態を監視して変化があったときに登録した処理を自動的に実行できるもの**です。


#書き方・解説
```HTML:HTML
<div id="app">
  <p>
    {{ message }}
  </p>
  <p>
    <input type="text" v-model:value="message">
  </p>
  <pre>{{ $data }}</pre>
<!-- ⏫変更された内容がわかるように記述しておきます。 -->
</div>
```
```Vue.js
var app = new Vue({
  el: "#app",
  data: {
    message: 'Hello Vue.js!'
  },
  watch: {
  //⏫監視プロパティ（ウォッチャ）
    message: function(newValue, oldValue){
                     //⏫新しい値　　　　　⏫変更前の値
      console.log('new: %s , old: %s' , newValue,oldValue)
      　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　//⏫newValue,⏫oldValueがコンソールされる。
    }
  }
})
```

監視プロパティ（ウォッチャ）watchを定義し、``data`のmessage`の値に変更があったらconsoleにlogに新しい値と変更前の値を表示するプログラムを記述しました。

![スクリーンショット 2021-10-19 19.53.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2114750/d27226b0-924f-6639-790d-37c8165dc548.png)

上記のようにテキストに「abc」と続けて値を変更していくと、以下のように新しく入力した値と入力する前の値がconsoleに出力されました。

![スクリーンショット 2021-10-19 19.54.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2114750/603b4344-c4fa-e087-b4b9-3e67408386ab.png)




#まとめ
・監視プロパティ（ウォッチャ）とは、対象の監視物に変化があったとき記述した処理が自動的に実行されるものである。

#最後に
今回は監視プロパティ（ウォッチャ）の基本についてアウトプットしました。
今回の記事を読んで質問、間違い等あればコメント等頂けると幸いです。

今後ともQiitaにてアウトプットしていきます！

最後までご愛読ありがとうございました！
R 事始め(スタートアップ編)

kaggleのコンペは主にRやPythonで記述される。
次回にRを使うときのために備忘録を残します。

## Rインストール手順
公式サイトから各環境に合わせてインストールします。
https://cran.r-project.org/

詳細な方法は以下のページが参考になります。
https://rstudio-education.github.io/hopr/starting.html

## 概要理解

- 動画資料 
  ざっくり概要つかむのに適しています。
  https://www.youtube.com/watch?v=_V8eKsto3Ug

- Rチートシート
  こちらの記事で紹介されているチートシートが網羅的でおすすめです。
  https://qiita.com/ocean_f/items/b47f21d5c992a03e7465

## Package紹介  
  使用頻度の高いpackageの紹介があります。
  https://www.analyticsvidhya.com/blog/2021/04/top-10-r-packages-for-data-science-you-must-know-in-2021/

- 日本語でRの詳しい使い方が記載されているサイト
  http://cse.naro.affrc.go.jp/takezawa/r-tips/r.html

- dplyr
  >dplyrはデータ操作の文法であり、最も一般的なデータ操作の課題を解決するのに役立つ一貫した動詞のセットを提供します。
https://dplyr.tidyverse.org/index.html

- dplyr使い方
  - https://qiita.com/matsuou1/items/e995da273e3108e2338e
  - [Hands-on dplyr tutorial for faster data manipulation in R](https://youtu.be/jWjqLW-u3hc)

- JSONファイルを読み込むパッケージ
  [rjson](https://cran.r-project.org/web/packages/rjson/index.html)

- lubridate 日付型の扱うパッケージ
  - https://kazutan.github.io/SappoRoR8/index.html

- shiny
  pythonでいう[streamlit](https://streamlit.io/)のようなパッケージ
  
  >Shinyは、Rから直接インタラクティブなWebアプリを簡単に構築できるRパッケージです。スタンドアロンアプリをWebページでホストしたり、RMarkdownドキュメントに埋め込んだりダッシュボードを作成したりできます。CSSテーマ、htmlwidgets、JavaScriptアクションを使用してShinyアプリを拡張することもできます。
  https://shiny.rstudio.com/tutorial/

- shinyハンズオン
  
  https://youtu.be/9uFQECk30kA

## R使用時Tips

- 特定ディレクトリのファイルを正規表現で読み込み
  https://stackoverflow.com/questions/11433432/how-to-import-multiple-csv-files-at-once

- 日付フォーマットの文字列の型変換(char->date)
  https://stackoverflow.com/questions/4310326/convert-date-time-string-to-class-date
GitHub Actionsのワークフローがお粗末だったので直したメモ

この記事は[こちら](https://zenn.dev/isana/articles/ciworkflowupdate)からの転載になります。

## モチベーション
半年くらい前によく分からないまま自分で組んだCIがとても良くない感じのままメンテせずに放置していたのでお手入れをしたので、その時の作業メモを公開します。

## やりたいこと
1. PRが作られた時とリモートブランチに変更がpushされた時にテストを実行
2. 特定のブランチ(mainとdev)にPRがマージされたらdeployを実行

## 修正前のyml
`test_build_deploy.yml`という一枚のymlでやりたいことをまとめてやろうとして以下のような形になっていました。

```yml
on:
  push:
  pull_request:
    branches: [main,dev]
    types: [opened, edited, closed, reopened]

jobs:
  test:
    runs-on: ubuntu-latest
    if: github.event.pull_request.merged == false

  build:
    runs-on: ubuntu-latest
    if: github.event.pull_request.merged == true

  deploy:
    runs-on: ubuntu-latest
    needs: build
```

## お粗末ポイント
1. PRを作るとpushとpull_request.opendを同時に検知して初回だけテストが2回キックされる
2. mergeを検知するためにpull_request.closedを設定しなければいけないが、マージせずにPRを閉じた時にもテストがキックされる
3. PRをmergeするとdeployと同時にtestもキックされる

## pushとPRを同時に検知して同じjobが2回走る
お粗末ポイントの1と2は以下のようにして解決しました

- 誤 : 一つのymlでやろうとしていた。

`test_build_deploy.yml`のワークフローをトリガーするイベントは以下のようになっていました。
```yml
on:
  push:
  pull_request:
    types: [opened, reopened]
```

- 正:無理せずにファイルを分割する。

テスト実行用の設定を`test.yml`として用意し、pushをトリガーとします。
```yml
on:
  push:
```

デプロイ用の設定を`deploy.yml`として用意し、PRのclosedをトリガーとします。
```yml
on:
  pull_request:
    branches: [dev, main]
    types: [closed]
```
## deployだけ走ってほしい時にtestも走ってしまう
お粗末ポイント3についてはファイルを分けただけではまだ不十分でした。PRをマージするとマージコミットのプッシュを検知してdeploy.ymlとtest.ymlが同時に走るので`branch-ignore`で回避しました。

`test.yml`
```yml
on:
  push:
  	branches-ignore:
      - "main"
      - "dev"
```

`deploy.yml`
```yml
on:
  pull_request:
    branches: [dev, main]
    types: [closed]
```

## baseとheadがどっちがどっちか分からん
[![Image from Gyazo](https://i.gyazo.com/b665eb2d06b8899b86e85545849a0123.png)](https://gyazo.com/b665eb2d06b8899b86e85545849a0123)
**ベースブランチ(base)**
`github.base_ref`

**比較ブランチ(compare)**
`github.head_ref`
I
【PHP】Yahoo 気象情報API から雨量情報を取得してみた

# 初めに
ダッシュボード作成のために、雨量をAPIにて取得する
[YOLP 気象情報API ](https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html)を使用する。
⇒PHPを用い、HP等で表示させる用途を想定

>[リファレンス](https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html)より引用
>指定した緯度経度の雨の強さを取得できるAPIです。
>現在時刻の降水強度実測値から、60分後までの降水強度予測値を取得できます。

## 環境
- Windows10
- PHP 8.0.6

## パラメータ
* appid => Yahooデベロッパー登録をし、取得する
* coordinates => 経度,緯度(世界測地系)
* output =>  出力形式を指定(json/xml)
* date =>日時を指定(YYYYMMDDHHMI形式)

本コードではparamsにてパラメータをセットしてください。

# コード
```PHP:yahoo_weather_api.php
<?php
//Yahoo weather APIにて気象情報を取得する
$api = 'https://map.yahooapis.jp/weather/V1/place?';
//パラメータをセット
$params = array(
    "appid" => "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    "coordinates" => "135.000000,35.0000000",
    "output" =>  "json",
    "date" => date("YmdHi", strtotime("now")),
);

$url = $api . 'appid=' . $params['appid'] . "&coordinates=" . $params["coordinates"] . "&output=" . $params["output"];
$weather_json = file_get_contents($url);
$weather_array = json_decode($weather_json, true);

//降水強度実測値を変数に格納
$date = $weather_array["Feature"]["0"]["Property"]["WeatherList"]["Weather"]["0"]["Date"];
$rainfall=$weather_array["Feature"]["0"]["Property"]["WeatherList"]["Weather"]["0"]["Rainfall"];

//出力
echo "日時:" . $date . "\n";
echo "雨量:" . $rainfall. "\n";
```
# 終わりに
実測値といえども、気象レーダー解析雨量のため実際の降雨量と差異があるが、アメダスがない地点の雨量も取得できるのは魅力的。
>※降水強度は、気象レーダーで観測された降水の強さを時間雨量(mm/h)に換算した値で、実際の雨量とは異なります。

# 参考サイト
https://developer.yahoo.co.jp/webapi/map/openlocalplatform/v1/weather.html
LINE Botに格言を表示

[前回](https://qiita.com/tomosukesan/items/738468d8e6fd1576f615)の記事ではobnizに格言を表示しました。

今回は格言を表示してくれるLineBotを作成しました。

##流れ
まずはこちらの記事を参考にLine Botを作ります。
おうむ返しまで完了させます。

https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d

`server.js`を変更するので一度、`control + C`で`node server.js`を終了させます。


mylinebotフォルダの直下で
`npm i axios`を実行します。

そうすると`package.json`の中に`axios`が追加されます。

`server.js`ファイル内の`'use strict'`の次の行あたりにAPIを叩くための呪文`const axios = require('axios');`を記載。

コード終わりの方の`//実際に返信の言葉を入れる箇所`のところに表示させたいAPIの情報を記載。

全体像は次の通り

```javascript
'use strict';

//APIを叩ける
const axios = require('axios');

const express = require('express');
const line = require('@line/bot-sdk');
const PORT = process.env.PORT || 3000;

const config = {
    channelSecret: 'チャネルシークレット',
    channelAccessToken: 'チャネルアクセストークン'
};

const app = express();

app.get('/', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);

    //ここのif分はdeveloper consoleの"接続確認"用なので削除して問題ないです。
    if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){
        res.send('Hello LINE BOT!(POST)');
        console.log('疎通確認用');
        return;
    }

    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

async function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }
  const res = await axios.get('https://api.adviceslip.com/advice'); //APIのURL
  console.log(res.data.slip.advice);

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: res.data.slip.advice //実際に返信の言葉を入れる箇所
  });
}

app.listen(PORT);
console.log(`Server running at ${PORT}`);

```
これでLINE Botになにか話かけると格言のAPIがランダムで出力されます。


![スクリーンショット 2021-10-24 22.46.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1622645/93717c1d-a639-a38e-596c-602cd4a54403.png)

こんな感じです〜。
ありがとうございました！
ROS×PCL　ROS　Indroduction

今回はROSの概要について説明していきます！

## ROSとは
ROSとはRobot Operating Systemの略です．ロボットのソフトウェア開発者向けにライブラリやツールを提供するロボット開発プラットフォームです．

## ROSのメリット
- 分散処理方式のミドルウェアであること

つまりいろんな処理を並列で回せるって感じです．何かプロジェクトがあったらそれを実現するためにいろんなグループができてそれぞれでタスクをクリアしていくみたいなのと一緒です．ロボットに必要な処理を分担して並列で回すことでリアルタイムでのロボット操作が可能になります．

## ROS version

|OS|version|
|----|----|
|Ubuntu18.04|melodic|
|Ubuntu20.04|noetic(ROS)  foxy(ROS2)|
|Windows|foxy(ROS2)|

現状ではmelodicが多く利用されていますが，そのうちサポートが終了するようです．しかしnoeticおよびROS2はいまだライブラリの充実が完全ではないという面もあるようで，現状ではmelodicを推奨しているところが多そうです．

## Next

https://qiita.com/kosei1515/items/4da703e9390f98b0247d


## Reference

https://qiita.com/kosei1515/items/bbd73d71aa9bdc6a4467
ROS×PCL 目次

# ROS×PCL　目次

編集中です

## ROS
1. [Introduction](https://qiita.com/kosei1515/items/15b80472055dbc47a967)
1. [Install](https://qiita.com/kosei1515/items/4da703e9390f98b0247d)
1. [Create workspace](https://qiita.com/kosei1515/items/1e2d22aa5b23eec4a7b4)
1. [Create package](https://qiita.com/kosei1515/items/ac80b221e34516ce568d)
1. [Pub&Sub tutorial](https://qiita.com/kosei1515/items/19d14c069a6d34116ff9)
1. Make launch file
1. Make msg file
1. rviz

## PCL
1. Introduction
1. Install
1. Getting Started
【Nuxt.js】GoogleフォントをTailwindCSSで表示させる

#はじめに
こんにちは。
こちらの記事では、GoogleフォントをTailwindCSSを用いて表示する方法を記しています。
誤っている点がございましたらコメントいただけると幸いです。

##実装手順

###1. nuxt.config.jsの設定
まずは`nuxt.config.js`のhead内のlinkに、使用したいGoogleフォントのリンクを貼り付ける。

```nuxt.config.js
head: {
  link: [
  { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Sen&display=swap' }
}
```

###2. tailwind.config.jsの設定
`tailwind.config.js`のthemaのextendにfontFamilyを追加する。（複数追加するときはカンマで区切る）

```tailwind.config.js
theme: {
  extend: {
    fontFamily: {
      sen: ["'Sen', sans-serif"]
    }
}
```

###3. クラスの追加
使用したいテキストにクラスを追加する。

```javascript:index.vue
<template>
  <h1 class="font-sen">Title</h1>
</template>
```

<br>


#おわりに
ここまでGoogleフォントをTailwindCSSを用いて表示する方法についてまとめました。
Tailwindを使用する際、個別で設定する箇所が出てくるので今後もこまめに記事にしていければと思います！
以上、最後まで読んでいただきありがとうございました！
GASで、Twitter宣伝の定期実行をしよう

# GASで、Twitter宣伝の定期実行をしよう

## 定期実行の場をローカルからクラウドへ

今までPythonでスクリプトを書いたものを、Macのあれこれを使って定期実行する、といった方法をとっていました。

詳細→[どうせ起動してるPCを使って、宣伝ツイートを自動でRTする - Qiita](https://qiita.com/sotono/items/9088a60f55b5c23b8063)

ところが、この運用だと以下のデメリットがあることが運用してから気づきました。

- Macの電源を落としてる、Bootcampを使っていると定期実行されない
- ローカル上においてあるので管理が面倒
- スクリプトごとにPipenv仕込んでるので容量をある程度食う ~~(それはお前の問題だよ)~~

とにかく、**手段としてはローカルすぎるので、クラウドのなんちゃらを使って定期実行できないかな**という目的を以ていろいろ探してみました。

一番上に来るのがAWS、なんか難しそうなのでパスします。
なんかいい方法…いい方法…

…待てよ、**最近業務で使い始めたGASなら定期実行楽になるんじゃね？**

というわけで、GASを使って定期実行のあれこれを整えていきます。


## 制作環境を整える

普段使っているエディターがVScodeなので、せっかくなら慣れたコイツを使いたいぜ！
…ということで、以下の記事を参考にVScodeでGASが使えるように環境を整備します。

[GAS用のCLIツール clasp を使ってGASをローカルで開発して実行するの巻。 - Qiita](https://qiita.com/jiroshin/items/dcc398285c652554e66a)

```bash=terminal

# インストール
$ npm install -g @google/clasp

# Googleにログインして情報を引っ張ってくる
$ clasp login

# ローカル作業用フォルダーを用意して移動する
$ mkdir [作業用のフォルダー] 
$ cd [作業用のフォルダー]


# プロジェクト作成
$ clasp create
# ~~
# なんかいろいろ出るので、今回はStandaloneを選択
# ~~

# スクリプトを作成してクラウド側に反映
$ touch main.js
$ clasp push

# 実際どうなってるのかGASの画面を出す
$ clasp open

```

だいたいこのような流れでVScodeでGASが書けるようになります。
あとは、変更するたびに`clasp push`したり、
単純なものならコピペしたりして対応します。

## Pythonで定期実行していたスクリプトをGASに変換する

Pythonで運用していたので、これを基にGASで通じるように書き直します。
Pythonのコードは、[どうせ起動してるPCを使って、宣伝ツイートを自動でRTする - Qiita](https://qiita.com/sotono/items/9088a60f55b5c23b8063) を参考にしてください。

```javascript=main.js

const API_KEY = "";
const API_SECRET = "";
const ACCESS_TOKEN = "";
const ACCESS_TOKEN_SECRET = "";

var tweet_id = "1447156148084174852";

const RT_URL =
    "https://api.twitter.com/1.1/statuses/retweet/" + tweet_id + ".json";

const UNRT_URL =
    "https://api.twitter.com/1.1/statuses/unretweet/" + tweet_id + ".json";

Logger.log(RT_URL);
Logger.log(UNRT_URL);

// Twitter APIの認証とレスポンス取得
function run() {
    var service = getService();
    var options = {
        method: "post",
        muteHttpExceptions: true,
        validateHttpsCertificates: false,
        followRedirects: false,
    };

    if (service.hasAccess()) {
        var res = service.fetch(UNRT_URL, options);
        var res = service.fetch(RT_URL, options);
    } else {
        Logger.log("認証失敗");
        return;
    }
}

function getService() {
    return OAuth1.createService("Twitter")
        .setConsumerKey(API_KEY)
        .setConsumerSecret(API_SECRET)
        .setAccessToken(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)
        .setAccessTokenUrl("https://api.twitter.com/oauth/access_token")
        .setRequestTokenUrl("https://api.twitter.com/oauth/request_token")
        .setAuthorizationUrl("https://api.twitter.com/oauth/authorize");
}


```


### 躓いたところ

`tweet_id`は横着せずに文字列にしないとエラーを吐きます。
```
{"errors":[{"code":144,"message":"No status found with that ID."}]}
```


## 定期実行トリガーを仕込む

- GAS左側のメニューからトリガーを選択

![スクリーンショット 2021-10-26 21.08.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/207577/4e2e56af-dc33-85d4-3066-0b86ed5543c4.png)

- `トリガーを追加`をクリックし、条件を記入する

## 完了

お疲れ様でした。
これをもとに良い定期実行ライフを送ってください。
【PHP】月位置(方位角･高度･月齢)計算APIから月齢を取得してみた

# 初めに
ダッシュボード作成のために、月齢をAPIにて取得する
[月位置(方位角･高度･月齢)計算API ](https://mgpn.org/api/moon/)を使用すると取得できる。
⇒PHPを用い、HP等で表示させる用途を想定

## 環境
- Windows10
- PHP 8.0.6

## パラメータ
lat (float):必須、取得したい場所の緯度、
lng (float): 必須、取得したい場所の経度

本コードではparamsにてパラメータをセットしてください。

# コード
```PHP:moon_age_api.php
<?php
//moon age APIにて月齢情報を取得する
$api = 'https://mgpn.org/api/moon/position.cgi?json';
$params = array(
    "lat" => "35.000000",
    "lon" => "135.00000",
);
$url = $api . '&lat=' . $params['lat'] . "&lon=" . $params["lon"];

$moon_age_json = file_get_contents($url);
$moon_age_array = json_decode($moon_age_json, true);
$moon age = round($moon_age_array["result"]["age"], 3);

//$moon_ageに今の月齢が格納される
echo "moon age:".$moon_age
```

# 参考サイト
https://mgpn.org/api/moon/
[Flutter Desktop] Windows/MacOSアプリを Microsoft/App Store にリリースする手順 with GitHub Actions

## はじめに
https://docs.flutter.dev/desktop にあるように、2021/10/26 時点では、ストア公開するのはオススメはできません。
そのため、やるとしても個人開発のものに留まるでしょうが、一応方法をまとめておきます。自己責任でお願いします。

筆者は 2021/04 から Microsoft Store と App Store で公開していますが、大きな問題には遭遇していません。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">自分の知る範囲だとなさげです。<br>片方だけだけならFlutter GalleryやAuthPassがありますね。<br>まだ&quot;not recommended&quot;なので当然ではありますが。<br><br>pedaxは個人開発かつdesktop固有の複雑さがあまりない(焦点がffi/isolate)ので、気楽に公開してます。<br>次のflutter meetupあたりで話そうかなと思ったり。</p>&mdash; done 🍵 (@sensuikan1973) <a href="https://twitter.com/sensuikan1973/status/1378021381732925440?ref_src=twsrc%5Etfw">April 2, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

### 補足
この記事に記載した内容の具体例は https://github.com/sensuikan1973/pedax にあるので、必要に応じて参照してください。
また、(Flutter で) Windows/MacOS アプリを開発/公開するにあたり有益なリンクを https://github.com/sensuikan1973/pedax#reference に記載してあるので、そちらも必要に応じて参照してください。

## Microsoft Store
[MSIX](https://docs.microsoft.com/en-us/windows/msix/overview) で公開するのが最も楽です。

既に [Flutter Package | msix](https://pub.dev/packages/msix) があるので、ありがたく使わせてもらいましょう。
最近も開発が活発で最高&感謝一杯のパッケージです。

### 1. Microsoft Developer Acconunt 作成などの準備
https://www.advancedinstaller.com/msix-publish-microsoft-store.html にある通りなので省略。

### 2. アプリ情報の更新
* `windows/runner/Runner.rc` にある `"StringFileInfo"` の更新: https://github.com/sensuikan1973/pedax/pull/83/commits/5e6b43b327ea23e9f673533b0c196181ef1374f5
* アイコンの更新: https://github.com/sensuikan1973/pedax/pull/30

### 3. msix の設定追加
https://pub.dev/packages/msix の通りです。
例: https://github.com/sensuikan1973/pedax/pull/103

### 4. GitHub Actions の追加
Microsoft Store で MSIX を配布する場合、署名は Microsoft がやってくれます。
そのため、Windows 上で MSIX を作成する GitHub Actions を作ってしまえば、Windows マシンがなくとも配布することが可能です。
(とはいえ動作確認のために Windows マシンは当然必須ですが)

こんな感じの workflow を作る。

https://github.com/sensuikan1973/pedax/blob/main/.github/workflows/publish.yaml

### 5. workflow で作った msix を Microsoft Partner Center から提出する
https://github.com/sensuikan1973/pedax#deploy-microsoft-store

参考: https://www.advancedinstaller.com/msix-publish-microsoft-store.html

たったこれだけでおしまいです。

ちなみに、fastlane みたく CLI/API で出来なくもないという話はある。See: https://github.com/sensuikan1973/pedax/issues/235

## App Store

### 1. Apple Developer Program などの準備
iOS の時と変わらないので省略。

### 2. fastlane 用意など
* fastlane: https://github.com/sensuikan1973/pedax/pull/143
* アイコンの更新: https://github.com/sensuikan1973/pedax/pull/30

### 3. GitHub Actions の追加
https://github.com/sensuikan1973/pedax/blob/main/.github/workflows/publish.yaml

### 4. 手元の Mac で fastlane 使って公開
https://github.com/sensuikan1973/pedax#deloy-apple-store
筆者は手元の Mac で fastlane 使って公開しています。
頻繁にストアに公開する人は、それも GitHub Actions に載せるとよいですね。
Lambda (SAM CLI) + MySQL(docker-compose)を利用した REST API作成方法メモ

* ローカル環境でLambda(SAM) + MySQL(docker-compose)を利用したAPIを作成する方法についてメモする。



## AWS SAM(Serverless Application Model)

* サーバーレスアプリのデプロイに特化したCloudFormationの拡張機能。
* YAMLもしくはJSONでリソース定義する。

### SAM CLI

* SAMを利用した開発サポートツール
* サーバーレスアプリのひな形作成、ビルド、AWSへのデプロイができる。
* 開発者のローカル環境でLambdaをDockerコンテナ起動できる。

## 構成

![sam.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/28ba6b48-16b7-3127-3657-9604cc31e631.png)



## 準備

* SAM CLIインストール

  ```shell
  brew tap aws/tap
  brew install aws-sam-cli
  ```

## 実装

### MySQL

* docker-compose.yml

  * MySQL起動用

  ```yaml
  version: "3.3"
  networks:
    container-link:
      name: docker.internal
  services:
    db:
      image: mysql:5.7
      container_name: db
      environment:
        MYSQL_ROOT_PASSWORD: rootpass
        MYSQL_DATABASE: sample_db
        MYSQL_USER: mysqluser
        MYSQL_PASSWORD: mysqlpass
      networks:
        - container-link
      volumes:
        - ./db/data:/var/lib/mysql
        - ./db/my.cnf:/etc/mysql/conf.d/my.cnf
        - ./db/initdb.d:/docker-entrypoint-initdb.d
      ports:
        - 3308:3308
      command: --port 3308
      tty: true
  ```

* 起動

  ```shell
  docker-compose up -d
  ```

  ※起動後テスト用のテーブル作成とデータ登録を行う。

  ※`seq`,`name`,`age`カラムを持つ`sample`テーブルを作成している。

### Lambda

* ひな形作成

  ```shell
  sam init
  ```

  ※ランタイムはPython3.8を選択。

* `template.yml`

  * Hello World Exampleを利用する。
  * 今回はハローワールド用テンプレートをそのまま利用する。

* `requrements.txt`

  ```txt
  requests
  mysql-connector-python
  ```

  ※MySQLコネクター追加。

* Lambda処理(`app.py`)修正

  ```python
  import json
  import mysql.connector
  
  def lambda_handler(event, context):
      # MySQLからデータ取得
      # それぞれの環境に合わせて記述
      sql = "select * from sample;"
      conn = mysql.connector.connect(
          host = "db",
          port ="3308",
          user = "mysqluser",
          password = "mysqlpass",
          database = "sample_db"
      )
      cur = conn.cursor(dictionary=True)
      cur.execute(sql)
      results = cur.fetchall()
      cur.close()
      conn.close()
  	# レスポンスボディ生成
      response = []
      for result in results:
          response +=[
              {
                  "name":result["name"],
                  "age":result["age"]
              }
          ]
      
      return {
          "statusCode":200,
          "body":json.dumps({"results":response})
      }
  ```



## 動作確認

* ビルド

  ```
  sam build --use-container
  ```

* 実行

  ```
  sam local start-api --docker-network docker.internal
  ```

* リクエスト

  ```http
GET /hello HTTP/1.1
Host: localhost:3000
Content-Type: application/json
  ```

* レスポンス

  ```json
{
    "results": [
        {
            "name": "test",
            "age": 25
        },
        {
            "name": "test2",
            "age": 26
        }
    ]
}
  ```

  

## 参考情報

* [Architecting and Building - レガシーシステムにサーバーレスを利用して素早くサービスを追加するには【後半】](https://logmi.jp/business/articles/323350)
Python 正規表現で個人情報マスキング

#個人情報をマスキングする
ダッシュボードでデータを掲載したいものの、個人情報は情報セキュリティ的に載せられない。
CRM（顧客情報管理システム）などで手入力されるデータは表記が揺れるため、
SQLやPowerQueryには限界を感じ、自分の扱う個人情報で特に多い電話番号のマスキングに
正規表現を使用してみた。

SQLでもある程度できるが、使用しているDBで一般的なreplace関数の引数が使えず、
何度も電話番号が出現した場合は対応できないため、諦めた。

#テストデータを用意する
携帯電話と固定電話を含んだリストを作成する。
メモなどに入力され、電話番号以外にも情報があり、それは残したい想定。

```python
data = {'ID':[1, 2, 3, 4, 5],
        'Memo':['2021/12/12_テスト000-0123-4567テスト',
                '2021/12/12_テスト(000)0123-4567テスト',
                '2021/12/12_テスト00001234567テスト',
                '2021/12/12_テスト(00)0123-4567テスト',
                '2021/12/12_テスト(000-123-4567テスト']
        }

df = pd.DataFrame(data)
```

#どの正規表現がいいか試す
##ハイフンや()を取り除いて10桁の数字を指定してみる
ハイフンや()を取り除くために、英数字以外を空白に置き換えて、
10桁の数字の場合、「***」に置き換えしてみた。
英数字以外は__\W__や__[^a-zA-Z0-9]__で表現されるらしい。

結果は以下で、電話番号自体はマスクされた。

```python
df['masked'] = df['Memo'].map(lambda _: re.sub(r'\d{10}', r'***', re.sub(r'\W', r'', str(_))))

#out put
#   ID                  Memo               masked
#0   1   2021/12/12_テスト000-0123-4567テスト  20211212_テスト***7テスト
#1   2  2021/12/12_テスト(000)0123-4567テスト  20211212_テスト***7テスト
#2   3     2021/12/12_テスト00001234567テスト  20211212_テスト***7テスト
#3   4   2021/12/12_テスト(00)0123-4567テスト  20211212_テスト***テスト
#4   5   2021/12/12_テスト(000-123-4567テスト  20211212_テスト***テスト
```
しかし、日付の記号も消えるという、やや残念な様子である。


##3桁-4桁-3桁の正規表現を指定してみる
正確には、
『0-1文字の数字以外+2-3桁の数字+0-1文字の数字以外
+3-4桁の数字+0-1文字の数字以外+4桁』の数字を指定してみた。

0～9の数字は__\d__か__[0-9]__で表現される。
__{}__は、直前のものの繰り返し回数を指定できる。
\d{3}は3桁、\d{2,3}は2～3桁の、0～9の数字のどれがということになる。

__?__は0-1回という意味で、__[^0-9]__か__\D__は数字以外ということで、
__^__を付けるとそれ以外という意味になる。（__^__だけは、文字列の先頭）
__[^0-9]?__とすることで、0-1回の数字以外が入るという意味になる。

結果は以下で、日付は残り、電話番号自体もマスクされた。

```python
df['masked'] = df['Memo'].map(lambda _: re.sub(r'\D?\d{2,3}\D?\d{3,4}\D?\d{4}', r'***', str(_)))

#out put
#   ID                             Memo                 masked
#0   1   2021/12/12_テスト000-0123-4567テスト  2021/12/12_テスト***テスト
#1   2  2021/12/12_テスト(000)0123-4567テスト  2021/12/12_テスト***テスト
#2   3     2021/12/12_テスト00001234567テスト  2021/12/12_テスト***テスト
#3   4   2021/12/12_テスト(00)0123-4567テスト  2021/12/12_テスト***テスト
#4   5   2021/12/12_テスト(000-123-4567テスト  2021/12/12_テスト***テスト
```

#結果
この正規表現に当てはまる電話番号以外データがない限り、
基本的には上記でカバーできそうだった。
正規表現は情報を流し読みするだけでは理解できず、
実際にいろいろ試してみて勉強になった。

正規表現について詳しくは、書籍や他記事を参照してほしい。
続・15分で資料を読んで30分で対話する「あつ読み」(第13回～第28回)

15分で資料を読んで30分で対話する、オンラインでのゆるい読書会を毎週継続しています。
28週続けることができたので、この記事を通して、ここまでのふりかえりをしようと思います。


前回の記事はこちらから。企画の背景と実際の流れや、第1回から第12回までのテーマが書いてあります。

https://qiita.com/flyaway/items/9bafec935a34871bfe80

# 第13回～第28回のテーマ

|回|日付|タイトル|
|---:|----|----|
|13|2021/07/07|[技術的負債とステークホルダと説明責任と](https://logmi.jp/tech/articles/324486)|
|14|2021/07/14|[スケールする要求を支える仕様の「意図」と「直交性」](https://qiita.com/hirokidaichi/items/61ad129eae43771d0fc3)|
|15|2021/07/21|[世界一わかりやすいClean Architecture](https://www.slideshare.net/AtsushiNakamura4/clean-architecture-release)|
|16|2021/07/28|[オープンでフラットな組織が突然「閉鎖的」と言われるとき](https://note.com/4bata/n/n4c1653fe39ac)|
|17|2021/08/04|[マイクロマネジメントは悪か？よりよい組織をつくるためのマネジメント形態についての考察](https://techlife.cookpad.com/entry/2021/07/26/160000)|
|18|2021/08/11|[オブジェクト指向のその前に-凝集度と結合度/Coheision-Coupling](https://speakerdeck.com/sonatard/coheision-coupling)|
|19|2021/08/18|[「遅れ」なんてない](https://irof.hateblo.jp/entry/2018/04/25/231516)|
|20|2021/08/25|[若手から見たリモートワーク時代のチームビルディング](https://engineer.dena.com/posts/2021.01/kencom-hoken-team-building/)|
|21|2021/09/01|[【翻訳】技術的負債という概念の生みの親 Ward Cunningham 自身による説明](https://t-wada.hatenablog.jp/entry/ward-explains-debt-metaphor)|
|22|2021/09/08|(社内資料)提供サービスの非機能要件に関する説明資料|
|23|2021/09/15|[エンジニアリングマネージャーとしてどんなことをしているのか？](https://tune.hatenadiary.jp/entry/2021/09/05/165453)|
|24|2021/09/22|[インストラクショナルデザイン -教えることの科学と技術-](http://kogolab.chillout.jp/textbook/2012_ID_text.pdf)|
|25|2021/09/29|[Effective Remote Working](https://speakerdeck.com/iwashi86/effective-remote-working-f8421bbb-a932-4ac6-a1c3-4b54ff2cf920)|
|26|2021/10/06|(社内資料)顧客情報取扱規程|
|27|2021/10/13|[ゲーム開発 プロジェクトマネジメント講座](http://www.jp.square-enix.com/tech/openconference/library/2011/dldata/PM/PM.pdf)|
|28|2021/10/20|(社内資料)コーチング理論を背景とした研修資料|

# ふりかえり

以下は、私の個人的なふりかえりです。

## よかったところ(Keep)

- 運営を手伝ってくれる人を募集したところ、すぐに立候補者が現れた
    - GASやSlackのワークフローを利用して、一部の作業を自動化してくれた
    - 私が出席できないときも、ファシリテートしてくれた
- 題材の候補に関しては、私だけでなく、参加者が挙げてくれるものが増えた
- 社内資料に関しては、作成者本人が来てくれたことがある。資料や作成の背景について、直接質問できたのがよかった
- 営業部門の人とも会話することができた
- 他部門のチームでも、これを参考に同様の会が開かれていた
- Slack分報で、まとめや感想をつぶやいてくれる人が多い。それが会の宣伝にもなっている
- 直接参加していない人でも、資料の存在を知るきっかけになり、読んでいることがある
- 「あつ読み」という名前は、やっぱり社内Slackでの検索性が高くてよい

## なんとかしたいところ(Problem)

- 題材の資料は多数決で決まるのだが、極端に資料の量(ページ数)が多い日がある
    - もうちょっと範囲を絞れないか？
- 参加者が固定化されてきてしまった
- 若い人が少ない(あつ読み高齢化問題)
- 題材とする資料への投票が少なくなってきてしまった

## これからやってみたいこと(Try)

- 入社1～3年目あたりの人を集めて、その人たちだけで対話する回
- 特定のテーマの深堀り。長めの資料を少しずつ読んでいくイメージ

# まとめ

まず、28回も続けられたのはとても嬉しいです。自分だけの力ではできなかったし、運営を手伝ってくれた人や参加者のおかげだと思っています。

私が7月に読んだ記事に「優れた企画は無限に続けられる」ということが書いてありました。

> 「企画として無限に走れるものが一番優れた企画」というのはすごい言われたんですよね。吉川さんは『世界まる見え』も作ってるんですけど、「日本のテレビが紹介するものより世界で映像が生まれるペースのほうが圧倒的に早いんだから、永遠に番組ができる」と言ってたんです。
> [【令和テレビ談義】～各局エースディレクター編～＜1＞ テレビが伝承すべきノウハウとは](https://news.mynavi.jp/article/20210711-reiwa_tv_discussion1-1/2)

あつ読みは週1ペースで話題の記事を読んでいますが、それより世界で良い記事が生まれるペースのほうが圧倒的に早いはずです。このため、これからも細かい改善を重ねつつ、ずっと続けられる会になるといいな、と思っています。
Cannot find module ... or its corresponding type declarations

## 問題
以下のようにimportしたら、

```typescript
import app from '@/basicAuth'
```

以下のエラーが出ました。

```
TS2307: Cannot find module '@/basicAuth' or its corresponding type declarations.
```

webpackには、alias定義しているのに、何故だろうと思ってました。

## 原因
tsconfig.jsonにaliasを定義していないからです。

## 対応方法
以下のように`baseUrl`と`paths`を定義すれば解決します。

```json
  "compilerOptions": {
    〜〜〜〜 省略 〜〜〜〜
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
   〜〜〜〜 省略 〜〜〜〜
  }
```
デバッグ用：pretty_errors

* インストール

```
pip install pretty_errors
```

* 使用

``` python 
import pretty_errors
pretty_errors.configure(
　　display_locals = True,
　　filename_display = pretty_errors.FILENAME_EXTENDED,
)

```
display_localsは、局部変数を設定する、
filename_display は、ファイルの名を設定する、
他のこと（色など）も設定できる。

* 全局セット

```
python -m pretty_errors
```
docker複数コンテナの立ち上げが面倒な時の.sh（備忘録）

#困った事

テストで、Dockerコンテナの起動停止を何度もしなければいけない。
docker-compose.ymlでいくらサービスを集約しても、graylogやconsulなど、サービス単位が増えると、ymlがあるフォルダまで移動～起動コマンドを叩いたり、docker psするのが非常に面倒。。さらにテスト・本番環境のコンテナが同一環境にあってさらに面倒。。


#解決策検討
| 解決策 | 考察 | 採用 |
|:-----------|------------:|:------------:|
| kubernetes | 学習コスト高い | X        |
| .sh        | 書き方わかんない| O       |


短絡的に、.shを勉強してみたいのと、他に方法思いつかないので.sh作ってみた。
#### 前提条件（補足）
* ymlに「container_name:名前」でコンテナ名を定義する。
* 事情によりymlが格納されたフォルダ名とコンテナ名に違いがある。



#解決
```docker-up.sh
#!bin/bash
# 目的：本番環境コンテナの作成

# 処理の流れ:
# 1 起動中のコンテナ名を取得
# 2 未稼働のコンテナ名を特定
# 3 コンテナ数が一致すれば全量満たしている為処理終了
# 4 差分があれば処理継続

#　登場ファイル
#　/tmp/confirmed_container_counts.txt　     -> 必要コンテナ数
#  /tmp/now_established_container_names.txt  -> 起動中コンテナ名
#  /tmp/now_established_container_counts.txt -> 起動中コンテナ数

# 全量のコンテナ名を配列の値へいざnow(新たにコンテナが増えた際は、コンテナ名をここに追記)
confirmed_containers_name=("containerA" "containerB" "containerC" "containerD" "containerE")

# 想定する必要なコンテナ数を/tmp/confirmed_container_counts.txtに書き出し。
echo ">>>  必要コンテナ(数) >>>"
echo "${#confirmed_containers_name[*]}"
echo "${#confirmed_containers_name[*]}" > /tmp/confirmed_container_counts.txt

# 起動中のコンテナ名を取得(names列名が含まれるため最初の1行削除)
docker ps --format "table {{.Names}}" > /tmp/now_established_container_names.txt
sed -i '1d' /tmp/now_established_container_names.txt

echo ">>>  起動中コンテナ(名) >>>"
cat /tmp/now_established_container_names.txt

sleep 2

# 起動中のコンテナ数を把握
echo ">>>  起動中のコンテナ(数) >>>>"
cat /tmp/now_established_container_names.txt | wc -l
cat /tmp/now_established_container_names.txt | wc -l > /tmp/now_established_container_counts.txt
sleep 5

echo ">>> コンテナ数差分チェック開始 >>>"

# コンテナ数を比較
diff -s /tmp/confirmed_container_counts.txt /tmp/now_established_container_counts.txt
# 必要数を満たしていれば処理終了
if [ $? = 0 ]; then
 echo ">>> 本番コンテナは全量起動中です。処理を終了します >>>"
else
 echo ">>> コンテナは全量ではないため後続処理開始 >>>"

# コンテナが全量でない場合の処理
# 必要コンテナ名群から１コンテナ名ずつ取り出し、起動中コンテナ名と合致するか正誤判定
   for v in ${confirmed_containers_name[@]}
        do
        grep -q $v /tmp/now_established_container_names.txt
        if [ $? = 1 ]; then
                case "$v" in
# 実際は正規表現。
                containerA*)
                cd 'containerAのymlがあるディレクトリ'
                docker-compose up --build -d
                ;;
                containerB*)
                cd 'containerAのymlがあるディレクトリ'
                docker-compose up --build -d
                ;;
                containerC*)
                cd 'containerAのymlがあるディレクトリ'
                docker-compose up --build -d
                ;;
                containerD*)
                cd 'containerAのymlがあるディレクトリ'
                docker-compose up --build -d
                ;;
                containerE*)
                cd 'containerAのymlがあるディレクトリ'
                docker-compose up --build -d
                esac
        fi
   done
fi
```

#振り返り
意図した動作になり、結果作業は楽になった。.shに対するレベルが1ミリ上がった。
**Qiitaデビュー**を果たした。


#反省
* コンテナの増減に合わせて、ハードコーディングしなければいけないので、サービス名は外だしすればよかた。
* 順序性が保たれない想定外の動きをしたため、sleepを利用しているが、正しい使用方法なのか調査。
* コンテナ起動時のエラーに関しては、ファイルへ出力してもいいかもしれない（共有目的）。
* upかdownかstopかを変数に入れて、起動停止ができたらなおよし。
* イメージ構築が不必要な時にbuildいらないな。。
別ウィンドウではなくAndroidStudioの中でエミュレータ機を表示させる方法

#概要
AndroidStudioでエミュレータ機を実行する時、多くの方は別ウィンドウで表示させていると思いますが、ワイドディスプレイでもなければ何気に邪魔になると思います。
そんな時はAndroidStudioの中でエミュレータ機を表示させると扱いやすくなるかもしれません。
#本文
AndroidStudio > Preferences > Tools > Emulator ( WindowsならFile > Settings > Tools > Emulator ) で表示される画面でLaunch in a tool windowにチェックを入れます
![スクリーンショット 2021-10-26 19.14.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/6c127933-ba6e-a381-7343-5bcc131813d9.png)

そうするとAndroidStudioと合体した状態でエミュレータ機が動作します。

![スクリーンショット 2021-10-26 19.13.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/617265/853d6a81-7613-79be-7c70-85ec473daa74.png)


#補足
画面右端のEmulatorタブで表示をON/OFFする事もできます:sunny:
Flutterのaudioplayersで「Attempt to perform seekTo in wrong state」が出た場合の対処方法

###Flutterのaudioplayersで「Attempt to perform seekTo in wrong state」が出た場合の対処方法

使用していたライブラリ及びバージョンは下記の通り

- audioplayers: ^0.20.1

###対処
むやみにAudioPlayerをインスタンス化しなければ大丈夫っぽい
音を鳴らすごとにインスタンス作ってしまっていたのが原因

音（サウンドエフェクト）を出す毎にインスタンスを作っていた時は４０～５０回目で「Attempt to perform seekTo in wrong state」というエラーが出てしまうようになりました

インスタンス化をしない場合120回再生しても大丈夫でした

###インスタンス化を行わない場合の弊害

同時に２つの音が出せなくなる
AudioPlayerをプールして解決

プールすることで、インスタンス数分、同時に音が出せるようになりました。
めでたしめでたし。

下記クソコードを貼っておきます

```SoundManagerPool.dart
import 'dart:async';
import 'package:audioplayers/audioplayers.dart';

class SoundManagerPool {
  int playIndex = 0;
  List<SoundManager> managers = [];

  SoundManagerPool(int managerCount) {
    for (int i = 0; i < managerCount; i++) {
      managers.add(SoundManager());
    }
  }

  Future<void> loadSound(List<String> fileNames) async {
    for (var manager in managers) {
      await manager.loadSound(fileNames);
    }
    return;
  }

  Future<void> playSound(String path) async {
    await managers[playIndex].playSound(path);
    playIndex++;
    if (managers.length == playIndex) {
      playIndex = 0;
    }
    print("playIndex" + playIndex.toString());
    return;
  }
}

class SoundManager {
  late AudioPlayer advancedPlayer;
  late AudioCache audioCache;
  SoundManager() {
    advancedPlayer = AudioPlayer();
    audioCache = AudioCache(fixedPlayer: advancedPlayer);
  }

  Future<void> loadSound(List<String> fileNames) async {
    await audioCache.loadAll(fileNames);
    return;
  }

  Future<void> playSound(String path) async {
    await audioCache.play(path, mode: PlayerMode.LOW_LATENCY);
    return;
  }
}

```
Unity 起動速度を改善する方法（音ファイルのバックグラウンド読込）

##0.0 はじめに
Unityでゲームを作っていると規模が大きくなるにつれ起動速度が遅くなる場合があります。私の経験した症状はアプリ起動後、Unityのロゴが出た後、最初のシーンの画面が出るまでに10秒以上かかるというものでした。
この症状を改善させるうえで分かったこと、行ったことをまとめておきます。

##1.0 症状
アプリ起動するとUnityのロゴが出ますが、その後、最初のシーンの画面が出るまでに10秒以上かかる。

##2.0 原因
起動時に音データを一度に読み込んでいた。この処理に時間がかかっていた。
(音データが多くなればなるほど多くの時間がかかっていた。)

##3.0 対策
音データをバックグラウンドで読み込む設定に変更することで解決しました。

##4.0 解決方法
音ファイルを選択、Inspector（インスペクター）に表示される **Load in Background** にチェックを入れるだけです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352037/35e13306-7b19-abb5-546e-477dfd2f12d3.png)


##5.0 注意
この設定により音ファイルがバックグラウンドで読み込まれるようになり、スタート時の先読み込みが無くなります。このため未読み込みの音の再生時に若干のタイムラグが発生することがあります。BGMなど最初のシーンで再生する必要がある音は **Load in Background** の設定を外しておきましょう。
C# Mathf

##0.0 はじめに
数関係の処理に便利なMathfをいくつかまとめてみました。






##1.0 関数




####1.1 小数点四捨五入等
| 関数 | 説明 | 使用例 |
|:-----------|:-----------|:------------|
| Floor()    | 切り捨て    | Mathf.Floor(1.5f) // = 1|
| Ceil()     | 切り上げ    | Mathf.Ceil(1.5f) // = 2|
| Round()    | 四捨五入    | Mathf.Round(1.5f) // = 2|






####1.2 比較や判定
| 関数 | 説明 | 使用例 |
|:-----------|:-----------|:------------|
| Sign()    |符号取得 (0の場合は1)| Mathf.Sign(-1.5f) // = -1 (= マイナス)|
| Min()     | 最小数を返す    | Mathf.Min(1.5f, 1.7f) // = 1.5|
| Max()    | 最大数を返す | Mathf.Max(1.5f, 1.7f, 1.9f, 2.5f) // = 2.5|






####1.3 特殊計算
| 関数 | 説明 | 使用例 |
|:-----------|:-----------|:------------|
| Pow()    | 指数 | Mathf.Pow(2, 3) // 2の3乗=8|
| Sqrt()     | 平方根 | Mathf.Sqrt(4) // √4 = 2|
| Abs()    | 絶対値 | Mathf.Abs(-1.5f) // = 1.5 |






####1.4 範囲指定
| 関数 | 説明 | 使用例 |
|:-----------|:-----------|:------------|
| Clamp() | 上下限を指定 | Mathf.Clamp(-10, 0, 50); // 0|
| Clamp01() | 値を0 と 1 の間に制限| Mathf.Clamp01(1.5f); // 1|
| Lerp() | 線形補完 第三引数は0~1f| Mathf.Lerp(0.5f, 4.5f, 0.75f); // 0.5と4.5の間75%=3.5|

👍**ポイント**
ベクトルVector3にも同様に線形補完用Lerp()関数があります。（Vector3.Lerp()：ベクトルでの線形補完)






##2.0 定数
| 定数 | 説明 |
|:-----------|:-----------|
| PI    | π (3.1415926...) | |
| Deg2Rad | 度からラジアンに変換する定数 |
| Rad2Deg | ラジアンから度に変換する定数 |
MELSEC-Q INV,MEP,MEF,EGP,EGF使用時の注意点

INV,MEP,MEF,EGP,EGFって言われてもわからんけど、こいつらです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/cf85150d-491b-eb84-a7c6-ee5bbe92dce0.png)
INV 演算結果反転
MEP,MEF 演算結果パルス化
EGP,EGF エッジリレー演算結果パルス化

マニュアル見ててほぇ～って思ったのでちょっと調べてみた。

# 問題

下のSample1とSample2の動作は同じか？(Yはアドレス変えてあります)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/a4470d5e-6628-1928-783e-758230fd8c61.png)



答えは「同じではない」です。
Sample2は見た目通りの動きにはなります。
Sample1は見た目とは違う挙動になります。
マニュアル参照[SH-080804 MELSEC-Q/L プログラミングマニュアル（共通命令編）/演算結果反転 INV]

ではSample1はどのような動きになるのでしょうか？
答えは下の回路と同じ動きになります。
要するに分岐手前のM0は反転対象から除外されるとうことですね。
さてどうしてでしょうね？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/4092b895-7cf8-a7d7-b399-a7a8bc0b7003.png)


下はSample1のリスト表示です。
INVはLD*命令からINVまでの演算結果を反転する動作のようで、LD M0は反転対象に含まれず
LD M1からINVまで、LD M10からINVの演算結果が反転されるようです。
LD*が実行されるたびにそれまでの演算結果はどこかに退避されORB/ANBで退避した演算結果とAND/ORされるようです。
（たぶん…あんまりマニュアルには詳しく書いてなかった）

```plaintext
()は命令ではなく説明です

;Sample1
LD M0
(→演算結果退避A)
LD M1
AND M2
INV
(→演算結果退避B)
LD M10
AND M20
INV
ORB (←退避BとOR)
ANB (←退避AとAND)
OUT Y10
```

Sample1とSample1と同じ挙動の回路は見た目は違いますがリストで見比べるとほぼ同じような処理をしています。

```plaintext
;Sample1と同じ挙動
LD M1
AND M2
INV
(→演算結果退避A)
LD M10
AND M20
INV
ORB(←退避AとOR)
AND M0
OUT Y10
```
# MEP,MEF,EGP,EGFではどうか？
下の回路は[SH-080804 MELSEC-Q/L プログラミングマニュアル（共通命令編）/エッジリレー演算結果パルス化 EGP，EGF]のPointに書いてある回路です。
EGP，EGF命令は，下記の回路ブロックの位置で使用できません。と…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/c8a30adb-d67d-845b-f89a-cf674ff6edd8.png)
実際は使えないのではなくこれもINV命令と同じでパルスの適用範囲が見た目と違うということです。

動かしてみると[Sample3と同じ動き]の回路と同じ動作になります。

```plaintext
;Sample3
LD X0
LD X1
EGP V0
OR X2
ANB
INC W0
```

```plaintext
;Sample3と同じ動き
LD X1
EGP V1
OR X2
AND X0
INC W1
```

# まとめ
回路を整理しようとして大きな回路ブロックを作るとこういう罠にはまるような気がします。
回路の途中での反転やパルス化は便利なので。
気をつけよう😀
【TypeScript】数値を文字列へ変換する方法

# 結論
- 下記の関数を使用する
  - String()
  - .toString

```.ts
const foo: number | undefined  = 0;

const bar = String(foo);
const baz = foo.toString();

console.log(bar); // '0'
console.log(baz); // '0'
```

# String と toString の違い
## String
- `undefined` を変換した時、 `undefined` という文字列に変換されてしまう

```.ts
const foo: number | undefined  = undefined;

const bar = String(foo);

console.log(bar); // 'undefined'
```

## toString
- `undefined` を変換しようとした時、型エラーとなる

```.ts
const foo: number | undefined  = undefined;

const bar = foo.toString(); // 型エラー
```

※文字列を数値へ変換する方法は [こちらの記事](https://qiita.com/y-hira18/items/da2f153f5f9fc26e32fb) に記載
Azur IoT Edgeインストールと設定の公式構成ツール

Azure IoT Edgeのランタイムインストールとプロビジョニング設定ができる公式ツールがPreviewで公開されましたので、ご紹介したいと思います。
現在の構成ツールのバージョンは`v0.0.2`です。

https://github.com/Azure/iot-edge-config

# なぜ

IoT Edgeデバイスに対してOSインストールが終わったら、Azure IoT Edgeランタイムをインストールし、Azure IoT HubまたはAzure IoT Centralに接続するプロビジョニング設定をしなければいけませんでした。
この二つの手順を公式ツール(Bashスクリプト)でまとめて実行することができるようになりました。

# 前提条件

- Azure IoT Edge構成ツールは現在、DPSを経由したプロビジョニングが対象です。なのでAzure IoT Hubのみ使っている場合、ツール利用ができないです。Azure IoT CentralはDPSが基本的に使われているため、問題なく使えます。
- 認証方式はSASキーのみです。X.509証明書方式は使えません。
- OSはティア(Tier)1が対象です。

# ツール実行

実行は簡単なので特に記載することはありませんが、Azure IoT Centralの画面を例にして実行手順をまとめます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287622/449659b6-47fe-6337-fdc5-5f2a2245c508.png)

```bash
# 変数設定
IDScope="<ID Scope>"
RegistrationID="<Registration ID>"
SymmetricKey="<Symmetric Key>"

# ツールダウンロードと実行
wget https://github.com/Azure/iot-edge-config/releases/latest/download/azure-iot-edge-installer.sh -O azure-iot-edge-installer.sh \
&& chmod +x azure-iot-edge-installer.sh \
&& sudo -H ./azure-iot-edge-installer.sh -s ${IDScope} -r ${RegistrationID} -k ${SymmetricKey} \
&& rm -rf azure-iot-edge-installer.sh
``` 

### ログファイル確認

全ての出力内容はログファイルに保存されるので、実行が終わったらログファイルを確認してください。

```bash
$ ls -l /tmp/azure*
$ for filename in `ls -1 /tmp/azure*`
do
  echo "----------"
  echo $filename
  echo "----------"
  cat -n $filename
done
```
oAuth2+nginx：後ろのシステムにリクエストヘッダでユーザIDを渡す



[前](https://qiita.com/DiveMasakazu/items/453de6af587a117da2c7)の続き

環境は同じくWindowsServer2012

nginxでoauth2-proxyを使う事で単純にGoogleでログインはできたが、
その先のシステムが独自にロール等を管理している場合でも使えるようにユーザID、というかgmailのアドレスを渡したい。

[ここ](https://docs.linuxfabrik.ch/software/oauth2-proxy.html)を見ると

```
--pass-user-headers   pass X-Forwarded-User and X-Forwarded-Email information to upstream (default true)
```

とdefault trueなので、リクエストヘッダのX-Forward-UserかEmailに何らかのデータが載っているのかをnode.js＋Express上でreq.headersを表示して確認したが、そのままだと存在していないようなので、ネットで検索しながら設定。

oauth2-proxy側の設定ファイルには、tokenは必須ではないが、以下を設定に追加

```
## headers
pass_user_headers = true
pass_host_header = true
pass_access_token = true
set_xauthrequest = true
```

nginx側にはこんな感じでproxy_set_headerをしてやる

```
location /node/ {
    auth_request /oauth2/auth;
    proxy_pass http://localhost:3000/;
    auth_request_set $user   $upstream_http_x_auth_request_user;
    auth_request_set $email  $upstream_http_x_auth_request_email;
    proxy_set_header X-Auth_User  $user;
    proxy_set_header X-Auth_Email $email;
}
```

これでnodejs側でreqから以下のようなIDとEmailが取れた。

```
  'x-auth_user': '102016090607545600000',
  'x-auth_email': 'mymail@gmail.com',
```

このx-auth_emailをユーザIDとして利用する事で、システム独自で認可処理を持っている場合でも扱えるはず。
（手元のJavaのシステムはリクエストヘッダをユーザIDとして扱える、というか扱えるように昔コードを書いたので、やろうと思えばいける）
【JavaScript】関数とオブジェクト⑪　 new演算子



#はじめに

Udemyの[【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)の講座の振り返りです。

前回の記事

https://qiita.com/nao0725/items/2cb2e20cf81663582b46

#目的

* 関数とオブジェクトについての理解を深める

#本題
###1.new演算子

new演算子とはコンストラクター関数からインスタンスを作成するための演算子のこと

####例1

コンストラクター関数の戻り値がreturnで、オブジェクトになっている場合

````js
// 関数を定義
function F(a,b){
  this.a = a;
  this.b = b;
  // 戻り値にreturnで、空のオブジェクトを設定
  return {};
}
// インスタンス化
const instance = new F(1,2);
// この結果を出力すると{｝からのオブジェクトが出力される
console.log(instance);
// 戻り値{}がFに返ってきているから？？
`````

オブジェクトではなくプリミティブ型で戻り値を設定すると

````js
function F(a,b){
  this.a = a;
  this.b = b;
  // オブジェクトではなくプリミティブ型で戻り値を設定
  return 1;
}
const instance = new F(1,2);
// 実行結果はF {a: 1, b: 2}となる
console.log(instance);

// returnを消しても同様の結果
```

prototypeで定義してみる

```js
function F(a,b){
  this.a = a;
  this.b = b;
}

F.prototype.c = function(){}

const instance = new F(1,2);
console.log(instance);
``````

````実行結果.
F {a: 1, b: 2}a: 1b: 2
[[Prototype]]: Object
 c: ƒ ()
 constructor: ƒ F(a,b) 
 [[Prototype]]: Object
``````

[[Prototype]]: Objectの中にcが入っている


####例2　

new演算子を呼び出す

`````js
function F(a,b){
  this.a = a;
  this.b = b;
  return {};
}

F.prototype.c = function(){}

// 第一引数をコンストラクタの「C」として定義
// 第二引数以下はコンストラクタ関数の引数によって変わってくるので...(レストパラメーターズ)と
// 可変長の引数（固定ではなく任意の個数の引数）を表すためにargsを使用
function newOpe(C, ...args){
  // 出力結果は以下の通り（上記のように表すと引数が配列として返ってくる）
  console.log(args);
}

// F(1,2)を削除し、新たにnewOpeという関数を定義
// 第一引数にコンストラクタ関数
const instance = newOpe(F,1,2)
console.log(instance);
````

````Console.
(2) [1, 2]
 0: 1 
 1: 2 
 length: 2 
[[Prototype]]: Array(0)
`````

####例3

thisが呼び出し元のオブジェクトのインスタンスになる場合

````js
function F(a,b){
  this.a = a;
  this.b = b;
  // return {};
}

// thisというオブジェクトをprototypeの参照を保持したまま生成する
F.prototype.c = function(){}

function newOpe(C, ...args){
  // 空のオブジェクト_thisを定義　createメソッドを使って渡ってきたCのprototypeをコピー
  const _this = Object.create(C.prototype);
  // Cを実行するとき_thisは関数Fのオブジェクト,argsは配列としてa,bが渡ってきている
  // それを変数resultに格納
  const result = C.apply(_this, args);
  console.log(result, _this);
  // 戻り値でnewOpeに_thisの値を返す
  return _this;
}

const instance = newOpe(F,1,2);
// 実行結果がF {a: 1, b: 2}となる
console.log(instance);
`````


今日はここまで！

#参考にさせて頂いた記事

* [【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)
【TypeScript】文字列を数値へ変換する方法

# 結論
- 下記の関数を使用する
  - Number()
  - parseInt()
  - parseFloat()

# Number()・parseInt()・parseFloat() の違い
## Number()
```.ts
Number('1'); // 1
Number('1.7'); // 1.7
Number('1px'); // NaN
Number('foo'); // NaN
Number(null); // 0
```

## parseInt()
```.ts
parseInt('1'); // 1
parseInt('1.7'); // 1
parseInt('1px'); // 1
parseInt('foo'); // NaN
parseInt(null); // NaN
```

## parseFloat()
```.ts
parseFloat('1'); // 1
parseFloat('1.7'); // 1.7
parseFloat('1.7px'); // 1.7
parseFloat('foo'); // NaN
parseFloat(null); // NaN
```

## まとめ
|| 解釈の仕方 | 小数点以下を変換するか | `null` を渡した場合に返却する値 |
|-|-|-|-|
| Number() | 文字列全体を解釈する | 変換する | 0 |
| parseInt() | 文字列から数値部分を切り出す | 切り捨てる | NaN |
| parseFloat() | 文字列から数値部分を切り出す | 変換する | NaN |

※数値を文字列へ変換する方法は [こちらの記事](https://qiita.com/y-hira18/items/4c927270bad1670e72a3)に記載
omniauth-samlでログイン後にセッションが消える

omniauthを既に使っていてたため、omniauth-samlを使ってSAML対応することになったけど、SAMLの承認自体は通るもののその後セッションが持続せず毎回ログアウトされるようになっていた。

### 結論

```ruby:omniauth_callbacks_controller.rb
protect_from_forgery except: [:saml]
```

自前のstrategyを持っていたりしてどこでtokenが消失するのかとかを追っていたけど、単純にprotect_from_forgeryで消されていただけだった。
今まで使っていたomniauth-google-oauth2がexceptしなくても動いていたので気づくのに時間がかかった。