OmniAICreator/Qiita-1.07M huggingface dataset:
AWSで社員自己紹介サイトを作ってみた

#1.活動内容

久保田チームではAWSを活用して社員の自己紹介サイトを作成してみました。
なお、作成にあたりチーム内で下記のような役割分担をして作成を進めました。
・インフラ構築
・フロントエンド作成
・DB構築



#2.利用したサービス
###IAM
アカウント
　・rootユーザー
　・IAM ユーザー

設定
　・ポリシー
　・グループ
　・ロール

###VPC
・サブネットグループ
・セキュリティグループ

###EC2
下記をインストール
・Apache
・MySQL
・phpAdmin
・WordPress

###RDS
下記をインストール
・MySQL



#3.成果物
1.各種サービスの手順書​
　・EC2インスタンス作成とWordpressインストール手順​
　・RDSインスタンス作成手順​
　・Wordpressへの自己紹介プラグイン導入手順​

2.AWS上のサイト構築​
　・社員自己紹介サイト


#4.サイト構成図

![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530983/7aaadaa5-d440-f10d-c52a-e87c4a39679d.png)



#5.実際のサイトページ


###トップページ
![画像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530983/dd4d83f8-aa37-b493-8b67-b7844e094308.png)


###自己紹介ページ
![画像3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530983/56a47d7c-1f1f-8261-3ecf-03eed4102f4e.png)



※架空の人物を記載



#6.課題
自己紹介ページ(プラグイン)のデータが格納されているテーブルが見あたらず、RDSとWordpressを連携させることができなかったという課題が残りました。​
![画像4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/530983/217ef2ed-4d65-e4af-8ad2-33deb5caee50.png)





#7.最後に
今年のチーム会の活動では、実際にAWSを利用して自己紹介サイトの構築を行うことでAWSの各種サービスの理解を深めることができました。
来年の活動では、課題解決と自己紹介サイトの改善を行うことを検討したいと考えております。
Arduinoの勉強（番外）　第8弾：デジタル通信方式 (4) - CAN通信方式

# #CAN通信方式
##はじめに
ハードウェアの勉強のためにArduinoを復習も兼ねて参考教材に沿って，勉強する．細かな部分の理解までして，自在に扱えるようになることが目的である．今回は教材に沿わない番外編だが，第8弾として，「CAN通信方式」を扱う．

## CANとは
![slide1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/def98d08-fc43-e0c1-c61d-0051ec376fec.png)
![slide2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/9f248559-3893-d220-bd9f-d4e66e6ed7cd.png)

## CANのメリット
![slide3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/b3df83bf-e3a0-b58f-cb15-67633624101a.png)

## CANのデメリット
![slide4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/52862023-4ebe-ced9-d608-6e80dfd9f618.png)


## マルチマスター方式
![slide5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/36a030f8-0f0c-53db-f25c-aa27fc50e01e.png)
### 例：CAN通信を使用したモータ制御
![example.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/dae45a02-927f-b922-235f-450930272909.jpeg)


## その他の特徴
![slide6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/596ac291-d3de-a667-2892-2a9eb5b5a2fd.png)
![slide7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951300/68b2e242-b835-da58-55dd-a0d6e2f79d97.png)

## 感想
正直，今回のまとめで，CAN通信を完全に網羅することはできていないが，ざっくり概要を理解することはできたと思う．要は，「信号線を単純化して配線を少なくするとともに，CANの通信方法によりノイズにも強い通信を実現することができる」，簡単ではあるが，このように理解している．一番の強みはノイズに強い部分なのかなと思う．配線の単純化であれば，ほかの通信方式でもでき得る．

## 参考文献
- 技術者ブログ「CAN通信を使用したモータ制御について」　株式会社明和製作所
（https://www.meiwa-ss.co.jp/blog/20210712_6590/）

- 組み込み開発フォーラム「CANプロトコルを理解するための基礎知識」　増田浩史（MONOist）
（https://monoist.itmedia.co.jp/mn/articles/0806/16/news124.html）

- スキル「進化する通信システム」　SEIBIKAI
（https://www.seibikai.co.jp/archives/skill/shindan/660）
Flutter BottomNavigationBarのラベル(テキスト)を非表示にする方法

BottomNavigationBarのテキストを表示したくなくてググったけど、日本語の記事では
テキストを空欄にしてフォントサイズを0にしている記事しかなく気持ち悪かったので他の方法ないか探してみた。

https://iori.dev/archives/2333

英語の記事で良いの発見

https://devsheet.com/code-snippet/hide-title-from-bottomnavigationbar-in-flutter/

```.dart
BottomNavigationBar(
    showSelectedLabels: false,
    showUnselectedLabels: false,
    items: <BottomNavigationBarItem> []
);
```
Synology NASのdockerでXMRigを利用してマイニング

# 環境
- 製品 : `Synology DS720+`
 - DSMバージョン : `DSM 7.0.1-42218`
 - メモリを10GBに拡張済み
- Docker
 - パッケージセンターより`20.10.3-1239`をインストール済み

# コンテナ作成
- レジストリより`ubuntu:latest`をダウンロードする。
- 詳細設定より、任意のDSM上のフォルダを`/data`にマウントする。(後で利用するため)
- その他は特に変更せずダウンロードしたイメージからコンテナを作成する。

# 端末に接続
- 起動したコンテナの詳細画面より端末に接続する。
- これ以降の手順でDSM画面上の端末にコマンドを貼り付ける際、一度`Ctrl+A`を入力して`プレフィックスモード`に入ってから`Ctrl+V`で貼り付けることができる。
- docker上の操作では`sudo`を付ける必要はない(?)

