OmniAICreator/Qiita-1.07M huggingface dataset:
Chromebook Linux環境 備忘録

ChromebookにLinux環境を入れてみた。インストール自体はワンボタンでできてしまう。
インストールすると下記のようなターミナルが表示されてシェルが扱えるようになる。

そもそもChrome OS自体がLinuxベースで動いているはず。さらにLinuxをインストールするとはどういうことか？気になったので調べて整理してみた。

https://chromium.googlesource.com/chromiumos/docs/+/HEAD/containers_and_vms.md

どうやらこんな感じの入れ子構造になっているっぽい。

![chromes.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126781/ec8576da-9a18-f189-cbd8-c919877b3faf.png)

今回インストールしたLinuxは水色のPenguin Containerの部分。

## Chrome OS
一番下のChrome OSのユーザランドには、croshでアクセスができる。croshはChromeブラウザーでCtrl-Alt-Tを押すと起動する。
unameを見ると4.19系のカーナルが動作していることが分かる。

![Screenshot 2021-08-22 10.35.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126781/f134d143-fcd5-976c-32a7-7460b6495483.png)

## Termina VM

crosvmでvmcコマンドを実行して、動作しているVMのリストを確認する。
terminaというVMが動作しているのが分かる。

```bash
crosh> vmc list
termina (10737426432 bytes, min shrinkable size 2723151872 bytes, raw)
Total Size (bytes): 10737426432
```

crosvmでvshコマンドを実行して、VMの中に入る。
unameで5.4系のカーネルが動作していることが分かる。/etc/os-releaseでディストリビューションがChromium OSであることも分かる。
上記のページに書いてある内容と一致する。TerminaはChrome OSのLinuxカーネルとユーザランドからコンテナ実行に必要な要素以外を削ぎ落としたもの。

> Termina is a VM image with a stripped-down Chrome OS linux kernel and userland tools. Its only goal is to boot up as quickly as possible and start running containers. 


```bash
crosh> vsh termina
(termina) chronos@localhost ~ $ uname -a
Linux localhost 5.4.119-14945-gafc97d54f809 #1 SMP PREEMPT Tue Aug 10 21:44:33 PDT 2021 aarch64 GNU/Linux
(termina) chronos@localhost ~ $ cat /etc/os-release 
NAME=Chromium OS
ID=chromiumos
HOME_URL=https://www.chromium.org/chromium-os
BUG_REPORT_URL=https://crbug.com/new
VERSION=92
VERSION_ID=92
BUILD_ID=13982.81.0
```

## Penguin Container
termina VMでLXD環境で動作しているコンテナ一覧を表示する。
penguinという名前のコンテナが動作しているのが分かる。

```bash
crosh> vsh termina
(termina) chronos@localhost ~ $ lxc list
+---------+---------+-----------------------+-----------------------------------------------+------------+-----------+
|  NAME   |  STATE  |         IPV4          |                     IPV6                      |    TYPE    | SNAPSHOTS |
+---------+---------+-----------------------+-----------------------------------------------+------------+-----------+
| penguin | RUNNING | 100.115.92.203 (eth0) | 2402:6b00:752c:a700:216:3eff:fed9:6470 (eth0) | PERSISTENT | 0         |
+---------+---------+-----------------------+-----------------------------------------------+------------+-----------+
```

lxc execコマンドで中に入ってシェルを実行する。ChromebookにLinuxをインストールすると起動するターミナルアプリでも同じシェルが起動していると思われる。

```bash
(termina) chronos@localhost ~ $ lxc exec penguin -- /bin/bash
root@penguin:~# uname -a
Linux penguin 5.4.119-14945-gafc97d54f809 #1 SMP PREEMPT Tue Aug 10 21:44:33 PDT 2021 aarch64 GNU/Linux
root@penguin:~# cat /etc/os-release 
PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
```

penguinはシステムコンテナなので、カーネルはTerminaVMと同じはず。unameで確認すると、たしかに5.4.119となっていて同じっぽいことが分かる。
ユーザランドはDebian。
Clean Architectureを会社で例えてみる

#Clean Architectureってなに
__関心の分離__をする開発手法。
詳しくは公式サイト見てね。

https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

#Clean Architectureにすると何が嬉しいのか

1. 再利用しやすい
1. 微調整が簡単にできる。
1. メンテナンスがしやすい。

#各レイヤーを会社に例えてみる
各レイヤーを会社で例えるとこんな感じ。
イメージは、全員総力で走っているベンチャー企業を制度が整った大企業に変化するイメージ。

上下関係や仕事内容を明確化することで、仕事の微調整が簡単にできたり、新しい部署を作っても制度を再利用できたり、問題が起きても誰が原因なのか明確にできるのでメンテナンスしやすい。
![Screen Shot 2021-08-22 at 16.24.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1553280/1d1734de-0ad4-737a-bf50-1dd495fb3a2c.png)


##1.webサーバー（APIサーバーの子会社）
クライアントから依頼を受け、親会社のcontrollerの指示に従って、必要な情報を渡す。

##2.controller（usecaseの部下）
マニュアルをもとに、リクエストの中から必要な情報を取り出したり適切な形に変換したりする。
pathやqueryを読み取る
bodyのjsonをオブジェクトに変換
usecaseさんに渡す。

##3.usecase
エンジニアじゃない人でも分かることをここに書く
何を入れたら何が返ってくるのか一連の流れのマニュアルを作る。

webサーバーから欲しい情報を受け取り、DBサーバーに問い合わせて、返ってきた情報をwebサーバーに返す。
webサーバーから受け取る仕事は、controllerに、
DBサーバーへの問い合わせの仕事は、infrastructureに、
webサーバーに返す仕事は、presenterにやってもらう。

##4.infrastructure（usecaseの部下）
usecaseさんから情報を受取り、マニュアルをもとに、DBサーバーに問い合わせる。
DBサーバーから受け取った情報をusecaseさんに渡す。

##5.DBサーバー（APIサーバーの子会社）
親会社のinfrastructureさんからを依頼を受け、DBから必要な情報を渡す。

##6.presenter（usecaseの部下）
usecaseさんから情報を受取り、マニュアルをもとに情報を適切な形に変換し、webサーバーに渡す。

##7.domain（APIサーバーの社長）
誰からの指示も受けない絶対的存在。
全体のルールを決める。

#どうやって情報の受け渡しをするのか

ルール

1. 上司は、部下が仕事ができるようにマニュアルを作る。
部下は、構造体をたどり、必要なマニュアルを入手して仕事をする。

1. 情報を渡す時は、渡す人にマニュアルを作成してもらい、そのマニュアルに従って情報を渡す必要がある。

1. マニュアル作成方法
引数と返り値を使って、マニュアルを作成。
構造体を使って、作ったマニュアルを保存。

全体像はこんな感じ
![Screen Shot 2021-08-22 at 17.04.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1553280/c0a0b354-7f10-abcb-dcb5-dadd90a105d9.png)


ここで問題が、
情報を渡す時は、受け取り側にマニュアルを作ってもらうことがルール。
しかし、赤の矢印のように上司から部下に情報を渡すケースが存在する。
その際は、
上司が自分でマニュアルを作って情報を渡さなければならない。
これが__依存関係逆転の原則__であり、__インターフェイス__を使って実現する。

#最後に
会社の制度を作る間は大変だけど、作ってからは楽になる。
それと同じでClean Architectureは最初は大変だけど、作ってしまえば後は楽チンである。
[035] ドキュメントの AND による絞り込み検索(「015」)の全件表示 by Ruby elasticsearch 7.14

|シリーズトップページ|
|:--|
|https://qiita.com/robozushi10/items/2c8b6951ee342b013974|

# 概要

Ruby の Elasticsearch Client を使って Elasticsearch 7.14 で REST API の操作する.
また、比較のために、Kibana DevTool でのクエリも合せて記しておく.

今回はインデックス「shakespeare」から次の「**ドキュメントの検索**」を行う.
・**フィルード「text_entry」の値に「ACT」かつ「V」という "単語" を含むドキュメントを取り出す.**

このとき、scroll_id を使って全件の取り出しを行う.




# 検証環境

下記の要領で検証用データ「Shakespeare」が登録された
・Elasticsearch + Kibana (7.14)
を使用した

[[00] Ruby の elasticsearch client パッケージを使って Elasticsearch 7.14 を操作してみる ... 検証環境構築編](https://qiita.com/robozushi10/items/8bd936fe6fbab080149e)



# 参考にした情報

|URL|
|:--|
|https://zenn.dev/kaiba/articles/e8d93e0397404186124c|
|https://stackoverflow.com/questions/64708844/how-to-scroll-data-using-scroll-api-elasticsearch|
|[DevTool での scroll_id の使用方法](https://www.elastic.co/guide/en/elasticsearch/reference/current/scroll-api.html)|
|[elasticsearch-api における scroll\_id の指定方法](https://github.com/elastic/elasticsearch-ruby/blob/master/docs/examples.asciidoc)|
|[elasticsearch-api を使って scroll の情報を削除する](https://github.com/elastic/elasticsearch-ruby/blob/master/elasticsearch-api/lib/elasticsearch/api/actions/clear_scroll.rb)|
|[Qiita - 【Elasticsearch】ScrollAPIを使ってみた](https://qiita.com/mug-cup/items/5fbe8a4fd27a2f6492b1)|

　

# 実践

## Kibana DevTool の場合

### コード

下記を参照すること
・[[015] ドキュメントの AND による絞り込み検索 by Ruby elasticsearch 7.14](https://qiita.com/robozushi10/items/de6bc9fac3f30937eaea)


## Ruby の場合

### コード

重要な部分は「`if __FILE__ == $0 以降`」である.

class MySimpleClient は丸々コピーで良い. (が、192.168.10.115 のみ、適宜読み替えること)

```ruby
#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-
require 'multi_json'
require 'faraday'
require 'elasticsearch/api'
require 'active_support/core_ext'
require 'active_support'

class MySimpleClient
# note_0001
  include Elasticsearch::API
  CONNECTION = ::Faraday::Connection.new url: 'http://10.208.207.53:29200'
  def perform_request(method, path, params, body, headers = nil)
    CONNECTION.run_request \
      method.downcase.to_sym,
      path_with_params(path, params),
      (body ? MultiJson.dump(body): nil),
      {'Content-Type' => 'application/json'}
  end

  private

  def path_with_params(path, params)
    return path if params.blank?

    case params
    when String
      "#{path}?#{params}"
    when Hash
      "#{path}?#{params.to_query}"
    else
      raise ArgumentError, "Cannot parse params: '#{params}'"
    end
  end
end

if __FILE__ == $0

  client = MySimpleClient.new

  q = {
    "query": {
      "query_string": {
        "query": "text_entry:ACT AND text_entry:V"
      }
    }
  }
  res = client.search index: 'shakespeare', scroll: '10m', body: q
  h = JSON.parse(res)
  pp h
  while h['hits']['hits'].size.positive?
    q = {
      "scroll_id": h['_scroll_id']
    }
    res = client.scroll scroll: '5m', body: q
    h = JSON.parse(res)
    pp h
  end
end
```
【メモ】ベーシック認証（basic認証）を設定してIP制限をかける方法

# 概要
Basic認証をかけつつIP制限をする機会があったのでメモ。
apacheです。


# 作業手順

1. htpasswdファイルの作成
2. httpd.confにBasic認証の設定を追加
3. apacheをreload
4. 動作確認


# 1. htpasswdファイルを作成
```
$ htpasswd -c /var/www/.htpasswd [username]
```

コマンドから作成しなくてもOK。


# 2. 設定ファイルにBasic認証の設定を追加
```
/etc/httpd/conf/httpd.conf
```

に以下を追加。

```
------------------------------------------
RemoteIPHeader X-Forwarded-For
<Location />
  AuthType Basic
  AuthName "Login"
  AuthUserFile /var/www/.htpasswd
  Require valid-user

  # 許可するIPを記入
  Require ip xxx.xxx.xxx.xxx
  Require ip xxx.xxx.xxx.xxx
</Location>
------------------------------------------
```

.htaccessファイルへの記載でもいいみたい。

# 3. apacheをreload
```
$ sudo systemctl reload httpd
```

# 4. 動作確認
許可したIPからのアクセスでBasic認証が求められないか&許可していないIPからのアクセスでBasic認証が求められるかを確認。
[Python]ローカルPCからSSHトンネリングを行いDB接続する方法 メモ

* Pythonで踏み台サーバー経由でDB接続する方法についてメモする。


## 環境構成

* ローカルPCから踏み台サーバー(EC2)を経由し、DB(Aurora)に接続するようなケースを想定

![python_db_tunnel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/86c562fa-9e65-17fd-f7fb-ac715eab8e43.png)


## コード

* `conf/setting.conf`

  * 踏み台サーバーとDBの接続情報を記述する。

  ```conf
  # 踏み台サーバー接続情報
  [BASTION]
  BASTION_HOST = YOUR_BASTION_HOST_IP
  BASTION_PORT = 22
  BASTION_HOST_KEY = None
  BASTION_USERNAME = YOUR_BASTION_USERNAME
  BASTION_PASSWORD = YOUR_BASTION_PASSWORD
  BASTION_PKEY = None
  BASTION_REMOTE_BIND_ADDRESS_HOST = YOUR_AURORA_HOST
  BASTION_REMOTE_BIND_ADDRESS_PORT= 3306
  # DB接続情報
  [DB]
  DB_USER = YOUR_DB_USERNAME
  DB_PASSWORD = YOUR_DB_PASSWORD
  DB_NAME = YOUR_DB_NAME
  ```

  

* `test.py`
  * 設定ファイル`conf/setting.conf`から取得した接続情報を用いてDB接続を行い、SELECT文を実行して、実行結果を`result`フォルダ配下に保存する。

```python
import sshtunnel
from sqlalchemy import create_engine
import pandas as pd
import configparser

# 設定ファイルから接続情報読み込み
conf = configparser.ConfigParser()
conf.read('conf/setting.conf', encoding='utf-8')

# 踏み台サーバー接続情報
BASTION_HOST = conf['BASTION']['BASTION_HOST']
BASTION_PORT = int(conf['BASTION']['BASTION_PORT'])
if conf['BASTION']['BASTION_HOST_KEY'] == 'None':
	BASTION_HOST_KEY = None
else: 
    BASTION_HOST_KEY = conf['BASTION']['BASTION_HOST_KEY']
BASTION_USERNAME = conf['BASTION']['BASTION_USERNAME']
BASTION_PASSWORD = conf['BASTION']['BASTION_PASSWORD']
ifconf['BASTION']['BASTION_PKEY'] == 'None':
	BASTION_PKEY = None
else:
    BASTION_PKEY = conf['BASTION']['BASTION_PKEY']
BASTION_REMOTE_BIND_ADDRESS_HOST = conf['BASTION']['BASTION_REMOTE_BIND_ADDRESS_HOST']
BASTION_REMOTE_BIND_ADDRESS_PORT = int(conf['BASTION']['BASTION_REMOTE_BIND_ADDRESS_PORT'])
# DB接続情報
DB_USER = conf['DB']['DB_USER']
DB_PASSWORD = conf['DB']['DB_PASSWORD']
DB_NAME = conf['DB']['DB_NAME']

# DB接続
sshtunnel.SSH_TIMEOUT = 5.0
sshtunnel.TUNNEL_TIMEOUT= 5.0
with sshtunnel.SSHTunnelForwarder((BASTION_HOST, BASTION_PORT), ssh_host_key=BASTION_HOST_KEY,
                        ssh_username=BASTION_USERNAME, ssh_password=BASTION_PASSWORD,
                        ssh_pkey=BASTION_PKEY,
                        remote_bind_address=(BASTION_REMOTE_BIND_ADDRESS_HOST, BASTION_REMOTE_BIND_ADDRESS_PORT),local_bind_address=('localhost',3306)) as server:
    con = create_engine(
        f'mysql+pymysql://{DB_USER}:{DB_PASSWORD}@localhost:{server.local_bind_port}/{DB_NAME}?charset=utf8',
        echo=True)
    # pandasでSQL実行+実行結果を取得
    df = pd.read_sql('SELECT * FROM fuga', con)
	# CSV 保存
    df.to_csv('result/data.csv')
    # Excel 保存
    # df.to_excel('result/data.xlsx',sheet_name='data',index=False)
```



## 実行方法

```shell
python test.py 
```

※`result`ディレクトリにSELECT文実行結果が保存される。



## 参考情報

* [PythonでSSHトンネリングしてデータベースアクセス](https://blog.honjala.net/entry/2016/06/25/013131)
* [pythonプログラムにおける設定ファイル管理モジュール～configparserの使い方と注意点～](https://qiita.com/mimitaro/items/3506a444f325c6f980b2)
Ubuntuでゲームする方法について

# はじめに
Ubuntuでゲームで遊ぶため方法について簡単にリストアップしてみます。


# ゲームで遊ぶには
Ubuntuでゲームで遊ぶ場合は以下の方法が浮かぶ
* デフォルトのゲームで遊ぶ
* Ubuntu Softwareのゲームカテゴリーからダウンロード
* Linux専用ゲームをWebから探して実行
* ブラウザゲームで遊ぶ
* RetroArchでゲームロムをエミュレート
* Wineを使用して、Windowsのゲームをエミュレート
* AndroidエミュレータでAndroidゲームを遊ぶ
* SteamをインストールしてSteamのゲームを遊ぶ
* 自分でゲームを作る

# 実践

## デフォルトのゲームで遊ぶ
Ubuntuをインストールした時に、以下のゲームがインストールされているので、スタートメニューから選択することでゲームで遊べる
* AisleRiot ソリティア
* GNOME マインスイーパー
* GNOME 上海
* GNOME 数独

## Ubuntu Softwareのゲームカテゴリーからダウンロード
インターネットに接続していれば、Ubuntu用のゲームをUbuntu Softwareを起動すればUbuntuで遊べるゲームがあるので、その画面からインストールする。

## Linux専用ゲームをWebから探して実行
以下のサイトの一覧から、対象のゲームのリンク先からインストールして遊ぶ
* [ゲーム一覧](https://wiki.archlinux.jp/index.php/%E3%82%B2%E3%83%BC%E3%83%A0%E4%B8%80%E8%A6%A7)

パッケージ化されたインストールはそんなに難しくないけど、場合によってソースからビルドする必要がある

## ブラウザゲームで遊ぶ
Firefoxなどのブラウザを使用して遊ぶゲーム
[Google Doodle](https://www.google.com/doodles)などが有名
Chromeブラウザがオフラインのみで遊ぶことができるゲームもある
なお、昔みたいにFlash Playerをインストールする必要があるなどがないため、結構簡単

## RetroArchでゲームロムをエミュレート

以下のコマンドで、インストールして立ち上げるソフト

```
sudo apt install retroarch
```

特殊な方法で、吸い出したゲームROMが遊ぶことができるマルチエミュレータ
いろんなプログインを導入するとFCやSFC、GBなどのゲームを遊ぶことができる。
ROMデータについては、著作権の勉強が必要

## Wineを使用して、Windowsのゲームをエミュレート

以下のコマンドで、wineと呼ばれるWindowsアプリのエミュレータをインストールできる

```
sudo apt install wine
```

wineを使用してWindowsアプリをエミュレーションすればゲームで遊べるけど、あそぶまでに、
苦労が多い

なお、Lutrisというランチャーがあるので、これを使用してインストールや管理が楽になる

```
sudo apt install lutris
```

## AndroidエミュレータでAndroidゲームを遊ぶ
以下のコマンドを実行して、Androidエミュレータをインストールする

```
sudo snap install --beta --devmode anbox
```

筆者環境のUbuntuでは起動しなかった。

## SteamをインストールしてSteamのゲームを遊ぶ

以下のコマンドでSteamのアプリケーションをインストールします。

```
suod apt install steam
```

起動後、Steamのサイトから、ゲームを購入することで遊ぶことができます。
たとえ、Windows用ゲームでもProtonと呼ばれるエミュレータがあるので、普通に遊ぶこともできます。

## 自分でゲームを作る

自作で、CUIで遊べるゲームやGUIで遊べるゲームをつくる
【備忘録】エンジニア用語解説

駆け出しエンジニアです。
わからない用語だらけのため、備忘録を残しておきます。

# マイクロサービス（マイクロサービス アーキテクチャ）
複数の規模の小さなサービスを組み合わせてひとつの大きなアプリケーションを構成する、ソフトウェア開発の技法のひとつ。

従来は、「すべての機能がまとまったひとつの大きな塊」としてソフトウェアを設計することが多かったのに対し、マイクロサービスは「まず機能を分解していき小さなサービスをつくる。それらを組み合わることでひとつの大きなソフトウェアを構成する。」という考え方。

ひとつひとつのサービスは小さいだけでなく、自らの持つ役割に専念して、自律的に機能するという点もマイクロサービスに欠かせない要素。

具体的には、個々のサービスはできるだけシンプルにすること、そして複数のサービスがひとつのOSやハードウェアの上で動かないようにすることが重要。

# API gateway
APIゲートウェイとは、APIの管理や実行を容易にするしくみのこと。

まずAPIとは、ソフトウェアやアプリケーションなどの一部を外部に公開することにより、第三者（私達）が開発したソフトウェアと、機能を共有できるようにしてくれるもののこと。

APIゲートウェイはクライアントから受け取ったリクエストを、それぞれのマイクロサービスにルーティングする。

通常のマイクロサービス・アーキテクチャの場合、クライアントとそれぞれのAPIは個別に通信を行うことになる。
例えば、1つのWebサービスに「決済サービス」「ショッピングカート」「メール機能」「レコメンド機能」といった複数のAPIが使われている場合、クライアントはそれぞれのAPIと個別に通信することになりこの例でいえば計4回の通信が発生する。

APIゲートウェイを使うと、クライアントはAPIゲートウェイと通信を行い、APIゲートウェイがそれぞれのマイクロサービスと通信するため、クライアントはマイクロサービスを知る必要がない。

# トラフィック
トラフィックとは、交通（量）、通行（量）、往来などの意味を持つ英単語。

ITや通信の分野では、通信回線やネットワーク上で送受信される信号やデータのことや、その量や密度のことをトラフィックということが多い。

Webの分野では、WebサーバやWebサイトへの外部からの接続要求数、アクセス数、送信データ量などのことや、サイトやページの間を行き来する閲覧者の流れのことをトラフィックということが多い。

# エンドポイント
ITの分野では、通信ネットワークの末端に接続された機器や端末、あるいは利用者が直に触れて操作する機器などを指すことが多い。

特に、セキュリティの分野でネットワークの中継機器などと対比して、利用者が操作するパソコンやスマートフォンなどを対象とした保護施策を「エンドポイントセキュリティ」などという。

電子機器内部の構成で、回路や配線の終端の端子やコネクタ、また、それらに接続された装置などのことをエンドポイントということがある。

ソフトウェアの分野では、あるプログラムがAPIなどの形で外部に公開している機能の所在を示す識別名やネットワーク上のアドレス、URL/URIなどのことをエンドポイントということがある。

# レイテンシ
データの転送要求などのリクエストを発してから、リクエストの結果が返ってくるまでにかかる遅延時間のこと。

例えばメモリやハードディスクなどの記憶装置からデータを読み出す場合には、命令を送ってから記憶装置におけるデータが記録された箇所までアクセスする時間と、記憶装置から読み出されたデータが命令の結果として返ってくるまで、わずかながら遅延時間が生じる。

また、コンピュータのCPUとSDRAMなどのメモリとの間ではクロック周波数が異なるため、処理にレイテンシが生じやすい。この場合の遅延は特にメモリレイテンシなどと呼ばれることがある。

一般的に、レイテンシの値が小さければ小さいほど、コンピュータのデータアクセスに関する性能は高いということになる。


# コードフリーズ
「パソコンがフリーズした」のフリーズと同じ単語 freeze だが、
コードフリーズはある状況で意図的に行うフリーズを指す。

コードフリーズ＝主にソフト開発の終盤で「プログラムの追加・変更をストップすること」。

# ドメインチーム体制
チーム単位で、ある程度担当領域を決めてしまって取り組むチーム体制のこと。

# フルサイクルエンジニア（フルサイクル開発者）
フルサイクルエンジニアという言葉は、2018年5月に書かれた
 [Full Cycle Developers at Netflix — Operate What You Build][link-1] 
[link-1]:https://netflixtechblog.com/full-cycle-developers-at-netflix-a08c31f83249
という記事にある "full cycle developer" に由来している。

Netflixにおけるフルサイクルとは、開発チームが運用とサポートを含めたサービスライフサイクルの一連のタスクをすべてを担い、開発を回すことを意味する。

▼参考にさせていただいた記事

https://qiita.com/m-chika/items/5e1d6291a53c3cda3371

# DDD(Domain Driven Design(ドメイン駆動設計))

1.開発を進める上で実際の業務をきちんと理解せずに作ると、リリースしても全然使われずに不評なものができてしまう。

2.メンテナンス性が悪く整理されていないプログラムを書いたら、後から修正するのが大変になってしまう。

DDDはこのような問題を解決するために生まれたもので、
実際にあるドメイン(専門領域)をどのように実装に落とし込み、開発に変換して行くかを考えた手法の一つを指す。



▼参考にさせていただいた記事

https://qiita.com/watanabeso/items/74962bb1ba1d6f89f09e

本日のアウトプットは以上です。
ESP32でArduinoJsonのデータをEEPROMに保存するには

# ESP32でArduinoJsonのデータをEEPROMに保存するには

次のホームページの英訳をメモとして残しておきます。翻訳はDeepLで行いました。

https://arduinojson.org/v6/how-to/store-a-json-document-in-eeprom/

## How to use EEPROM with ArduinoJson?

ArduinoJsonは、`EEPROM.read()`と`EEPROM.write()`を知らないので、ネイティブにEEPROMを使うことはできません。 しかし、`serializeJson()`と`deserializeJson()`は、事実上あらゆる種類のストレージに使用できる汎用インターフェースをサポートしています。ストリームです。 

`EEPROM.read()`と`EEPROM.write()`を呼び出すStreamクラスを作成するのは非常に簡単ですが、StreamUtilsライブラリがまさにこれを行うEepromStreamクラスをすでに提供しているので、自分で作成する必要はありません。 JSONドキュメントをEEPROMに書き込むには、次のようにします。

```
EepromStream eepromStream(address, size);
serializeJson(doc, eepromStream);
```

ご覧のように、EepromStreamのコンストラクタには、ストリームとして公開したいEEPROMの領域のアドレスとサイズの2つの引数を渡します。アドレスに何を使えばよいかわからない場合は0を、サイズに何を使えばよいかわからない場合は上の表の値を使います。いくつかのプラットフォームでは、`EEPROM.length()`を使用することができます。 同様に、EEPROMからJSONドキュメントを読み取るには、次のようにします。

```
EepromStream eepromStream(address, size);
deserializeJson(doc, eepromStream);
```

もちろん、ソースファイルの先頭に`StreamUtils.h`をインクルードすることをお忘れなく。

## Notes for ESP8266 and ESP32

ESP8266およびESP32は本物のEEPROMを持っていませんが、EEPROMをエミュレートしています。

- ESP8266はSPIフラッシュの4KBセクターを使用 
- ESP32は不揮発性ストレージ(NVS)のblobを使用 

これらのプラットフォームでEEPROMエミュレーションを使用するには，EEPROMライブラリを初期化する必要があります。

```
EEPROM.begin(512); 
```

この例では、512はEEPROMエミュレーションのために確保するメモリ量です。ドキュメントによると、ESP8266では4096Byte、ESP32では~~508000~~20000Byteまで可能だそうです。 (ESP32：パーティションのNVS領域を変更すれば拡張可能）
次に行うべきことは、変更を「コミット」すること（つまり、不揮発性メモリにキャッシュを書き込むこと）です。
これは次のように行います。 

```
eepromStream.flush();  // (calls EEPROM.commit() on ESP)
```

この関数はESPで`EEPROM.commit()`を呼び出し、他のプラットフォームでは何もしません。


# See Also

https://github.com/bblanchon/ArduinoStreamUtils#how-to-use-eeprom-as-a-stream

（了）
エンジニアに必要な言語化、質問力、論理的思考の鍛え方

# 頭の回転の速さを鍛えるには
頭の回転の速さは筋トレと同じです。****とにかく使うことが大事です。***
１人で学習をやっていて身に付くものではありません。

# 対策
***まず人と話す、質問をするです。***
slackとかでもokです。***テキストでコミュニケーションを取ることも大事です。***

意識して欲しいことは

***1 自分より頭の回転の速い人と話してフィードバックをもらう***

***2 学習時に質問をして質問の練習をしていく***

自分1人ではなかなか難しいので上の2つを取り組んでみよう。

***3.頭の回転が速いと思う人のコンテンツや動画で考えを盗む***
身近にいなければ、考え方などを吸収しましょう‼︎
[04] EFK (Elasticsearch + Fluentd + Kibana) の構築 ... Dockerコンテナの立ち上げ(Proxyサーバ下)


# はじめに

前述した「[[03] EFK (Elasticsearch + Fluentd + Kibana) の構築 ... Dockerコンテナの立ち上げ(説明あり)](https://qiita.com/robozushi10/items/f6082044fbf2d583c801)」を
Proxy 下で実行する場合の記述である.

ここでは Proxyサーバを「proxy.co.jp:8080」として、変化点を変更形式で記す.


## assets/elasticsearch/Dockerfile

elasticsearch のプラグインを追加する場合は、
`elasticsearch-plugin` コマンドに Proxyサーバとポートを指定してやる必要があった.


```diff
 FROM elasticsearch:7.14.0
  
+## プロキシサーバを使う場合
+ENV http_proxy="http://proxy.co.jp:8080"
+ENV https_proxy="http://proxy.co.jp:8080"
 
 # 日本語全文検索ができるようにする
-RUN elasticsearch-plugin install analysis-kuromoji
+RUN ES_JAVA_OPTS="-Dhttp.proxyHost=proxy.co.jp -Dhttp.proxyPort=8080 -Dhttps.proxyHost=proxy.co.jp -Dhttps.proxyPort=8080" elasticsearch-plugin install analysis-kuromoji
```

## assets/fluentd2/Dockerfile

```diff
 FROM fluent/fluentd:v1.13-1
 
+## プロキシサーバを使う場合
+ENV http_proxy="http://proxy.co.jp:8080"
+ENV https_proxy="http://proxy.co.jp:8080"
 
 USER root
 
 RUN gem install fluent-plugin-elasticsearch
```

## assets/nginx/Dockerfile

```diff
 FROM nginx:1.20.0-alpine
 
+## プロキシサーバを使う場合
+ENV http_proxy="http://proxy.co.jp:8080"  
+ENV https_proxy="http://proxy.co.jp:8080"
```
ピュアなruby環境でsentry-rubyが動作しないときの備忘録

あんまりいないと思うけど、RailsとかSinatoraとかを使っていない純粋なruby環境でSentryを使ったときに沼った。
そのときの備忘録を残す。

# 環境
Gemは単純にsentry-rubyを指定するだけ
バージョンは、最新の4.6.5だったけど、多分関係ない

```ruby:Gemfile
source "https://rubygems.org"
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby "2.7.4"

...
gem "sentry-ruby"
...
```

コードは[公式のサンプル](https://docs.sentry.io/platforms/ruby/usage/)通り

```ruby:sample.rb
require "sentry-ruby"

Sentry.init do |config|
  config.dsn = '__MY_DSN__'
end

begin
  1 / 0
rescue ZeroDivisionError => exception
  puts exception
  Sentry.capture_exception(exception)
end
```

# エラー内容
エラー内容がSentryに通知されない

# 原因
デフォルトの挙動だと、バックグラウンドのスレッドでSentryがエラーを送信しているが、プロセスが終了すると、それに引きづられて、スレッドも終了する
そのため、エラーが送信される前にプロセスが終了するとエラーが送信されない。
…ような気がする
https://docs.sentry.io/platforms/ruby/configuration/options/#optional-settings

# 解決策
公式が言っているようにActiveJobsみたいなバックグラウンドジョブを利用するか、
下記のように、スレッド数を0に指定して、同期的に動作するようにする

```rb
Sentry.init do |config|
  config.dsn = '__MY_DSN__'
  config.background_worker_threads = 0
end

```

…というかドキュメント見ればちゃんと書いてあった。
よく読まんとな
【初学者向け】JavaScriptのreduceとは？使い道は？

JavaScriptの学習初期段階では全く理解できなかったreduceメソッドについてざっくり解説していきます。

#reduce( )で出来る事

・**配列の合計を算出できる（今回はこちらを解説）**
・配列の最大値を出す
・map( )として (配列を使って新しい配列を生成)
・filter( )として (配列から条件を満たすもので新しく配列生成)
・flat( )として (二次元配列を一次元に)
・文字列を配列にする
...etc
本当に様々なことができるようになります！

#reduce( )を使って配列の合計を算出しよう

###①reduce( )を理解する上で一番大事なイメージ
初学者の方に最初に頭に入れて置いて欲しいpointがあります。


それは配列の合計を算出する際
☆配列の要素を一気に足して値を返すをいうイメージではなく、
**reduce( )は配列を頭から順番に一つずつ足していくイメージです**。

(具体的には)
[1, 3, 5, 7]という配列の合計を算出する際、~~「1 + 3 + 5 + 7」→ 「16」
といった計算を１回行う~~のではなく
まずは最初の２つの「1 + 3」→ 「4」。
その後に「4 (※1+3の合計) + 5」→「9」をする。
その後に「9 (※4+5の合計) + 7」→「16」といった**計算( 合計で3回 )イメージ**です

この計算イメージを頭に入れて次に進んでください

###②構文について
今回は簡略化して引数２つだけで説明します。(引数は最大４つ取れます)
1つ目・・accu( accumulatorの略 )
        ①のイメージで説明した通りここには前回の計算の合計(蓄積された値)がきます

2つ目・・curr( currentValueの略 )
ここにはaccuと足し合わせる要素が順番にきます。
①のイメージの説明で言うと3,5,7が順番に入ります。

```js
const arry = [1, 3, 5, 7]; //配列を用意 

const sum = arry.reduce((accu, curr) => {
    //accとcurrに入る数値を確認するために記述してます(本来必要ないです)
    console.log(acuu, curr);
    //1 3 (1回目の計算)
    //4 5 (2回目の計算)
    //9 7 (3回目の計算)

    return accu + curr ;
});//

console.log(sum) //16
```
よりシンプルに記述するなら(途中のconsole.logは省いて)

```javascript
const arry = [1, 3, 5, 7];
const sum = arry.reduce((accu, curr) => accu + curr);
console.log(sum) //16
```
とも記述できます。

#まとめ
reduce( )の実行イメージと引数の役割が理解できれば意外と簡単に感じることができると思います
【Laravel】マイグレーションをrollbackした時のメモ

環境:Laravel 6.2

※※初学者が自分のメモも兼ねて書いています。おかしなことを言ってたら教えて頂けると嬉しいです。※※


rollback　＝　直前のマイグレーションを取消し、DBをマイグレーション前の状態に戻す

```:コマンド
php artisan migrate:rollback
```

#### ■注意
マイグレーションを実施すると、DBのmigrationsテーブルにレコードが作成される。（下図参照）
このテーブルのbatchカラム部分の数字が最も大きいレコードが、ロールバックの対象となる「直前のマイグレーション」なので、意図しないテーブルが消えてしまわないように事前に確認する。

![スクリーンショット 2021-08-22 143258.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985396/fa45a11b-6e4c-1f2a-d774-a1238a68f607.jpeg)
Power Automate Desktopでファルイのコピー & ペースト

### やりたいこと
RPAで何が出来るだろうと話をしているとこんな話が出ました。
「出来上がったファイルを社内サーバー内のいくつかのフォルダに入れないといけないけど、階層が深かったりフォルダが沢山あっていつも迷う」

Power Automate Desktopのアクションを見ていたらフォルダやファイルの操作が有ったのでやってみました。

### やったこと
今回はデスクトップに有る「共有フォルダ」内のファイルを「ドキュメント」「ピクチャ」に有る「資料の共有」フォルダに移動したいと思います。

流れとしては
共有するファイル（資料）を指定フォルダに入れる。
↓
フローを実行。
↓
共有フォルダ内のファイルが「ドキュメント」「ピクチャ」に有る「資料の共有」の中にコピーされる。
と言う感じになります。

●新しいフローの作成
　適当な名前を付けて作成開始
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514815/1e6cbf18-34c9-1c22-ca90-a8aba80acf4a.png)

●フォルダ内のファイルのコピー
　フォルダアクションの「フォルダー内のファイルを取得」を使いファイルをコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514815/f77abffb-b1bd-b790-a576-86eceef1cf36.png)

●ファイルのコピー
　コピーしたファイルを張り付けるのは「ファイルのコピー」
　コピーするファイルは「フォルダー内のファイルを取得」で取得しているのでそこで作られた変数　%file% を使います。
　宛先フォルダーはドキュメント内にある「資料の共有」フォルダを指定します。
　ファイルが存在する場合は今回は「何もしない」にしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514815/ae9e9cea-ac1b-c8f5-0bf0-5a6e075f46fd.png)
※ちなみにファイルが存在する場合とは「同じ名前」のファイルが存在する場合みたいです。

●実行
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514815/35d08eda-2399-1b7f-ab22-a328357b7cb0.png)
　実行するとデスクトップの「共有フォルダ」の中にあるファイルがドキュメントとピクチャーにある「資料の共有」フォルダのなかにコピーされます。

　このままでもいいですが、元ファイル（共有フォルダ内にあるファイル）を消してみましょう。
　「フォルダを空にする」を使い空にするフォルダ「共有フォルダ」を指定してあげるだけで元ファイルもなくなりすっきりします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514815/f8228df9-f5a6-f939-2fa7-b4997e100467.png)

　仕事で色んな部署などと資料の共有をする時に、フォルダが社内サーバーのどこに有るか分からなくなることありますよね。
　一度これで設定しておくとフォルダの位置が変わらない限り複数個所に一瞬でコピーしてくれるので助かります。
Kahan と ruby の sum

# これは何？
こんな記事

https://qiita.com/Nabetani/items/59a8ec6160cd409c97f0

を書くに当たり。

Kahan のアルゴリズムを Wikipedia をもとに実装してみたけど、 ruby の `Enumerable#sum` の結果と合わなかった。

何が違うのか調べてみた。

# Wikipedia の Kahan

記事は [こちら](https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%8F%E3%83%B3%E3%81%AE%E5%8A%A0%E7%AE%97%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0) 。

ruby で書くとこう。

```ruby:ruby
def kahan(a)
    sum=c=0.0
    a.each do |v|
        y = v-c
        t = sum+y
        c = (t-sum) - y
        sum = t
    end
    sum
end
```

実行してみると。

```ruby:ruby
ary = [1e18, 1.0, -1e18]
p( { kahan:kahan(ary), sum:ary.sum } )
# => {:kahan=>0.0, :sum=>1.0}
```

合わない。
上記の独自実装が `Enumerable#sum` に負けている。

# 本家のソースを見る

ソースはこちら。

https://github.com/ruby/ruby/blob/v3_0_2/enum.c#L3927

論文へのリンクがあるけど、お金払わないと読めないようなのでソースを見る。

Wikipedia との違いは

||Wikipedia|ruby|
|:--|:--|:--|
|名前|Kahan|Kahan-Babuska|
|誤差の扱い|随時総和に算入|誤差を総和とは別に積んでいってあとで総和に算入|

という感じ。

ruby で書くとこう。

```ruby:ruby
def kahan_babuska(a)
    sum = c = 0.0
    a.each do |v|
        t = sum+v
        c += v.abs <= sum.abs ? sum-t+v : v-t+sum
        sum=t
    end
    sum+c
end
```

実行してみると

```ruby:ruby
ary = [1e18,1,-1e18]
p( { kahan:kahan(ary), kahan_babuska:kahan_babuska(ary), sum:ary.sum } )
# => {:kahan=>0.0, :kahan_babuska=>1.0, :sum=>1.0}
```

おお。合っている。

# もっと精度を良くする

ruby の sum は大変良いんだけど、もちろん誤差が出ることもある。

```ruby:ruby
ary = [1e40, -1e20, 1, 1e20, -1e40]
p( { kahan:kahan(ary), kahan_babuska:kahan_babuska(ary), sum:ary.sum } )
#=> {:kahan=>0.0, :kahan_babuska=>0.0, :sum=>0.0}
```

`1e40, -1e20, 1, 1e20, -1e40` は、 `kahan_babuska` をもってしても `1` にはならない。

で。
先程の `kahan_babuska` の実装を見ると、 `c` に対して `+=` を繰り返している。
これは `kahan_babuska` の出番だ。

というわけで、  `kahan_babuska` の誤差項に  `kahan_babuska` を適用してみた。

```ruby:ruby
def kb2(a)
    sum = c0 = c1 = 0.0
    a.each do |v|
        t = sum+v
        v0 = v.abs <= sum.abs ? sum-t+v : v-t+sum
        t0 = c0 + v0
        c1 += v0.abs <= c0.abs ? c0-t0+v0 : v0-t0+c0
        c0 = t0
        sum=t
    end
    sum+c0+c1
end

ary = [1e40, -1e20, 1, 1e20, -1e40]
p( { "+":ary.inject(&:+), sum:ary.sum, kb2:kb2(ary) } )
#=> {:+=>0.0, :sum=>0.0, :kb2=>1.0}
```

おお。二段 kahan-babuska ならちゃんと 1 になる。

# 計算量

ループ内の処理を比べてみた。

||加算|絶対値|比較|
|:--|:-:|:-:|:-:|
|単純加算|1|0|0|
|Kahan|4|0|0|
|Kahan-Babuska|4|2|1|
|2段 Kahan-Babuska|7|4|2|

そんなに誤差が嫌なら浮動小数点数を使うべきではないようにも思うけど、必要な場面に遭遇したら払う気になるコストではあると思う。
STM32G031のArduinoでの時計,DS1307で時刻表示OLED,OLED096UNO-A(PA11,P12にリマップ)


stm32g031,stm32l010用に最軽量化したもの

x がんばつて　OLEDをリマップした。PA11-CLK,PA12-IO
x 時間合わせは、別

1.SCLとSDAを接続、プルアップも忘れずに
2.電源の接続
3.下記のソースコードを書き込む
4.コンパイル実行で表示されたら終了
5.おわり

SSD1306 128x64


![h_con21.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/fb63ada0-d626-f2ef-8bd9-11afc02fb377.jpeg)




```c++


#include <Arduino.h>
#include <U8x8lib.h>
#include <Wire.h> //I2C library

//OLEDの定義
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(/* reset=*/ U8X8_PIN_NONE);        

//ds1307のアドレス
#define ADDR    0x68  // 2進数 1101000

int     cursor1;      //カーソルの位置
char    data_read[8]; //i2cバッファー

//時間をoled表示形式に変換
char ch_hex_a_b[4];
char *ch_hex_a(int l_num)
{
    ch_hex_a_b[0] = '0' + (l_num >> 4);
    ch_hex_a_b[1] = '0' + (l_num & 0xf);
    ch_hex_a_b[2] = ':';
    ch_hex_a_b[3] = 0;
    return(ch_hex_a_b);
}//ch_hex_a

//oledに表示
void i2c_oled_w(char *s)
{
  //文字の表示
  u8x8.drawString(
  ((cursor1-0) & 0x07  )*2, // x
  (cursor1>>3)*2,           // y
  s);

  while(s[0] != 0) {
    s++;
    cursor1++;
  }

} //i2c_oled_w

//初期化
void setup(void)
{
  //I2Cの初期化
  Wire.begin(PA12,PA11); //stm32g031

  //OLEDの初期化
  u8x8.begin();

  //フォントの設定
  u8x8.setFont(u8x8_font_px437wyse700a_2x2_r);

  //スタートロゴ
  cursor1 = 0;
  i2c_oled_w("START");
  delay(500);

  //画面の初期化
  u8x8.drawString( 0,0,"        ");
  u8x8.drawString( 0,2,"        ");
  cursor1 = 0;

}//setup

//メインループ
void loop(void)
{
  //カーソルのクリア
  cursor1 = 0;

  // レジスタのアドレスを先頭にする
  Wire.beginTransmission(ADDR);
    Wire.write(0x00);
  Wire.endTransmission();

  // I2Cスレーブに8byteのレジスタデータを要求する
  Wire.requestFrom(ADDR, 8);

  // 8byteのデータを取得する
  for (int ii = 0; ii < 8; ii++) {
    while (Wire.available() == 0 ) {}
    data_read[ii] = Wire.read();
  }//for

//  data_read[2]=0x12; //debug
//  data_read[1]=0x34; //debug
//  data_read[0]=0x45; //debug

  //画面に表示
  i2c_oled_w(ch_hex_a(data_read[2]));
  i2c_oled_w(ch_hex_a(data_read[1]));
  ch_hex_a(data_read[0]);ch_hex_a_b[2]=0;
  i2c_oled_w(ch_hex_a_b);
  
  //1秒待つ
  delay(1000);

}//loop




```


時間合わせ


```c++

#include <Arduino.h>
#include <Wire.h> //I2C library

//STM32G031J6M6
#define sdaPin PA12    // ArduinoA4
#define sclPin PA11    // ArduinoA5

#define ADDR    0x68  // 2進数 1101000

#define HH  12
#define MM  34

void setup()
{
  Wire.begin(sdaPin,sclPin); //STM32G031J6M6

//i2c書き込み
  Wire.beginTransmission(ADDR);

  Wire.write((char) 0    ); //0 レジスターの位置は0
  Wire.write((char) 0    ); //1 秒
  Wire.write((char) ( MM /10)*16+( MM %10) ); //2 分
  Wire.write((char) ( HH /10)*16+( HH %10) ); //3 時

  Wire.write((char) 3    ); //4 曜日
  Wire.write((char) 1    ); //5 日
  Wire.write((char) 1    ); //6 月
  Wire.write((char) 0x20 ); //7 年

  Wire.endTransmission();

} //end setup

void loop()
{

} //loop


```
QRコードの作成（Google Colabを使って簡単に！）

#はじめに
新型コロナの影響でますます利用価値が高まっているQRコード。プログラムに組み込む機会も増えていると思いますので、Pythonを使って簡単に表示させる方法を紹介します。以下内容の利用は自己責任でお願いします。

#Google Colab　コード
まず初めに、必要なモジュールをインストールします。
1.画像を扱うためのモジュールPillowをインストールします。

https://pillow.readthedocs.io/en/stable/

```python
!pip3 install pillow
```

2.QRコードの変換モジュールをインストールします。

https://pypi.org/project/qrcode/

```python
!pip3 install qrcode
```

3.文字列をQRコードに変換するためのコードを書きます。

```python
#モジュールのインポート
from PIL import Image
import qrcode
from IPython.display import Image,display_png

img = qrcode.make("この文をQRコードにして表示します。")

#test.pngというファイル名で保存し、表示させます。
img.save('test.png')
display_png(Image('test.png'))
```
MQTT を file に変換するプログラム

次のシステムを構築する常駐プログラムです。
[Grove IoT スターターキット for SORACOM で作るリモートカメラシステム](https://qiita.com/ekzemplaro/items/541b82908cec46f6aa7b)

```py:mqtt_to_file.py
#! /usr/bin/python3
#
#	mqtt_to_file.py
#
#					Aug/22/2021
# ------------------------------------------------------------------
import paho.mqtt.client as mqtt
import json

MQTT_HOST = "www2.ekzemplaro.org"
MQTT_TOPIC = "sample/imageTopic"
MQTT_PORT = 1883
MQTT_KEEP_ALIVE = 60
#
# ------------------------------------------------------------------
def on_connect(mqttc, obj, flags, rc):
	print("rc: " + str(rc))
#
# ------------------------------------------------------------------
def file_write_proc(file_name,str_out):
#
	fp_out = open(file_name,mode='w',encoding='utf-8')
	fp_out.write(str_out)
	fp_out.close()
# ------------------------------------------------------------------
def on_message(mqttc, obj, msg):
	file_name = "/var/tmp/mqtt_work/status.json"
#
	print(msg.topic + " " + str(msg.qos))
	str_out = msg.payload.decode()
	print(len(str_out))
#
	if len(str_out) < 100:
		print(str_out)
#
	unit_aa = json.loads(str_out)
	if "button" in unit_aa:
		print(unit_aa["button"])
		if (unit_aa["button"] == "on"):
			file_write_proc(file_name,str_out)
	elif "status" in unit_aa:
		print(unit_aa["status"])
		file_write_proc(file_name,str_out)
# ------------------------------------------------------------------
mqttc = mqtt.Client()
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.connect(MQTT_HOST, MQTT_PORT, MQTT_KEEP_ALIVE)
mqttc.subscribe(MQTT_TOPIC)

mqttc.loop_forever()
# ------------------------------------------------------------------
```

実行時の様子

```text
$ ./mqtt_to_file.py 
rc: 0
sample/imageTopic 0
16
{"button":"off"}
off
sample/imageTopic 0
15
{"button":"on"}
on
sample/imageTopic 0
16
{"button":"off"}
off
```

systemd で常駐化する。

ワンショットサービスの作成

```bash:/opt/one_shot/one_shot.sh
#!/bin/bash
#
#	one_shot.sh
#
LOG_FILE="/tmp/one_shot.log"
date >> $LOG_FILE
/var/www/html/mqtt/mqtt_to_file/mqtt_to_file.py >> $LOG_FILE
echo "one_shot" >> $LOG_FILE
date >> $LOG_FILE
#
```

```text:/etc/systemd/system/one_shot.service
[Unit]
Description=one_shot shell
After=netctl@profile.service

[Service]
# Environment=NODE_PORT=3000
Type=simple
User=www-data
Restart=on-failure
ExecStart=/bin/bash /opt/one_shot/one_shot.sh
WorkingDirectory=/tmp

[Install]
WantedBy=multi-user.target
```

起動

```bash
sudo systemctl start one_shot
```

OS再起動で起動するように設定

```bash
sudo systemctl enable one_shot
```
use btckeygen generate more bitcoin HD address


 ```
btckeygen -bip39 -pass=123456 -n 6 -mnemonic "bounce ..."

Path(BIP84)        SegWit(bech32)                             WIF(Wallet Import Format)
------------------------------------------------------------------------------------------------------------------
m/84'/0'/0'/0/0    bc1q7jj8h5da5m882qhnsk0x4uxf4jvst6j9sg0rjl L2tnYt5qdw2La6KVJ9nsx6djgbSrLCkxLaGQV3kDdsZ9KL6X7cXQ
m/84'/0'/0'/0/1    bc1qr4yake5mc90g5mddq5hcunr7ed6gejfekpq6wz L5Ks6yoHvAH9j35nLnspDUR2HkWHkpMHkuMNHkpFpokG94wXPxCv
m/84'/0'/0'/0/2    bc1qz65dsqjlx8nl6ypw6qu3tapsruzn4wqnlcdwge KzsckdCqcHa57e3HdqfMi1iZJWdfbh7MwCNq87ie5GvHRB2mvPTc
m/84'/0'/0'/0/3    bc1qssl77lvrhqjgsturt9gphfh2tejvce90u2wg85 L5mZA2DdTQroGBEfeY7LDeBfseP8CSt241xxaWW3vkeRs2KE1oR7
m/84'/0'/0'/0/4    bc1q6a66tg9gf2yf0d3vrgxznh8mmqw6pe7wsc6jzt L1Pz6oCUc5xWscZLgHTrnkxJR7YBqr6xgFRqoaf6cYNRM8qdmDgp
m/84'/0'/0'/0/5    bc1qj84lqu6vev0yslfpvnah4qr6d4z7ltyqw2e7um L2uXtavzaVZUdKv5v5ScwKb5fL9q2Npfph3LcB2qDdLhSR8HQoS6
```

---

参考：

https://github.com/modood/btckeygen
HTTP POST を file に変換する API

次のシステムを構築するAPI です。
[Grove IoT スターターキット for SORACOM で作るリモートカメラシステム](https://qiita.com/ekzemplaro/items/541b82908cec46f6aa7b)

```py:http_to_file.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
#	http_to_file.py
#
#					  Aug/22/2021
#
# ---------------------------------------------------------------------
import	os
import  sys
import  json
import	cgi
import	fcntl
#
# ---------------------------------------------------------------------
def file_write_proc(file_name,str_out):
	fp_out = open(file_name,mode='w',encoding='utf-8')
	fcntl.lockf(fp_out, fcntl.LOCK_EX)
	fp_out.write(str_out)
	fp_out.close()
#
# ---------------------------------------------------------------------
if os.environ['REQUEST_METHOD'] == 'POST':
	length, _ = cgi.parse_header(os.environ['CONTENT_LENGTH'])
	data = sys.stdin.buffer.read(int(length))
#
	json_str = data.decode("utf-8")
#
	sys.stderr.write("*** http_to_file.py *** start ***\n")
	file_name = "/var/tmp/mqtt_work/status.json"
	file_write_proc(file_name,json_str)
#
	data_aa = {}
	str_aa = json.dumps(data_aa)
#
	print('Content-Type: text/json; charset=utf-8')
	print("Access-Control-Allow-Origin: *\r\n")
	print(str_aa)
#
# ---------------------------------------------------------------------
```
【応用情報】午後９プロジェクトマネジメント頻出まとめ

#マネジメントの流れ

##➀プロジェクト統合マネジメント
プロジェクト憲章や計画書の作成など、各作業を統合的に管理・調整することで、目的を達成するための活動。

##②スコープマネジメント
遂行に必要な作業を過不足なく洗い出す。

・スコープ定義：プロジェクトの範囲を確認。
・WBS：青果物や作業を要素分解し、スコープ全体を定義。

##➂タイムマネジメント
・PERT・アローダイアグラム


##④コストマネジメント
・コスト見積り
・標準タスク法：作業項目を単位作業まで分解し、ボトムアップに積み上げていき、全体の工数を見積もる。
・COCOMO：ソフトウェアの規模によって係数を考慮。
・ファンクションポイント法（FP）

★コンティンジェンシー予備：作業の所要時間やコスト

##⑤品質マネジメント
・品質管理
・品質保証
・品質コントロール



#アローダイアグラム
クリティカルパス＝最早結合点時刻（各結合点に一番遅くつく時間）と最遅結合点時刻（後ろからすすめて一番早い時間）が一致する各項目を結んだパス。


#プロジェクトの粒の大きさ
WBS＞ワークパッケージ＞アクティビティ

#バーンダウンチャート
縦軸に

#EVM
コストで進捗管理する方法。客観的に管理するために、単位をコスト（円）で統一する。

###ＰＶ
計画価値。計画時に見積もられる。
予算×予定していた進捗率

###ＥＶ
実績価値。
予算×今の進捗率

###ＡＣ
実コスト。実際にかかったコスト

生産性＝完成工数÷EV（当初計画）
実際の生産性は、完成工数÷ACで求められる。

**当初の予定との比較は、紛らわしいがPVではなくEVで考える。**


#よく間違える！PIMBOKのリスク
##マイナスのリスク
回避
リスクそのものを取り除いたり、プロジェクトに影響がないようにスコープや目標を縮小・変更する方策。
**取引先の業績が悪化して，信用に不安があるので，新規取引を止める**

転嫁
リスクによるマイナスの影響を第三者へ移転する戦略。リスクのある業務・作業のアウトソーシングや、損害保険の契約によってリスクの影響を自社から他社に移転する。

軽減
リスクの影響範囲を狭くしたり、発生確率を低減するような方策。
**個人情報の漏えいが起こらないように，システムテストで使用する本番データの個人情報部分はマスキングする**

受容
リスクが現実化した時の影響が許容可能範囲内である時やリスクの除去が困難である場合に、特段対策をせずにそのままにしておくこと。対策費用が予想される損失金額を上回っているときなどに採られる方策。
**あるサブプロジェクトの損失を，他のサブプロジェクトの利益と相殺する**

#PMO
・プロジェクトに関連するガバナンス・プロセスを標準化し，資源，方法論，ツール及び技法の共有を促進する。
・マネジメント業務の支援、PMのサポートをする。
リストの要素を横並びで出力する

# "".join()の使い方
"要素の間に入れる文字".join(リスト)
**※リストの要素がint型だと使えない**

```.py
l = ["a", "b", "c", "d"]
print(",".join(l))
# a,b,c,d
print("".join(l))
# abcd
print(" ".join(l))
# a b c d
```

## リストの要素がint型の場合
str型に変換してから"".join()を使う

```.py
l = [1, 2, 3, 4]
l = [str(x) for x in l]
print(" ".join(l))
# 1 2 3 4
```
Hugo で Image Processing を使うとエラーが出る件

Hugo には [Image Processing という機能](https://gohugo.io/content-management/image-processing/)があり、HTMLファイルを生成する時に画像も適当に処理してくれます。

この機能を使用しようとテンプレートをいじっていたところ、以下のようなエラーが出てきました。

```
executing "_default/_markup/render-image.html" at <$image.Resize>: error calling Resize: *resources.genericResource is not an image
```

このとき使用していたテンプレートの断片は以下のようなものです。

```
{{ $image := .Page.Resources.GetMatch (printf "%s" (.Destination)) }}
{{ $small := $image.Resize "480x webp" }}
{{ $medium := $image.Resize "768x webp" }}
{{ $large := $image.Resize "1024x webp" }}
{{ $xlarge := $image.Resize "1920x webp" }}
{{ $alt := .PlainText | safeHTML }}
<picture>
  <source media="(max-width: 480px)" srcset="{{ $small.RelPermalink }} 480w">
  <source media="(max-width: 768px)" srcset="{{ $medium.RelPermalink }} 768w">
  <source media="(max-width: 1024px)" srcset="{{ $large.RelPermalink }} 1024w">
  <source media="(max-width: 1920px)" srcset="{{ $xlarge.RelPermalink }} 1920w">
  <img src="{{ $image.RelPermalink }}" alt="{{ $alt }}">
</picture>
```

画面解像度に応じて適当な解像度のファイルを呼びだすようにしたいんですね。

このエラーが出る原因は、本来 Image Processing で処理できない SVG 形式のファイルまで処理しようとしているためでした。

以下のようにsvgファイルを除外するようにして解決しました。

```
{{ $image := .Page.Resources.GetMatch (printf "%s" (.Destination)) }}
{{ if ne $image.MediaType.SubType "svg" }}
{{ $small := $image.Resize "480x webp" }}
{{ $medium := $image.Resize "768x webp" }}
{{ $large := $image.Resize "1024x webp" }}
{{ $xlarge := $image.Resize "1920x webp" }}
{{ $alt := .PlainText | safeHTML }}
<picture>
  <source media="(max-width: 480px)" srcset="{{ $small.RelPermalink }} 480w">
  <source media="(max-width: 768px)" srcset="{{ $medium.RelPermalink }} 768w">
  <source media="(max-width: 1024px)" srcset="{{ $large.RelPermalink }} 1024w">
  <source media="(max-width: 1920px)" srcset="{{ $xlarge.RelPermalink }} 1920w">
  <img src="{{ $image.RelPermalink }}" alt="{{ $alt }}">
</picture>
{{ end }}
```
urlからExpoアプリを起動する

# やりたいこと
- モバイルで、ブラウザからurlリンクで、インストール済みのExpoアプリを起動する
- Deeplinkを使用しない。という例をベースにする

https://docs.expo.dev/guides/linking/#when-to--not--use-deep

# 前提 
- Expo 4.7.2

# 参考資料
> Linking
> https://docs.expo.dev/guides/linking/

# 実装例
- 実装例は、動作するクラスではなく、機能の実現に必要なコードのみを記載。

## urlリンクを生成する処理

```任意の画面等で.js
    const initialUrl = await Linking.getInitialURL();
　　　　　　　console.log(initialUrl+"?docid=zzkiRCXRlQa1Ukv88gva")
```

- 開発環境だと次のような値が入る。 `exp://127.0.0.1:19000`
- スタンドアロンアプリとして出力したあとはアプリのスキーマとなる。 スキーマは（`myapp://` の部分） app.jsonで定義しておく必要がある
- 参照：　https://docs.expo.dev/guides/linking/#linking--module
- アプリ内での取り回しのため、クエリパラメータで値を付与しておく。たとえばFirebaseで検索をかけるためのdocidを付与する

## リンクからのアクセスを受け付ける処理

```app.js
import React, {useEffect} from 'react';
import React, {useEffect} from 'react'; 
import * as Linking from 'expo-linking';

  useEffect(() => {
    // URLからの遷移
     urlFlg = false

    // リスナーの設定。UserEffectを利用すると設置とリムーブを次のように定義できる。
    Linking.addEventListener("url", _handleRedirect);
    return () => {
      Linking.removeEventListener("url", _handleRedirect);
    };
  });

// urlからのアクセスを処理する
handleRedirect =async (event) => {
    urlFlg = true; // useEffect自体はリンクから以外も実行されるため、Deeplink以外の場合と切り分けのためのフラグ

    // urlをパースし、引数を取得する
    let data = await Linking.parse(event.url)
      console.log("パラメータ===========")
      console.log(data.queryParams) //これで取得可能
      // たとえばクエリパラメータにdocidを仕込んでいた場合は次で取得する
      // 対象docid data.queryParams.docid

      // 画面遷移
      navigation.navigate('表示する画面',{id: data.queryParams.docid}); // たとえば画面遷移先にidを渡し、遷移後にそれで適切なデータを画面に表示する
  };

  if(!urlFlg){
    console.log("URLからの遷移では無い")
    // URLからの遷移ではない場合のナビゲーションを定義しておく
  }


```
ABC215をPythonで実装してみた。

# 目的
ABC215の復習をするにあたり備忘録として残す。

## [A - Your First Judge](https://atcoder.jp/contests/abc215/tasks/abc215_a)

```python
N = input()

if "Hello,World!" == N:
    print("AC")
else:
    print("WA")
```

## [B - log2(N)](https://atcoder.jp/contests/abc215/tasks/abc215_b)

```python
N = int(input())

for k in range(100):
    if 2**k <= N:
        pass
    else:
        result = k-1
        break
print(result)
```
他にもlog2を使用する方法もあるが数値誤差があるので避けた方がよい。

https://yuyuyu-331.hatenablog.com/entry/2021/08/22/005134

## [C - One More aab aba baa](https://atcoder.jp/contests/abc215/tasks/abc215_c)

```python
from itertools import permutations

S, K = input().split()
K = int(K)

v = [''.join(p) for p in permutations(S)]
v = set(v)

print(sorted(v)[K - 1])
```

permuationsについては下記を参照してください。

https://note.nkmk.me/python-math-factorial-permutations-combinations/

# 参照

https://qiita.com/c-yan/items/c02a960fc9b41db18fdd
Heroku デプロイのエラー 改行コードに注意

#はじめに

Heroku でデプロイする際に以下のエラーでハマりました。いろいろ試して原因が  Aptfile の**改行コード**にあることに行きつきました。

```
remote:  !     Push rejected, failed to compile Apt app.
remote:
remote:  !     Push failed
remote: Verifying deploy...
remote:
remote: !       Push rejected to [app-name].
remote:
To https://git.heroku.com/[app-name].git
 ! [remote rejected] main -> main (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/[app-name].git'
```

##使用環境
Windows10


#Heroku の Aptfile の改行コードは LF にする

Python でウェブアプリを作成し Heroku へデプロイしようとしていました。まず最初にハマったのは Heroku 環境で OpenCV を使うところですが、この点に関しては多数の記事があり参考にさせていただきました。

https://qiita.com/haru1843/items/210cb08024195b9d1bc8

ですが同じようにやってもデプロイまで行けませんでした。最初に記載したエラーログを見ると Aptfile で引っかかっているようです。

いろいろ試しましたが、デプロイの方法を教えてくれた人がくれた Aptfile を使うとうまくいくことが分かりました。どうも自分で作った Aptfile が原因なようです。

見た目はそっくりなのですが、良い Aptfile とダメだった Aptfile の 2 つをよくよく見比べると改行コードが異なっていました。

Visual Studio Code (VS Code) では改行コードは画面右下に以下のように表示されています。

![vscode_s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1158524/7c37c9f7-7781-ad85-736c-06a49ebbb1d0.png)


PyCharm でも同じような場所に表示されていました。

![pycharm_s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1158524/98dac469-082a-8003-0946-136d8d455381.png)


Windows 環境の改行コードは **CR-LF** ですが Heroku (Linux) 環境の改行コードは **LF** であるためここで引っかかっていたようです。

https://cprogram.net/line-feed-code/

やり方はいろいろあるようですが、VS code や PyCharm で CR-LF となっているところをクリックし変更することでデプロイできるようになりました。今回 Procfile は 1 行だったので問題ありませんでしたが、おそらくこちらも同様です。

![pycharm_LF_s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1158524/5d7f0f7c-2c5e-bb55-3d31-3e2cd1058209.png)



#おわりに
以前書いた記事の中でも desktop.ini というファイルでハマったのですがそれも Windows 特有の問題でした。前回に引き続き今回も Windows 特有の問題でハマりました。Unix ベースの環境であればこういう余計な問題は回避できそうですね。エンジニアに Mac が好まれる理由が少し分かった気がします。プログラミングにおいて Windows の情報はどちらかというとマイナーで、特に私のような初心者には気が付きにくいと思いましたので気にしてみてください。
MVVMとダイアログ

[MVVMとNavigation](https://qiita.com/miswil/items/565e5585686daa10aea8)の続きです.
ナビゲーションではなくダイアログを表示するには。

# 基本的なアイデア

基本的にDIとMessengerを使うだけで、やることは初歩的なダイアログ表示方法と同じです。
※MVVMフレームワークとして[Microsoft.Toolkit.Mvvm](https://github.com/CommunityToolkit/WindowsCommunityToolkit)を前提としたサンプルコードを記載しています。

# ViewとViewModelの関連付け

今回動的に解決されるのはViewのほうなのでViewLocatorクラスを作ります。シンプルに対応をマッピングします。DIコンテナとかを内部でラップしてもいいでしょう。Appクラスのスタートアップ等でマッピングを実装します。

```ViewLocator.cs
    class ViewLocator
    {
        public static ViewLocator Default { get; } = new ViewLocator();

        private Dictionary<Type, Type> locator = new Dictionary<Type, Type>();

        public void Register<TViewModel, TView>() where TView : Window
        {
            this.locator.Add(typeof(TViewModel), typeof(TView));
        }

        public Window Locate(Type viewModelType)
        {
            var winType = this.locator[viewModelType];
            return (Window)Activator.CreateInstance(winType);
        }
    }
```
# Viewロジック

ダイアログ表示は最終的にViewのメソッドになるのでBehaviorを追加します。MessegerからMessageを受け取り、ViewModelの型からViewを特定・生成し、ダイアログを表示するシンプルな内容。ViewModelの相互作用としてダイアログ表示を書いているので、Messageの中にViewModelが含まれます。
コードは省略しますがWindowにアタッチしておきます。

``` ShowDialogBehavior.cs
    class ShowDialogBehavior : Behavior<Window>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            WeakReferenceMessenger.Default.Register<ShowDialogMessage>(this, showDialog);
        }

        private static void showDialog(object recipient, ShowDialogMessage message)
        {
            var behavior = (ShowDialogBehavior)recipient;
            var dialog = ViewLocator.Default.Locate(message.ViewModel.GetType());
            dialog.DataContext = message.ViewModel;
            dialog.Owner = behavior.AssociatedObject;
            dialog.ShowDialog();
        }
    }
```
# Messageの実装

Messengerで渡すMessageです。先述の通りViewModelを含みます。今回はこれだけですが、実用的にはOwnerを設定するかとか、モーダルかモードレスかといったオプションを含めると思います。

``` ShowDialogMessage.cs
    class ShowDialogMessage
    {
        public object ViewModel { get; set; }
    }
```
# ViewModelの実装

ViewModelを生成し、Messageに格納し、Messengerに渡します。シンプルにnewしていますが、ここでDI等を使うのは簡単です。

``` MainWindowViewModel.cs
        private void ShowDialogCommandExecute()
        {
            var viewModel = new DialogWindowViewModel();
            WeakReferenceMessenger.Default.Send(new ShowDialogMessage { ViewModel = viewModel });
            this.DialogResult = viewModel.Input;
        }
```
このロジックを基底クラスに定義したり、DIでサービスを受け取ってそのメソッドを実行したりするようにすれば再利用も簡単になります。

# まとめ

DataTemplateを使ってデータ駆動ナビゲーションをした前回の記事に比べ、ただMessengerでダイアログを出すだけになりました。
結局Prismとどちらがいいかというと…
コードは[こちら](https://github.com/miswil/MVVMNavigationSample)
AWSアクセスキー流出と対策について

#きっかけ
AWS上のセキュリティについて調べるきっかけとなったのは、github上へのアクセスキーの流出です。
ポートフォリオ作成途中、Docker環境上でいろいろ試行錯誤するためにアクセスキーをベタ打ちしてしまっていたのを忘れてローカルリポジトリにpushしてしまいました。
即座に、AWSから警告のメールが届きよくよく調べてみるとそのままでは、不正利用されてしまい高額請求がくる恐れがあることがわかりました。
そこで以下に流出した際のとるべき対応と対策をまとめます。

#流出してしまったら
以下、AWSから指示された対応手順に沿って記載します。
##①アクセスキー無効化、削除する
まずは、一番にアクセスキーを使用できない状態にしましょう。**(これが一番大事です)**
**たとえ公開リポジトリを削除したとしても、すでにアクセスキーが漏洩してしまっていたら悪用されてしまいます。**

すでにアプリケーション等で使用している場合は、交換する必要があるのでまずは削除せずに以下の手順で無効化します。
①二つ目のアクセスキーを作成する
②流出したキーを無効化する
③アプリケーションに新しい鍵を設定
ここまで行い問題がなければ元のキーは削除、問題があれば再度有効にして対応します。

また、ここでAWS初心者の人はrootアクセスキーとIAMユーザーのアクセスキーでこんがらがることもあるかもしれません。

##②承認されていないアクティビティがないか確認する
アクセスキーを無効化する前に、すでに不正使用されている場合もあります。
その場合、CloudTrailのログを確認し、見覚えのないものを発見したらアカウントを保護するために、未承認のIAMユーザー、ロール、ポリシーを削除し、一時的な認証情報を失効させます。

##③不正なEC2インスタンス、Lambda関数、EC2 Spotの入札など、不正なAWS利用がないかAWSアカウントを確認
こちらも②と同様にCloudTrailのログを確認します。
またそれと合わせて、マネジメントコンソールからも直接それぞれのサービスについて不正な利用がないか確認します。
注意事項としては、コンソール上で表示されているのは設定されている一つのリージョンに関してのみなので、リージョンを適宜切り替えて確認しましょう。
不正に起動されているものがあれば削除しておきましょう。


#対策
今回キーの流出を起こしてしまったのは、人為的なミスとシステム上で対策ができていなかったことが原因だと考えています。

今後同じようなことを起こさないために、以下の対策を取り入れます。
##システム上の対策
###①git-secretsの導入
**導入することでソースにアクセスキーを含んだままcommitできなくなります**
今回の原因がうっかりということもあり、事前に秘密情報が含まれているコミットをリジェクトできるようにしておきます。
pushの際には気をつけていても、うっかりcommitしてしまっていたなんてことも考えられるので、、、
###②IAMのベストプラクティス
・[IAMベストプラクティス](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/best-practices.html)に沿って、個々のユーザーに必要となるポリシーのみのアタッチとします。(かなり勉強が必要そうですが頑張ります)
・必要な時以外はアクセスキーの発行は控えます。
・定期的にアクセスキーを変更します

#まとめ
今回、アクセスキーが流出してしまった際は非常に焦りました。
しかしながら、今後エンジニアになる上で必要となる知識であることを実体験で体感できた良い経験だとも感じております。
これを機にセキュリティ面に関しても力を入れて勉強します。

##参考文献
[git-secretsでgit経由の情報漏洩を防ぐ
](https://qiita.com/irico/items/01a36be74fb26148b8e6)
[AWS初心者向けWebinar](https://www.slideshare.net/AmazonWebServicesJapan/awswebinar-aws-56260969)
GoogleAdsenseとGoogleAnalyticsのコンバージョンは実は少し違う

# はじめに
GoogleAdsenseとGoogleAnalyticsとでコンバージョン数が違うことに疑問を持ったことがある人に向けての記事です。

# 結論
GoogleAdsenseとGoogleAnalyticsとでは、コンバージョンを記録する日時が異なる。前者は広告を踏んだ日時であり、後者はコンバージョンを記録したときの日時である。

# 詳細
例えばユーザー登録のコンバージョンを取るときに、「登録完了」というボタンをクリックしたタイミングでコンバージョンを記録する設定をGoogleAdsenseとGoogleAnalyticsの両方で同じようにしたとします。「登録完了」を押したタイミングで両方に記録されるのは当然ですが記録日時が違います。GoogleAdsenseの方は広告をクリックした日時であり、GoogleAnalyticsは「登録完了」ボタンをクリックした日時です。なので、ズレが生じます。

# まとめ
意外と気になっていたので原因が分かってスッキリして良かったです。
[メモ] Django REST の GET 応答に任意の情報を詰める


|本シリーズのトップページ|
|:--|
|https://qiita.com/robozushi10/items/4559a281d0319eb62c6c|

## 概要

ページネーション(Pagination)を自作することになった際に、
次の 2つの情報を GET の応答で返却することになった.
(デフォルトの GET の挙動だと、(A) のみしか返却できない)

(A) GETで要求された内容に対するレコード
(B) 全レコードの件数


## views.py

このとき APIView を継承したクラスで get をオーバライドしていたとした場合、
次のように変更することで上記の要求が満たせる.

```diff
 class XAPIView(views.APIView):
   def get(self, request, *args, **kwargs):
     略
     # モデル X のデータを 4件取り出してリスト L に詰める
     L = []
     L.append( X.objects.all()[0:4] )
     # 4件のレコードを返却する
     ser = XSerializer(L, many=True)
     # 4件のレコードは 'items' に詰めて、モデル X が全部で何件あるかという情報を 'total' に詰める.
     return Response(
-      ser.data
+      {
+        'items': ser.data,
+        'total': X.objects.all().count()
+      },
       status=status.HTTP_200_OK
     )
```

　

以上は、同僚に教えてもらった内容である. Micheal さんありがとう!.