# XMRigをビルド
[参考](https://xmrig.com/docs/miner/build/ubuntu)

## 必要なパッケージをインストール
利用しているイメージは最低限のコマンドしか利用できないため、必要なパッケージをインストールする。

```sh
apt-get update
apt-get install git build-essential cmake libuv1-dev libssl-dev libhwloc-dev -y
```

`tzdata`の設定時にタイムゾーンを訊ねられた場合は、`Asia`、`Tokyo`となるように選択する。

## ソースコードを取得
```sh
cd ~
git clone https://github.com/xmrig/xmrig.git
```

## ソースコードを編集
デフォルトでは寄付の割合が1%と設定されているため、0%に変更する。

```sh
sed -i -e "s/DonateLevel = 1;/DonateLevel = 0;/g" ~/xmrig/src/donate.h
```

## ビルド
```sh
mkdir xmrig/build && cd xmrig/build
cmake ..
make -j$(nproc)
```

# configurationsファイルを用意する
[こちら](https://xmrig.com/docs/miner/config)のドキュメントや[デフォルト設定ファイル](https://github.com/xmrig/xmrig/blob/master/src/config.json)、[ウィザード](https://xmrig.com/wizard)を参考に`config.json`ファイルを用意し、コンテナにマウントしているフォルダに配置する。

以下は`nicehash`を利用する設定ファイルの例。

```conf
{
    "autosave": true,
    "cpu": true,
    "opencl": false,
    "cuda": false,
    "pools": [
        {
            "coin": null,
            "algo": "rx/0",
            "url": "randomxmonero.usa-west.nicehash.com:3380",
            "user": "<マイニングウォレットアドレス>.synology",
            "pass": "x",
            "tls": false,
            "keepalive": true,
            "nicehash": true
        }
    ]
}
```

# XMRigを実行
```sh
~/xmrig/build/xmrig -c /data/config.json
```

以下のような初期表示が出力された後にマイニングが開始される。

```
 * ABOUT        XMRig/6.16.2 gcc/9.3.0
 * LIBS         libuv/1.34.2 OpenSSL/1.1.1f hwloc/2.1.0
 * HUGE PAGES   supported
 * 1GB PAGES    disabled
 * CPU          Intel(R) Celeron(R) J4125 CPU @ 2.00GHz (1) 64-bit AES
                L2:4.0 MB L3:0.0 MB 4C/4T NUMA:1
 * MEMORY       7.1/9.6 GB (74%)
 * DONATE       0%
 * ASSEMBLY     auto:intel
 * POOL #1      randomxmonero.usa-west.nicehash.com:3380 algo rx/0
 * COMMANDS     hashrate, pause, resume, results, connection
 * OPENCL       disabled
 * CUDA         disabled
```

`randomxmonero`を`rx/0`アルゴリズムでマイニングした際に`315 H/s`ほど出る模様。
【Rails】SQLでor検索

A OR Bの結果が出力される。

```ruby
User.where(name: 'コナン')
    .or(User.where(email: 'ran@gmail.com'))
#=> SELECT `users`.* FROM `users` WHERE (`users`.`name` = 'コナン' OR `users`.`email` = 'ran@gmail.com')
```

orメソッドがまだなかったときはこんな書き方をよく見かけたらしい。

```ruby
User.where("name = ? OR email = ?", 'コナン', 'ran@gmail.com')
#=> SELECT `users`.* FROM `users` WHERE (name = 'コナン' OR email = 'ran@gmail.com')
```
pythonで書かれたコードの実行時に、別途用意したファイルを標準入力から入力する

n版煎じですみません。
pythonで書かれたコードの実行時に、標準入力から別途用意したファイルを入力する方法メモ。

以下のようなコードがある場合

```python:test.py 
#!/bin/python3

line1 = input()
line2 = input()
print(line1)
print(line2)
```

何もせずに実行すると以下のようにCUIからの標準入力待ちになる。

```
$ python3 test.py
this_is_line1 ★入力する
this_is_line2 ★入力する
this_is_line1 ★出力される
this_is_line2 ★出力される
```

以下のようにすると別途用意したファイル（`input.txt`）を標準入力から入力できる。

```
$ python3 test.py < input.txt
this_is_line1
this_is_line2
```

```text:input.txt
this_is_line1
this_is_line2
```

### 追記

（コメントでアドバイス頂いたように）
`cat`の出力をパイプで渡した方が覚えやすい気がしています。

```
$ cat input.txt | python3 test.py
this_is_line1
this_is_line2
```

```text:input.txt
this_is_line1
this_is_line2
```


# 参考
[Pythonでcatみたいに標準入力またはファイル名指定でテキストを読み込む方法](https://qiita.com/hi-asano/items/010e7e3410ea4e1486cb)
PIXI Sprite(Class)をextendsしてカスタマイズする

# はじめに

**new** **PIXI.Sprite()** で作成したスプライトオブジェクトの型にはないプロパティを追加したかったが、そのまま追加すると「プロパティ'〇〇'は型 'Sprite'に存在しません」とTypescriptエラーが出るので、PIXI.Spriteをextendsしてカスタマイズしようと思います。

# PIXI.Spriteをextendsする
PIXI.Spriteをextendsするには、

```jsx
import * as PIXI from 'pixi.js'

export class ExtendedSprite extends PIXI.Sprite {
	constructor(texture: PIXI.Texture) {
		super(texture)
	}
}
```

と定義すればok。

PIXI.Spriteは引数にPIXI.Textureが必要なため、constructorでtextureを受け取り、super(texture)でextendsしたClass(PIXI.Sprite)を呼び出すだけです。
実際に使用する際は、**new** **PIXI.Sprite()**の代わりに、

```jsx
import image from 'assets/image.png';

const texture = PIXI.Texture.from(image)
const sprite = new ExtendedSprite(texture)
```

と記述します。

# extendsしたClassにプロパティを追加する
では、実際にこのextendsしたClassに新しくプロパティを追加してみます。例えば、Number型のcategoryIdを追加すると以下のようになります。

```jsx
export class ExtendedSprite extends PIXI.Sprite {
	public categoryId?: number
	constructor(texture: PIXI.Texture) {
		super(texture)
	}
}
```

これで、

```jsx
const texture = PIXI.Texture.from(image)
const sprite = new ExtendedSprite(texture)
sprite.categoryId = 1
```

とすることで、PIXI.Spriteを継承しつつもプロパティcategoryIdを持つClassを作成することが出来ました。
categoryIdごとにPIXI.Spriteを条件分岐して、Canvasに描画する際には便利だと思います。
【Rails】複数レコード一括保存のRspecの書き方

https://ryucoding.com/programming/rails-form-bulk-create
で紹介されている複数レコードを一括保存するcreateアクションをテストするリクエストspecの書き方で詰まったのでその備忘録です。


#テスト対象のコントローラーとアクション

```
 //questions_controller
def create
    @form = Form::QuestionCollection.new(question_collection_params)
    if @form.save
      redirect_to root_url
    else
      render 'new'
    end
  end
.
.
.
.
.

 def question_collection_params
    params.require(:form_question_collection).permit(questions_attributes: [:content, :mode_num])
 end

```
クイズ出題アプリで[question]という出題文を表すモデルを同時に10個作成するためのアクションです。

#rspec

```
 //questions_request_spec.rb

 describe "POST /create" do
      context "空欄なく入力した場合" do
        it "redirects to root path" do
          expect{
          post questions_path, params: {form_question_collection:
                                      {questions_attributes:
                                      　{0=> {content: "test question1", mode_num:1},
                                        1 =>{content: "test question2", mode_num:1},
                                        2 =>{content: "test question3", mode_num:1},
                                        3 =>{content: "test question4", mode_num:1},
                                        4 =>{content: "test question5", mode_num:1},
                                        5 =>{content: "test question6", mode_num:1},
                                        6 =>{content: "test question7", mode_num:1},
                                        7 =>{content: "test question8", mode_num:1},
                                        8 =>{content: "test question9", mode_num:1},
                                        9 =>{content: "test question10", mode_num:1}}}}
           }.to change(Question, :count).by (10)
          expect(response).to redirect_to root_path
        end
      end

```
[content]と[mode_num]はカラム名です。保存に成功し際はroot_pathにリダイレクトすることをテストするためのコードです。
上記の書き方で同時に10個のレコードをcreateするアクションのリクエストspecを実現できました。
メモアプリを作る

# GitLab
ソースコード（まだ途中）

https://gitlab.com/r1302/app-memo


# プロジェクト作成

すでにESLintを導入している下記を使う。

https://gitlab.com/r1302/template

# メモアプリ開発

## 開発準備

```
expo start
```
で開発スタート。

今回は、実機のiphoneでQRコードを読み込んで、Expoアプリで起動した開発中のアプリを見ながら開発していく。


## メモアプリのホーム画面を実装

https://qiita.com/Franly/items/72c030422a73f84d34ff


## コンポーネント化

https://qiita.com/Franly/items/f9f261fb8bee6cf1a38a

## propsを受け取るコンポーネント

https://qiita.com/Franly/items/afbdbeab093ed042f95d

## コンポーネントにpropsでstyleを渡す方法

https://qiita.com/Franly/items/cfae06f2c97fad7d78b7

## App.jsxをスッキリとさせる

https://qiita.com/Franly/items/daa20eda9f57d0942a5d

## アイコンの使い方

https://qiita.com/Franly/items/677601c815cab658f5ab

## 編集画面と新規作成画面の作成： 入力できるようにする

https://qiita.com/Franly/items/c430e8fdb3bb71d55829

## ボタンに動きをつける

https://qiita.com/Franly/items/92d21bef74fc14e1e702

## ナビゲーション
最新のバージョンでは動かなかったので、後で調査。

https://qiita.com/Franly/items/b9593aa1a03441dbada3

## React Hooksを使う
Hooksとは、関数型プログラミング、再レンダリングのタイミングなどReactの基礎。

https://qiita.com/Franly/items/465f4271609a681b809d

## firebaseでログイン機能を追加
firebaseにアカウント作成。

```
npm install firebase@8.1.2
```

を使用した。
最新のバージョンでは動かなかった。
後で調査。

## firebaseでデータベース
Amplify Authを完全に削除して初期状態に戻す


# Amplify Authの削除

AmplifyからAuthを削除するには、以下のコマンドを叩いて、pushします。

```bash
amplify auth remove
amplify push
```

しかし、GraphQL APIの認証にCognitoを使用していた場合、

```bash
✖ An error occurred when pushing the resources to the cloud
Template error: instance of Fn::GetAtt references undefined resource 〇〇
```

のようなエラーが発生してpushできません。GraphQL APIが削除した古いCognito User Poolを参照しているようなので、**amplify update api** で認証タイプを更新してあげる必要があるようです。

新しいUser Poolを作成している場合は認証タイプで**Amazon Cognito User Pool**を選択してあげれば良いですが、今回は一度 Amplify Authを完全に削除したいので、**API key**を選択

```bash
? Choose the default authorization type for the API
❯ API key
  Amazon Cognito User Pool
  IAM
  OpenID Connect
```

これで**amplify push**が成功し、無事Authのリソースが削除できました。
RouteObserverのdidPushは反応するのに、didPopが反応しない

# 現象

Flutterにて、画面遷移時のイベントをハンドリングして処理をしたくて、いろいろ検索しながらコード書いてみたけどうまくいかない。
didPush(画面が開いたとき)のみハンドリングできて、他のdidPop(画面閉じるとき)とかは無反応。。。

```dart:main.dart
  @override
  Widget build(BuildContext context) {
    return MaterialApp(

      // ~

      observers: ObserverStates.routeObserver,

      // ~

    )
  }
```

```dart:page.dart
class _MyPageState extends State<MyPage> with RouteAware {

  // ~

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    ObserverStates.routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
  }

  @override
  void didPopNext() {
    debugPrint("didPopNext ${runtimeType}");
  }

  // こいつだけ動く
  @override
  void didPush() {
    debugPrint("didPush ${runtimeType}");
  }

  @override
  void didPop() {
    debugPrint("didPop ${runtimeType}");
  }

  @override
  void didPushNext() {
    debugPrint("didPushNext ${runtimeType}");
  }

  @override
  void dispose() {
    ObserverStates.routeObserver.unsubscribe(this);
    super.dispose();
  }

  // ~
}
```


# 原因＆解決
私のアプリだと、MaterialAppの配下にNavigatorでタブ毎にrootを作成しているので、こっちに設定しなきゃいけなかったらしい。
要は、Navigator単位でObserverを用意してやる必要があるってこと。
(MaterialAppのObserverに設定しても、Navigator内の画面遷移は検知できんよってこと。だったらdidPushも反応しないでくれよ。。。)

```dart:tab.dart
    return Navigator(

      // ~

      observers: [ObserverStates.routeObserver],

      //~

    );
```
あの「free during beta」のWYSIWYG Markdownエディターがつい有料になった。。

> Typora is commercial software (not open source), but is free during beta.

Markdownエディターは数多く存在するが、`WYSIWYG(What You See Is What You Get)`エディターの中には一番使い易い（個人的に）。
長年以来、ずっと無料で使えるものでしたが、11月23日を迎えbeta版が終わって正式的にV1.0をリリースした。

[Typora V1.0 Release Note](https://support.typora.io/What's-New-1.0/)

https://typora.io
ホームページの一番下のダウンロードリンクの左に`購入`のボタンがある
![Typora price.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/667650/02e5e30f-4016-b2f8-ef04-ddd21c23ff02.png)
値段は14.99ドル（大体1700円あたり）、３つの設備で使えるがやっぱり高いの感じがするよね！

![may need purchase.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/667650/3982e15c-916a-5c17-8939-0f18adb52839.png)

ちなみに、この値段はサブスクリプションではないが大きなバージョンアップする際（1.0 -> 2.0）の時に、もう一度料金を支払う必要があるかもしれない

まあ、そもそもオープンソースのソフトウェアではない、Github上にはリポジトリが存在するがあれはただ`Issues`を提出用にすぎない

https://github.com/typora/typora-issues

もし、ずっとTyporaを使っていて、今後より良いものを期待するならぜひ購入して応援してください！
もし、お金を支払わたくないなら、選択肢は２つ：
1. 他のMarkdownエディタを使う: 例として`VSCode`+`Markdown All in One`+`Markdown Preview Enhanced`+`Markdown Preview Github Styling`の組み合わせ（VSCode以降はエクステンションの名前）
2. 最後のbeta版を使う:

https://typora.io/releases/all

![download link.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/667650/19407f93-014c-c167-a2c0-759ae39a0279.png)

問題としては、もう無料のアプデートはない。。
【Flutter】アプリリリースしてみて感じたアプリ作成時に絶対使いたい便利なパッケージまとめ

## はじめに
実際にflutterアプリを0から作成し、リリースまでをしてみて、便利だからどのflutterでアプリ作るときは必ず使おうと思ったものをメモする。

## パッケージリスト

- 認証まわり
    - `firebase_auth`
        - firebaseのauth認証をする
    - `google_sign_in`
        - googleアカウントでの連携認証を行う
    - `twitter_login`
        - twitterアカウトでの連携認証を行う
    - `sign_in_with_apple`
        - appleアカウントでの連携認証を行う
    - `flutter_signin_button`
        - 連携認証ボタンを簡単に作成できる
- バージョン管理
    - `package_info`
        - インストールされているアプリの情報を取得する
    - `pub_semver`
        - バージョン文字列同士(ex: `1.0.0`)の比較を行う
    - `firebase_remote_config`
        - firebaseのremote_configに登録した値を取得する
- リリース前の準備
    - `flutter_launcher_icons`
        - アプリアイコンの登録
    - `flutter_native_splash`
        - スプラッシュ画面の登録

## パッケージ詳細
### 認証まわりに関して

- `firebase_auth`
    - firebaseのauth認証をする
- `google_sign_in`
    - googleアカウントでの連携認証を行う
- `twitter_login`
    - twitterアカウトでの連携認証を行う
- `sign_in_with_apple`
    - appleアカウントでの連携認証を行う
- `flutter_signin_button`
    - 連携認証ボタンを簡単に作成できる

以下の記事で詳しく書いてます。

https://qiita.com/ninoko1995/items/9242005432808ae1a7f3

https://qiita.com/ninoko1995/items/51f9dd75b32522476342

https://qiita.com/ninoko1995/items/e1fc45deeff319a9e2ca

### バージョン管理(強制アップデート)に関して
RemoteConfigを用いる方法と、Firestore Databaseにバージョンを登録しておく方法が見つかった。
リリース履歴はAppStoreやgithub上のtagで管理すれば良いので、RemoteConfigを用いる方法を採用。

- `package_info`
    - インストールされているアプリの情報を取得する
- `pub_semver`
    - バージョン文字列同士(ex: `1.0.0`)の比較を行う
- `firebase_remote_config`
    - firebaseのremote_configに登録した値を取得する

以下の記事が参考になりました。

https://qiita.com/kasa_le/items/a7bae1b9aef6dfde2504

https://rnitta.hatenablog.com/entry/2019/09/26/171049

https://ichi.pro/apuri-no-koshin-furatta-70559107003876

### リリース前の準備に関して

- `flutter_launcher_icons`
    - アプリアイコンの登録
- `flutter_native_splash`
    - スプラッシュ画面の登録

上記パッケージを使ってサクッとアイコンやスプラッシュ画面を作成しましょう。

## まとめ
上記のパッケージ類は、どんなアプリを作る場合でも入れましょう！
【Rails】左外部結合と内部結合

##環境
Ruby 3.0.2
Rails 6.1.4.1

##前提条件
```ruby
class Owner < ActiveRecord::Base
   has_many :dogs
end

class Dog < ActiveRecord::Base
  belongs_to :owner
end
```

##左外部結合
左側のテーブルにしかないレコードも取得して結合する。
下の例でいうと、飼犬がいないオーナーのデータも取得できる。


```ruby
 Owner.left_joins(:dogs)
=> SELECT `owners`.* FROM `owners` LEFT OUTER JOIN `dogs` ON `dogs`.`owner_id` = `owners`.`id`
```


##内部結合
結合条件に一致するレコードのみを結合する。
下の例でいうと、飼犬がいるオーナーのデータのみ取得できる。

```ruby
 Owner.joins(:dogs)
=> SELECT `owners`.* FROM `owners` INNER JOIN `dogs` ON `dogs`.`owner_id` = `owners`.`id`
```


##参考

https://pikawaka.com/rails/left_joins
【Railsチュートリアル】ユーザー検索機能の実装

#はじめに

前回の記事[【Railsチュートリアル】いいね機能の実装](https://qiita.com/supyolo888/items/8c3beaf25811e0a98efb)に引き続きRailsチュートリアル第6版sample_appの機能拡張を進めていく。
今回は以下の様なユーザー検索機能を実装していく。
![スクリーンショット 2021-11-28 21.42.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2200191/e46fbcf8-1bb4-e26f-51d6-693bf2c71e12.png)

**前提**
第6版sample_appが完成している

#手順

Gemfileにransackを追加

```rb:Gemfile
gem 'ransack'
```

検索フォームを作成

```rb:app/views/users/_search_form.html.erb
<%= search_form_for @q do |f| %>
  <%= f.label :name_cont, 'User Search' %>
  <div class="input-group">
    <%= f.text_field :name_cont, placeholder: "Enter username...",
                      class: 'form-control' %>
    <span class="input-group-btn">
     <%= f.submit 'Go', class: "btn btn-primary" %>
    </span>
  </div>
<% end %>
```

ユーザーコントローラーを変更

```rb:app/controllers/users_controller.rb
class UsersController < ApplicationController
  .
  .
  .
  def index
    if params[:q] && params[:q].reject { |key, value| value.blank? }.present?
      @q = User.ransack(search_params, activated_true: true)
      @title = "Search Result"
    else
      @q = User.ransack(activated_true: true)
      @title = "All users"
    end
    @users = @q.result.paginate(page: params[:page])
  end
  .
  .
  .
  private
  .
  .
  .

    def search_params
      params.require(:q).permit(:name_cont)
    end

end
```

ビューを変更

```rb:app/views/users/index.html.erb
<% provide(:title, 'All users') %>
<h1>All users</h1>

<div class="row">
  <div class="search_form">
    <%= render 'users/search_form' %>
  </div>
</div>

<%= will_paginate %>

<% unless @users.empty? %>
  <ul class="users">
    <%= render @users %>
  </ul>
<% else %>
  <p class="search_message">
    Couldn't find any user.
  </p>
<% end %>

<%= will_paginate %>
```

cssを追加

```rb:app/assets/stylesheets/custom.scss
/* search_form */

.search_form {
  overflow: auto;
  margin: 10px;
  float: left;
  .input-group {
    width: 300px;
    .form-control {
      width: 250px;
    }
    .btn {
      width: 50px;
    }
  }
}
.search_message {
  margin-top: 30px;
  font-weight: bold;
  font-size: 1.3em;
}
```
CDNを何も知らない人に朗報です（前編）

#この記事何の記事？

この記事は、CDN(Content Delivery Network)について何も知らない方が、これを読んで「CDNってこんな感じなんだな」と思ってもらえる記事です。
すでに知っている方は対象に含まれません。。

また、後編は[CDNを何も知らない人に朗報です（後編）](https://qiita.com/n-yama/items/bad85dba4a6b3b458e1d)になります。
併せて読んでいただけますと幸いです。

#CDNとは
ざっくりですが、CDNとは、大容量のウェブコンテンツでも配信に問題が起きないようにするための最適化したネットワークのことです。

…といった感じのことを書きましたが、こんな概要だけではよくわからないと思うので、説明していきたいと思います（説明については前編・後編の記事に分ける予定です）。

#CDNの仕組み
ウェブコンテンツを利用する際、サーバーにアクセスしてコンテンツをダウンロードします。
この時アクセスするサーバーは**「オリジンサーバー」**と呼ばれるサーバーがあり、オリジンサーバーにはオリジナルのウェブコンテンツが存在します。
CDNを利用しない場合で、仮にあるウェブコンテンツに大量のアクセスがあった際、オリジンサーバーに負荷がかかってしまいます。
そうなったとき、ページの表示速度が遅くなったり、画面が表示されなくなったりなどの問題が生じます。

![CDN-CDNの仕組み1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1237088/91a1c6cd-325e-93d4-149e-928b18f23eb6.png)




CDNではこのような問題が起きないよう、**「キャッシュサーバー」**と呼ばれるオリジンサーバーからウェブコンテンツをコピーしたサーバーに代理でウェブコンテンツを配信してもらいます。こうすることでオリジンサーバーの負荷が減り、早くて効率的なコンテンツ配信が可能となります。

![CDN-CDNの仕組み2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1237088/16931e84-6745-1c2c-aec0-c4a882eefc2f.png)
 

また、キャッシュサーバーへのアクセスはネットワーク的に距離が近いところになるため、さらに高速化が期待できます。
例えば、日本からアメリカの企業のウェブサイトにアクセスしてファイルをダウンロードするとします。
その場合、日本から遠いサーバーにアクセスしてダウンロードするため遅くなります。

![CDN-距離が近い1.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1237088/ceb7a74c-19c9-3f96-9be8-814e0bf6adad.png)



CDNを利用するとキャッシュサーバーは世界中に配置しており、アメリカからではなく日本に設置されたキャッシュサーバーからダウンロードすることができ、高速にダウンロードが可能となります。


![CDN-距離が近い2.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1237088/7f7512c1-13e0-d6ca-e7d8-82e6a364f354.png)



#メリット
CDNの仕組みについて述べてきたことも含めて、メリットについて列挙します。

##サーバーアクセスを分散して負荷を軽減できる
オリジンサーバーからウェブコンテンツをコピーしたキャッシュサーバーのおかげでオリジンサーバーへのアクセスを代わりに受け付けて負荷を分散します。その結果、オリジンサーバーへの負荷が軽減されます。

##アクセスに対してのレスポンスの遅延を防ぐ
アクセスが分散されることで、アクセスが集中してコンテンツのダウンロード時間が長くなるなどの問題が生じないようにすることができます。





#デメリット
##古いコンテンツが配信される可能性がある
オリジンサーバーには最新のファイルが配置されていたとしても、キャッシュサーバーは古いファイルを保持したままの時があります。
これはオリジンサーバーが最新のファイルを配置しても、キャッシュサーバーはオリジンサーバーに新しいファイルを取得しにいかなければ、キャッシュサーバー自身の保持しているデータは更新されないため起こります。
キャッシュ時間（オリジンサーバーのファイルを再取得しにいく間隔）が長すぎると起こりやすくなるため、その時はキャッシュ時間の設定の見直しなど行う必要があります。

##キャッシュ事故が起きる可能性がある
個人情報が掲載されたページをキャッシュしてしまうと、他の人に個人情報が漏洩してしまう危険性があります。
このような事故を防ぐために、「キャッシュファイルの選定」が大切になってきます（どのファイルをキャッシュして、どのファイルはキャッシュしないなどの設定をすること）。

#終わりに
ウェブサイトが重かったり、表示速度が遅いなどの問題があるとユーザーが離脱しやすくなりますし、そうなってくるとgoogleから「ユーザーにとって使いにくいサイト」認定され検索順位も下がりいいことがないです。
当記事を読んで、ユーザーが快適にサイトを使うことができる手段のひとつとしてCDNという方法があるということを知っていただけたら何よりです。

（後編は[CDNを何も知らない人に朗報です（後編）](https://qiita.com/n-yama/items/bad85dba4a6b3b458e1d)になりますのでこちらも読んでみてください。）
NavigationBarの色が変わらない!

# UINavigationBarが黒や透明になる問題の修正
エミレーターのtitleが表示されるNavigationBarの色がYouTubeの動画通りにやっても色が変わらないのに気づいた!

**参考にした動画**
https://www.youtube.com/watch?v=3v4OIds-OOg

**解決方法はこの記事を参考にしました**
https://kikuragechan.com/swift/ios15-navigationbar-bug

先ほどは透明でしたが、色が黄色になりました。Xcodeのバージョンが上がっていくと１年前の動画通りにならないので、悩まされました...

![スクリーンショット 2021-11-28 21.33.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303722/c5cc5d96-f760-fed3-69ed-593ed3bfcafe.png)

# 追加情報
**x-code: 13.2.1の場合2箇所設定するだけでよかった!**

1. Scroll Edge AppearnaceのBackgroundで色を変更する
2. Scroll Edge Title AttrbutesのTitleをCustomに変更して、Title Colorで色を変更する。

![スクリーンショット 2022-02-20 9.59.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303722/c1b24fe3-f9a9-670c-67ed-b1916776e6c3.png)

![スクリーンショット 2022-02-20 9.59.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303722/b55894cf-49d4-dfe0-f551-2bbecf25f4e0.png)

![スクリーンショット 2022-02-20 10.00.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303722/8e6333be-fc5a-eb2c-ef71-f8275bd15414.png)

![スクリーンショット 2022-02-20 10.01.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2303722/39d1dc74-b19b-05a3-259a-4701cea91f6e.png)
Flutter Firebaseを開発・本番環境を分ける時にiOSでハマったこと

## 問題

FlutterでFirebaseをbackendとして使っている。
開発・本番環境を分けるべく、[この記事](https://zenn.dev/nrikiji/articles/f54c428f881997#5.-github-actions%E3%81%A7%E3%82%B9%E3%83%88%E3%82%A2%E3%81%B8%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89)を参考にしていた。
が、Xcodeの`TARGETS > Runner > Build PhasesのRun Scriptを設定してGoogleService-Info.plistを開発、本番ビルドで切り替える`のところで、記事のままやると失敗してしまい、以下のエラーが出てしまう。

```shell-session
<unknown>:0: error: failed to emit precompiled header '/Users/aldin/Library/Developer/Xcode/DerivedData/Runner-bjnbqukpbiokvkchuytagvputftk/Build/Intermediates.noindex/PrecompiledHeaders/Runner-Bridging-Header-swift_3T9V4UQ7DGNZS-clang_313XNT4ZQGK7Y.pch' for bridging header '/Users/aldin/Documents/skydive/room8/flutter/room8/ios/Runner/Runner-Bridging-Header.h'
    /Users/aldin/Documents/skydive/room8/flutter/room8/ios/Runner/Runner-Bridging-Header.h:1:9: note: in file included from /Users/aldin/Documents/skydive/room8/flutter/room8/ios/Runner/Runner-Bridging-Header.h:1:
    #import "GeneratedPluginRegistrant.h"
            ^
    2 errors generated.
    note: Using new build system
    note: Planning
    note: Build preparation complete
    note: Building targets in parallel
Could not build the application for the simulator.
Error launching application on iPhone 13.
Exited (sigterm)
```

## 解決策

`Run Script`の元の内容を保留して、追記する。コードは以下のようになる。

```shell
if [ "${CONFIGURATION}" == "Debug" ]; then
cp -r "${PROJECT_DIR}/Runner/GoogleService-Info-dev.plist" "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app/GoogleService-Info.plist"
elif [ "${CONFIGURATION}" == "Release" ]; then
cp -r "${PROJECT_DIR}/Runner/GoogleService-Info-prod.plist" "$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app/GoogleService-Info.plist"
fi

/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332865/bcbf2dfa-6c10-d62c-9246-05ad8efe1a03.png)
Ubuntu/Anaconda環境でGDALとopencvを使おうとしたときにハマったことメモ

## 結論
- jupyterが参照しているパスには要注意
- Python 3.8 でGDAL,OpenCV両方インストール可能

## 前提
- Ubuntu 20.04.3LTS
- Anaconda3
- リモートサーバーにアクセスするため、PuTYYのコマンドラインを使用


## 起きた事象
```python:
conda create -n '環境名' anaconda
```
でAnaconda上に仮想環境を作りそこにGDALとOpenCVをインストールするため、下記コマンドを入力。

```python:
conda install -c conda-forge opencv
conda install -c conda-forge gdal
```

するとconflictエラーで二つのライブラリがインストールできない。

## やったこと
### pythonのバージョンを確認
下記で確認できます。

```
conda info
```

確認したところpython3.9でした。 
下記サイトを見ているとpython3.9版のGDALは用意されているのでインストールできるはず。
<https://www.lfd.uci.edu/~gohlke/pythonlibs/#gdal>

### gdalからinstall
仮想環境をpython3.8で作り直しgdalからinstallしたところ問題なくインストール完了できました。

(時系列は前後しますが、pythonの他のバージョンでもGDAL,OpneCVのインストールを試みましたがconflictしなかったのは3.8のみでした。)


下記でjupyterをブラウザで開いて動作確認を行いました。

```
(仮想環境内で) jupyter notebook
```

動作確認はnotebook上で

```
from osgeo import gdal
```
で可能です。

何もエラーが出なければ問題なくインストールされています。
しかし　No module…　のエラーを確認。。。

念のため下記でGDALが入っているかを別方法で確認しました。

```:@notebook
conda list gdal
```

どうやら入っていなさそう。
ではさっきinstallできたのは何なのか。。。

## 問題はjupyterが参照するパス！
下記サイトを参考にjupyterのパスを確認してみました。
<https://qiita.com/kashi1mochi/items/d640098104b643abd466#%E6%A6%82%E8%A6%81>

```:@notebook
!which jupyter
```
jupyter パスは/home/ubuntu/jupyterとなっていて、そもそも仮想環境内でjupyterが使えていないことに気づきました。

たしかに仮想環境を作った後にはjupyterを入れていない、、、


コマンドラインでgdalの場所を参照してみるとjupyterの参照先と全然違うところに入っていました。そら動かんわ。

````:@PuTTY
gdal list
# packages in environment at /home/ubuntu/.conda/envs/(環境名):
#
# Name                    Version                   Build  Channel
gdal                      3.3.0            py38h507a4fd_1    conda-forge
libgdal                   3.3.0                h679344c_1    conda-forge

```


そこでjupyterを仮想環境にインストールしところjupyter notebook上でGDALをimportできました。

正しいjupyterの位置はここでした。

```
/home/ubuntu/.conda/envs/(環境名)/bin/jupyter
```

### 参考
Anacondaで仮想環境を作るときにpythonのバージョンを指定する方法(v3.8の場合)

```:@PuTTY
conda create -n '環境名' python=3.8 anaconda
```
【Laravel】ログインしているユーザーを検索画面に表示させなくしたい

#なにをしているか
Laravel6.xでツイッタークローンサイト作成中。備忘録。
#なにをしたいか
トップ画面から、サイトに登録しているユーザーの一覧を表示させたい。
ユーザーの検索機能を実装したが、ログインしているユーザー(自分)を表示させないようにしたい。
Controllerで検索機能実装済み。
#コード
```php:search.blade.php
@foreach($users as $user)
@if ($user->id !== Auth::user()->id)
<div class="userSearchList">
  <figure><img src="images/{{ $user->images }}"></figure>
  <p>{{ $user->username }}</p>
</div>
<div class="userSearchButton">
  @if (auth()->user()->isFollowing($user->id))
  <form action="/users/{{ $user->id }}/unfollow" method="post">
    @csrf
    <input type="submit" name="button" class="followButton" value="フォロー解除">
  </form>
  @else
  <form action="/users/{{ $user->id }}/follow" method="post">
    @csrf
    <input type="submit" name="follow" class="followButton" value="フォローする">
  </form>
  @endif
</div>
@endif
@endforeach
```
検索画面でユーザーのプロフィール画像・フォローボタンを設置しています(フォローしていなければフォローボタン、フォローしていればフォロー解除ボタンが表示される)

```php:search.blade.php
@if ($user->id !== Auth::user()->id)
```
この部分でテーブルに登録されているユーザーとログインしているユーザーが不一致のユーザー(要するに自分以外)の表示が実装できました！
意外とシンプルに実装できました。
SWIGで C++ の opencv コードのPythonラッパーを作成

#SWIGインストール

http://rinatz.github.io/swigdoc/install.html

Archlinuxの場合

````bash
sudo pacman -S swig
````

#ビルド

[参考 swig-and-c-shared-library](https://stackoverflow.com/questions/10126531/swig-and-c-shared-library)

###ラッパーファイル生成
OpenCVをインクルードしてる`mylib.h`に対して`mylib.i`を用意
`<string>`など標準ライブラリをインクルードしてるならその対応の.iも入れておく ([string-arguments-are-not-recognized-by-swig](https://stackoverflow.com/questions/12236150/string-arguments-are-not-recognized-by-swig))

````
%module mylib

// Make mylib_wrap.cxx include this header:
%{
#include "mylib.h"
%}

%include <std_string.i>
%include "mylib.h"
````

.iをコンパイルして

````bash
swig -c++ -python mylib.i
````

`mylib_wrap.cxx`,`mylib.py`が生成される

###コンパイルとリンク
生成された.cxxファイルをコンパイルして Python と Opencv をリンクする

`mylib.py`は`import _mylib`でネイティブを呼び出すのでオブジェクトファイルの名前は `_mylib.so`　にする

````bash
g++ -shared -fPIC -L. mylib_wrap.cxx  -o _mylib.so  `pkg-config --libs --cflags python3` `pkg-config --libs --cflags opencv4`
````

試しにpythonで呼び出す

````python
import mylib
a=mylib.SomeClass()
````
Firestoreのコレクション内全てのドキュメントから特定のフィールドを削除する方法

# 結論から申し上げると
* 2021年11月現在、Firestoreのコレクション内全てのドキュメントから特定のフィールドを削除する機能はない
  - [How to delete fields for all documents in Firestore (自分が投稿した質問です)](https://stackoverflow.com/questions/68480507/how-to-delete-fields-for-all-documents-in-firestore)
  - Firebaseのサポートに問い合わせても、「SDKを使ってfor文で実装しろ」との回答を貰う
* 以下のように自前で実装する必要がある

# 実装例
* usersコレクションの全ドキュメントからnameフィールドを削除する例(Javascript)
* Firebaseへ接続するためのセットアップは省略

```javascript
const firebase = require("firebase/app");
require("firebase/firestore");

const targetFieldName = 'name';
const delField = {[targetFieldName] : firebase.firestore.FieldValue.delete()};

(async () => {  
  const colRef = firebase.firestore().collection('users');
  colSnapshot.forEach((doc) => {
    colRef.doc(doc.id).update(delField);
  });
})();
```

# 補足
* 実行時間は15,000件で10秒かからない程度だった。非常に早い。
* 高速で実行されるが故、途中で止めることができないので、[readline-sync](https://www.npmjs.com/package/readline-sync)などを使ってコンソール上で実行前の最終確認を入れておくとチームに喜ばれるだろう
* Firebaseコンソール上で該当のコレクションを開いておくと、削除の過程を視認できるので、作業中は開いておくことをお勧めする

# 参考資料
* [Cloud Firestore からデータを削除する - フィールドを削除する](https://firebase.google.com/docs/firestore/manage-data/delete-data?hl=ja#fields)
Node-RED で LINE Notify

以前、[Amazon SES で受信したメールをLINEに転送する](https://qiita.com/Kakimoty_Field/items/4485a4e76d677d85ade0) という記事を書きましたが、通知を受け取るには「お友達登録」する必要がありました。今回は [LINE Notify] を使ってもっと簡単にLINEで通知を受け取れる手順をまとめておきます。

# LINE Notify
## トークン準備
[LINE Notyfy](https://notify-bot.line.me/ja/) のページでログインします。

あらかじめ LINEアプリで「ほかの端末と連携」→「ログイン許可」をしておく必要があると思います。
![Screenshot_20211128-180026_LINE.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/e6b14224-1788-480e-a7fc-62e2c08056d9.png)

LINE notify にログイン出来たら画面右上の自分の名前をクリックし「マイページ」をクリックします
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/36a6605a-3e8a-b94f-1607-15293f0c9ddf.png)

マイページが表示されたら `トークンを発行する` ボタンをクリックします
![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/f72e700f-e763-61f9-a4ec-c38dbdfdf030.png)

**トークン名**を入力と**自分が参加しているトークルーム一覧**がダイアログに表示されます。**トークン名**には任意の名前を入力し、**トークルーム**は`1"1で LINE Notify から通知を受け取る` を選択します。選択が終ったら「発行する」ボタンをクリックします。
![005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/2bad11d2-300f-60a1-188f-9c30efb69aeb.png)

プログラムで利用するトークンが表示されるので、どこかにメモしておきます。

![006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/075c3ec7-6c80-7b6d-9cc0-d22f4667bb29.png)

# Node-RED 
## フロー作成
メッセージ送信検証用のフローは以下になります。

![010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/f163cb55-66ad-26ae-7469-c6a404b8f2ab.png)

### http request ノード設定
**http request** ノードは`メソッド`を 「-msg.methodに定義 -」に変更しておきます。
![011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/75d02252-41a4-6e6b-31e9-4a92b518c5cf.png)


### Function ノード
Function ノードのソースコードは以下の通りです。**ご自身で発行したトークン**には、適切なトークンを設定してください。

```javascript:function.js
var token = "ご自身で発行したトークン";

msg.headers = {
    "Content-Type" : "application/x-www-form-urlencoded",
    "Authorization" : `Bearer ${token}`
};
msg.method = "POST";
msg.url = "https://notify-api.line.me/api/notify";

msg.payload = {
    message: "テスト送信"
}
return msg;
```

ソースコードを記入できたら「デプロイ」ボタンをクリックします。

# 動作テスト
**Inject** ノードをクリックしてフローを実行します。フローが正しく設定できた場合は、LINE アプリに通知が届きます。

![Screenshot_20211128-203944_LINE.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311258/e8c45a1f-7d7b-de7f-e4b6-0c5da0654b9f.png)

# 終わりに
メッセージの通知だけを行うのであれば LINE Notify はとても簡単に実装することができました。IoTデバイスの操作やセンサー情報の通知などもラクラク実装できるので、Node-RED との組み合わせで夢が広がります。

これからも良き Node-RED ライフをっ！
ラズパイで子供の寝相監視をした時のログと、参考にした記事のまとめ【前編】

# はじめに

もうすぐ2歳になる子供が家に滞在していたので、せっかくなので寝相をとって動画にして、みんなで楽しむことにした。
そのときに行ったことのまとめ。

# 使用機器

|項目|内容|
|:--|:--|
|ラズパイ|Rapsberry Pi4 4GBモデル|
|カメラ|[よくある暗視カメラ](https://www.amazon.co.jp/gp/product/B0768Z87JF/)|
|SDカード容量|32GB|


## 初期設定

ラズパイを買ったのは相当昔で、初期設定自体は何を参考にしていたかは忘れてしまった。

特筆するようなことはしていないので、[このページのような記事](https://raspida.com/rpi-setup2021)を参考にしておけば良さそうである。

# 方針

今回は次のような方針で動画を作成することにした。

1. Pythonでカメラを制御し、一定の間隔で画像を撮影するコードを作る
1. 遠隔制御し、1のコードを起動する
1. 撮影した動画をメインの環境(Windows)にコピーする
1. メイン環境にて動画化
1. みんなで楽しむ

記事が長くなりそうだったので、2までで区切ることにする

# カメラ制御方法

Pythonでカメラを制御する時のサンプルは公式が一番わかり易いと思う。

https://projects.raspberrypi.org/ja-JP/projects/getting-started-with-picamera/0

[Pythonコードでカメラモジュールをコントロールする方法](https://projects.raspberrypi.org/ja-JP/projects/getting-started-with-picamera/4)を参考にして、まずはカメラの動作確認をしたところ、かなりぼやけた内容の画像が取れていた。

今回のカメラモジュールの場合、レンズの部分がネジ式になっていて、手動で焦点距離を変えられるようだった。
ピントが合う部分を把握してから、レンズの部分を回転させてピントの調整を行おう。

ピント調整なども含めたコードは下記のとおりになった。

```python:camera.py

from picamera import PiCamera
from time import sleep
import datetime

DEBUG = False

TIMESPAN = 2

WAIT = TIMESPAN / 5

with PiCamera() as camera:
    if DEBUG:
        camera.start_preview()
        _ = input()
        camera.stop_preview()
    
    check_time = datetime.datetime.now()
    
    count = 0
    
    while check_time.hour >= 20 or check_time.hour <= 6:
        while datetime.datetime.now().second != (check_time.second + TIMESPAN) % 60:
            sleep(WAIT)
        check_time = datetime.datetime.now()
        camera.capture(f"{check_time:%d%H%M%S}.jpg")
        count += 1
        print(count)
        if DEBUG and count > 100:
            break

```

保存ファイル名を日付を含めている（check_time:%d%H%M%Sとしている）のは、
時間までだとファイル名をソートしたときに24時のファイルが一番最初に来てしまうから。
月をまたぐことはなかったのと、時間を簡単に確認できるようにしておく方が
動画を見ながら話すときにやりやすいとおもってUNIX時間の採用はしなかった。

理論上、一度の撮影に1秒以上かかると1分待たされる可能性が出てくるが、事前検証(GUI上で一晩動かして見てのテスト)では問題なかったためこのまま使用した。
なお、事前検証では撮影間隔を1秒にしていたが、32GBのSDでは容量が足りなかった。

これの対策について色々検証したが最終的に撮影間隔を2秒にする対応に落ち着いた。
検証記録は別途記事にまとめることにする。

# 遠隔操作によるコード起動

遠隔操作によるコードの起動方法はいくつかあるが、TeratermでSSHでラズパイに接続するのが一番手軽そうだった。
とりあえず[Teratermを落としても実行し続ける方法](https://blog.katsubemakito.net/linux/nohup)を確認してから、ソースコードのあるフォルダに移動して、```sudo nohup python camera.py```をすると…何が起きたかはよくわからないが一瞬で終了した。

(※後で知ったが、最後に&をつけるとバックグラウンドで実行できるようだった。
今回はコードを実行することだけが目的だったことと異常終了の検知ができたことからフォアグラウンド実行でも問題は出なかった。)

```cat nohup.log```すると、次のような表記。

```
  File "camera.py", line 25
    camera.capture(f"{check_time:%d%H%M%S}.jpg")
                                              ^
SyntaxError: invalid syntax
```

IDEで作業していたPythonでは対応していた新しい記法が使えないPythonを使おうとしていたようだ。
試しに```python```してみると…？

```
Python 2.7.16 (default, Oct 10 2019, 22:02:15)
[GCC 8.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

今後pythonを使う場合はPython3系しか使わないと思うので、[対応方法の記事](https://www.ingenious.jp/articles/howto/raspberry-pi-howto/python-3-change/)を参考にして、pythonをpython3に紐づけておいた。

もう一度実行して翌朝、```ls | wc -l```でファイル数を調べることによって、期待通りのファイルが出力されていることを確認した。

記事が長くなりそうなため、一旦ここで区切ることにする。
エンジニアコミュニティのやり方&よくある失敗例

## 結論

**リモート増えてもコミュニティやろうよって話**

- 信頼を置ける関係を作ろう
- そのためにコミュニティ化してコンタクト頻度を上げよう

---

### ターゲット

- リモートワークでコミュニケーションが減っている人
- みんなでワイワイしながら目標持って行動したい人
- 駆け出しエンジニア

---

### 筆者紹介

**フリーエンジニア＆個人開発の人**
これまでリーダーなんてやってこなかったし、
コミュニティに属すようなタイプではなかった
高校では帰宅部。勉強ぐらいしかしてなくて我が道を進んでいた

---

### 大学時代

医学科博士課程のとっても優秀な先輩がいた
一人でもなんでもできそうなのにサークルを運営をしてた
みんなでラジオ番組を作ったり、英語を勉強したり、海外に行きサークルの海外支部を作ったりなんかもした

---

### 早く行きたければ一人で行け、遠くまで行きたければ、みんなで行け。

---

### コミュニティってすごい

```
みんなで目的も持って進むことで
一人では成し遂げられないようなところまで行くことができる
```
を身を持って感じた

---

### 大学時代

- 英語🇺🇸アカペラ🎤バスケ🏀
    - サークルは最大５つ入ってた
    - 国際交流サークルも作った
- NPO団体、就活支援団体いろんなコミュニティに入った

---

### 就職してから

- LT会を主催
- [アプリ](http://ideee.tech/)を一緒に作るコミュニティを主催
- 仲良しエンジニアコミュニティを運営
- 福岡県人会コミュニティなんかも作った🍜

---

### [とにほめLT会](https://tonihome.connpass.com/)

![とにほめLT.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498701/b9a3fc4f-1ba5-f19b-9fda-0ed9e3fad2dd.jpeg)

---

- 半年以上続けているLT会
- 累計100人以上が参加し繋がり、成長をしてきた

---

### 個人アプリ[ideee](http://ideee.tech/)をチーム開発

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498701/bd83f87a-657a-adea-6386-06d1c554d4db.png" width="600px">

---

- **アイデアとエンジニアのマッチングアプリ**
- 制作人数８名でワイワイ
    - [チーム開発のメリット](https://qiita.com/naruqiita/items/dbabf6e38bc7792097f4)
- 制作から３ヶ月でアイデアを100以上、ユーザーも３００以上集めた

---

### 駆け出しの頃からエンジニアコミュニティを運営

- **クローズド化：**エンジニアになる前に一緒に勉強し、転職活動をしていたメンバーのコミュニティ
- **情報共有：**交流会をしたり、もくもく会をしてみんなエンジニアになった
- **成長：**今では会社のエンジニアNo.2や、フリーランスに

---

#### エンジニア自体がそもそもニッチ

さらに細分化した目的で
コミュニティを築くと希少性が高く、強い！

---

### 定期的な繋がりが信頼に

- Slackでちょっとクローズドなコミュニティ
- 定期的にイベントを開いていたからお互いによく知り合う
- 現在やってることや能力が伝わるから仕事も一緒にできた

---

## よくあるコミュニティ失敗理由

- 人の出入りが激しくなってしまう
- 当初の目的が時間と共に希薄化する

---

#### どうしたらいいの？🤔

---

### 目標を持ってさらに関係強化🚀

- 少数精鋭で初めて少しずつしか増やさない
- みんな求めていることを確認し目標を掲げる
- 常に目的を確認する

---

### エンジニアコミュニティの事例

かつてはエンジニアになることが目的
メンバーもどんどん増えた
↓
みんなエンジニアになってしまったら希薄化した🤢
↓
現在は少数メンバーでの**法人化を目標**にさらに関係強化！ 

---

### コミュニティと数

```
3~4人： それぞれの詳細な情報がお互いわかる
5~8人： お互いの名前やステータスがわかる
9~13人： 顔と名前が一致できる
14人〜25人： 同じコミュニティメンバーであることがわかる
```

---

### 数はコミュニティの目的次第

クローズドが良いか情報公開型で目標を掲げていくのが良いかはそれぞれ

コミュニティ運営側は
**人数や参加頻度に目標を持つべき**

---

### コミュニティの役割

```
コミュニティに入っている人が各々の目標に向かって頑張れる居場所になること
一人ではいけないところまでみんなで行くこと
```

---

## 結論

- コミュニティの運営方法は目的によってさまざま
- 信頼のおけるチームで目標を作って遠くまで行こう！

---

## 自己紹介

個人開発について発信しています！
Twitterもフォローください👏
[@1026NT](https://twitter.com/1026NT)
なる

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498701/3db40e7d-3213-be1f-8650-c6ad5dff69c9.jpeg" width="300px">
toioDoをRaspberry Piで動かす(pyscrlinkを動かせばOK)

#はじめに
　toio core cubeをビジュアルプログラミングで操作できる[toioDo](https://toio.io/do/)ですが、(2021年11月末時点で)対応環境が、Windows10、MacOS10.13、iPadOS13、Chromebookに限られています。BLEを使う都合上、PC(Windows/Mac)ではscratch-linkが必要だからという事情だとは思いますが、Raspberry PiとかJetsonとかのLinuxが動く環境でもtoioDoを使いたいですよねぇ。
　と思っていたら、なんとpythonで書いたscratch-link相当品があるとのこと。すばらしい。

https://github.com/kawasaki/pyscrlink

　試しにRaspberry Pi OSで動かしてみたらちゃんと動いたので、インストール手順などを書いてみようと思います。


#インストール手順
　基本、上記のGitHubにあるREADME.mdどおりに行えば大丈夫です。(動作した、しなかったは記事の最後の方にまとめました。)

```bash
sudo apt update
sudo apt upgrade
sudo apt install bluez libbluetooth-dev libnss3-tools libcap2-bin libglib2.0-dev
pip3 install pyscrlink
```

#動かし方

まずscratch_linkを動かしておきます。

```bash
cd ~/.local/bin   (pip3でのインストール方法によってはパスが違うのでscratch_linkがインストールされた場所にパスを通してください)
./bluepy_helper_cap  (これは最初の１回だけでいいようです)
./scratch_link -s 1　(これはtoioDoを使う前に起動しておく必要があります)
```

scratch_linkが起動した状態で、chromiumブラウザ、またはfirefoxブラウザでtoioDoのページを開くことでWindows10/MacOSと同様にtoioDoで２つのtoio core cubeをコントロールできます。

右上のtoio core cubeマークに緑色のチェックマークが入り、接続できていることがわかります。
![toiodo_raspios1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/435ff31a-677a-b43d-cd54-036813aa9ad8.png)

上図のScratchプログラム(といってもブロックを一つづつだけですが)を動かした状態。片方はLED点灯、片方は回転。
![toiodo_pyscrlink3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/5a49e35f-f0eb-1e5d-6e79-b13522b2d30d.jpeg)

toioDo公式サイトのサンプルも動作します。
![toiodo_raspios2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431649/fb0d7f8d-ea2e-bf75-fdc2-a09db78d46fb.png)

うまく行かない場合はscratch_linkを再起動してやりなおしてみてください。


#動作確認状況

　いまのところRaspberry Pi 4B/400では Raspberry Pi OS 64bit版を使うのが良いようです。(32bit版でも動きますが、全体的に動作が遅いのでおすすめしません。)

### 動いた

- Raspberry Pi 400 4GB Raspbery Pi OS 64bit版(2021-11-03)
- Raspberry Pi 400 4GB Raspbery Pi OS 32bit版(2021-11-03)
- Raspberry Pi 4B 4GB Raspbery Pi OS 64bit版(2021-11-03)
- Raspberry Pi 4B 4GB Raspbery Pi OS 32bit版(2021-11-03)
- PC(Ryzen5-2600) + USB-Bluetoothドングル(CSR8510 A10) Ubuntu 18.04 x86_64

### 動かなかった
- Raspberry Pi 400 4GB Ubuntu20.04
    - toioDoの画面からtoio core cubeに接続できず。(Chrome NSS DB not found. Do not add certificate.)
- Raspberry Pi 4B  4GB Ubuntu20.04
    - toioDoの画面からtoio core cubeに接続できず。(Chrome NSS DB not found. Do not add certificate.)
- Jetson Xavier NX開発者キット Jetpack 4.6
    - pip3 install pyscrlinkのときにPyBluez-0.23のインストールでerror in Pybluez setup command: use_2to3 is invalid.

# なんということでしょう

　Advent calender用の本記事を書いたのですがなんと、

[「toio」の無料プログラミングサービス「toio Do（トイオ・ドゥ）」バージョン1.3　
2021年12月7日（火）より順次アップデート予定](https://toio.io/news/2021/holidaysale21.html)

> またWeb BluetoothによりScratch　Linkなど追加インストールが不要に。

　scratch_linkなしでも動くようになるそうです。(^^;
　まあ、リリースされたら動作確認してみます。
sklearn1.0以降で混同行列を作成する

## 背景
これまでsklearn 0.20等で混同行列を作成する場合には、confusion_matrix関数を使用していました。
sklearn 1.0では新たに追加されたplot_confusion_matrix関数がexampleに追加されています。

ただし、plot_confusion_matrixは引数にestimator, X_test, y_testを引数に取ります。
既に予測済みの結果がある場合には、少し無駄な処理に見えます。
また、[Deprecatedに関するアナウンス](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.plot_confusion_matrix.html)も出ていることもあるため、あまり好んで使いたくありません。

## 予測済み結果から混同行列を作成する
`ConfusionMatrixDisplay.from_predictions` で作成できます。

```python
import matplotlib.pyplot as plt
from sklearn.metrics import ConfusionMatrixDisplay

ConfusionMatrixDisplay.from_predictions(y_test, pred)
plt.show()
```

ただし、以下のように画像が小さくなってしまうことがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121870/8106b7c9-876c-288f-f187-32077d1d5792.png)

### 混同行列のサイズを変更する
`plt.subplots`を使うことで変更できます。

```python
import matplotlib.pyplot as plt
from sklearn.metrics import ConfusionMatrixDisplay

cmp = ConfusionMatrixDisplay.from_predictions(y_test, pred)
fig, ax = plt.subplots(figsize=(10,10))
cmp.plot(ax=ax)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121870/50296e36-126b-6187-6d7c-3994d45ee968.png)




## 従来通り配列作成のみsklearnを使う
confusion_matrix関数が使えます。
https://scikit-learn.org/stable/modules/generated/sklearn.metrics.confusion_matrix.html#sklearn.metrics.confusion_matrix

## 参考リンク
- https://stackoverflow.com/questions/66483409/adjust-size-of-confusionmatrixdisplay-scikitlearn
ヤマトハッカソンで優勝しました！（2021年11月27〜28日）

# ヤマトハッカソンで優勝しました！

---

![スライド1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/cba75e7b-0c37-23f0-0b8a-44361b796a41.png)

---

![スライド2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/e17e0705-009b-f9b3-1d39-3e84bd8504f7.png)

---

![スライド3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/e2af219b-5035-5439-1bac-9acb4a00bd59.png)

---

![スライド4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/c65a6164-016e-7094-293e-ec4adbbc6f1a.png)

---

![スライド5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/1d9a2143-7ab6-2528-2236-544fa2426ac9.png)

---

![スライド6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/535f8357-a285-4d42-8e8d-a4db4e5ba9b7.png)

---

![スライド7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/345bcf05-3509-6d59-f6cb-697444c8ddd5.png)

---

![スライド8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/97af47f8-e731-22d4-e471-facf2bd8e0f0.png)

---

![スライド9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/61a26d84-3465-1f16-536f-73a4a46c5eaa.png)

---

![スライド10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/9ef5b7a7-8613-228e-31f1-eec22b0287c2.png)

---

![スライド11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/59ce3638-20c8-a7cb-f591-801c9b5e442c.png)

---

![スライド12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/0f6be917-24a5-f69c-9535-7495b30cb863.png)

---

![スライド13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/be4bbf79-4d52-a4bd-8dbe-bb1242eaf0c8.png)

---

![スライド14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/435eec4a-8e31-36e9-8420-47c10c513569.png)

---

![スライド15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2332636/23f14040-c48a-7850-5828-6a2645f9c8da.png)
tomcat 設定１－１

sudo su -

```bash


#CentOS のバージョン確認
cat /etc/redhat-release

#Apache Mavenを使う可能性もあるのでインストール
sudo yum install maven


#Java 8 (JDK) ランタイムのインストール
yum install java-1.8.0-openjdk

#java 8 (JDK) 開発環境のインストール
yum install -y java-1.8.0-openjdk-devel

#インストール確認
java -version

#apache確認
yum -y install httpd

#以下コマンドでサービスが起動していることを確認します。
systemctl start httpd.service

#ステータス確認コマンド
systemctl status httpd.service
systemctl enable httpd.service


□以下よりtomcat

#下記URLにアクセス
http://tomcat.apache.org/download-80.cgi

#zipにカーソルを合わせ、リンクのアドレスをコピーします。

#わかりやすく/usr/local/ファルダへ移動
cd /usr/local/

#wgetの後に(Binary DistributionsのCore)をコピーして下記のようにダウンロード
wget  https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.78/bin/apache-tomcat-8.5.78.tar.gz


#解凍コマンド→解凍後(apache-tomcat-8.5.72)
tar zxvf apache-tomcat-8.5.78.tar.gz


#tomcatへ名前を変更
mv apache-tomcat-8.5.73 tomcat


#Tomcat用ユーザー追加
useradd tomcat

#logディレクトリ作成
mkdir /var/log/tomcat

#tomcatの所有者を先に作成したtomcatユーザーに変更する。(opt/tomcatはディレクトリのことである。)
chown -R  tomcat:tomcat /usr/local/tomcat
chown -R  tomcat:tomcat /var/log/tomcat


#tomcatをsudouserユーザーに加えるのもありかも
usermod -aG wheel tomcat


#Tomcat用ユーザーログイン
su - tomcat

#version確認コマンド
/usr/local/tomcat/bin/version.sh

#起動コマンド
/usr/local/tomcat/bin/startup.sh

#停止コマンド
/usr/local/tomcat/bin/shutdown.sh


#起動しているかどうかの確認について
サーバーIP:8080
もしくは設定しているポートNo.
サーバーIP:portNo.

```


https://qiita.com/yuuki-furue/items/4dde98cb1e5adcb4e5b3
地味に便利なdisplay: contents;

## これは何

- 普段そこまで使わない気がするものの、地味に便利な`display: contents;`について説明した記事です

## `display: contents;`の働き

簡単に説明するなら`このスタイルが適用されたタグは、存在しない扱いになる`イメージです。

```html:書いているコード
<div style="display: contents;">
  <div>中の要素</div>
</div>
```

```html:描画されるイメージ
<div>中の要素</div>
```

## 使いどき

### 1. フレームワークなどの都合で不要な`<div>`が発生してしまう

筆者がパッと浮かぶのがRuby on Railsの`field_with_errors`や、React on Railsの`id="#{@name}-react-component-#{SecureRandom.uuid}"`です。

不要な箇所に勝手にdivを挟まれてしまうため、flexやgridでのレイアウトが壊れてしまう場合があります。

こんなときはclass名やid名を指定して`display: contents;`をかけてあげると解決します。

### 2. セマンティックとレイアウトを両立させたい

例えばこういうコードがあったとします。

```html:書いているコード
<div>
　　<h1>ページのタイトル</h1>
　　<p>リード文</p>
　　<section>
　　　　<h2>1つ目のセクションのタイトル</h2>
　　　　<div>
　　　　　　<!-- セクションの中身 -->
　　　　</div>
　　</section>
　　<section>
　　　　<h2>2つ目のセクションのタイトル</h2>
　　　　<div>
　　　　　　<!-- セクションの中身 -->
　　　　</div>
　　</section>
</div>
```

コード自体は普通の内容だと思いますが、このとき実現したいレイアウトが以下のようなものだったとします。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/9baa6bbb-7882-6b56-4337-a6d7c1da5e55.png)

見て分かるように`display: grid;`を使うとかなり楽に組めるのですが、先ほどのコードの`section`があるので微妙にややこしくなってしまいます。
`subgrid`が使えれば良いのですが、まだFirefoxでしか使えないので現実的ではありません。[^1]

[^1]: `subgrid`の良さは[こちらの記事](https://qiita.com/xrxoxcxox/items/cff77087cd76d622be27)で紹介しているので良かったら読んでみてください。

ここで`display: contents;`を使うと……。
（説明のしやすさの都合上インラインでスタイルを書いています）

```html:display:contents;有りのgrid
<div>
　　<h1>ページのタイトル</h1>
　　<p>リード文</p>
　　<section style="display: contents">
　　　　<h2>1つ目のセクションのタイトル</h2>
　　　　<div>
　　　　　　<!-- セクションの中身 -->
　　　　</div>
　　</section>
　　<section style="display: contents">
　　　　<h2>2つ目のセクションのタイトル</h2>
　　　　<div>
　　　　　　<!-- セクションの中身 -->
　　　　</div>
　　</section>
</div>
```

上記はこういうイメージになります。

```html:CSSが効いた後のHTMLのイメージ
<div>
　　<h1>ページのタイトル</h1>
　　<p>リード文</p>
　　<h2>1つ目のセクションのタイトル</h2>
　　<div>
　　　　<!-- セクションの中身 -->
　　</div>
　　<h2>2つ目のセクションのタイトル</h2>
　　<div>
　　　　<!-- セクションの中身 -->
　　</div>
</div>
```

このため、一番上のdivに`display: grid;`を指定すれば楽にレイアウトができます。

`ここからここまではsectionで括られている`ことを示しつつもレイアウトが上手くできるので嬉しいのではないでしょうか。

……ただ、現在はバグがあるようで`display: contents`が指定された要素はアクセシビリティツリーから削除されてしまうみたいです。

> ブラウザーのバグにより、現在のところ、この値を使用するとアクセシビリティツリーから要素を削除します。 — 読み上げソフトは中に何があるかを見ません。詳しくは後述のアクセシビリティの考慮の節をご覧ください。
>
> <cite>出典：<a href="https://developer.mozilla.org/ja/docs/Web/CSS/display#display_contents">display - CSS: カスケーディングスタイルシート | MDN</a></cite>

さすがに対応されるだろうとは思うのですが……良さが半減してしまうのは否めません。

## まとめ

- display: contents;は「指定された要素は存在しない扱い」になる
- フレームワークなどの都合で不要なdivが発生してしまうときや、セマンティックとレイアウトを両立したいときに使える
    - ただし2021年11月現在、アクセシビリティ的に良いとは言えない