OmniAICreator/Qiita-1.07M huggingface dataset:
Firestore×Algoliaで押さえておきたい基本的なポイント


firestoreで全文検索を実現する方法の一つとして、Algoliaの導入が考えられます。
Firestoreでは、全文検索をサポートしておりません。
公式でも、外部の検索サービスの使用が推奨されています。
[全文検索｜Firebase](https://firebase.google.com/docs/firestore/solutions/search?hl=ja)

本記事では、**これだけ押さえておけば簡単に検索機能が実装できるポイント**をまとめてみます。

#Search with Algoliaの導入
まずは、FirestoreとAlgoliaのデータを同期させる必要があります。
従来では、Cloud Functionsを用いて、Firestoreの変更を検知し、Algoliaにデータを渡す必要がありました。
現在は、Firebase ExtensionsにSearch with Algoliaがリリースされており、簡単にFirestoreとAlgoliaの連携が可能です。
Search with Algoliaの導入は他記事に丸投げします。下記記事で丁寧に解説されています。
[超お手軽にFirestoreに全文検索を導入できるSearch with Algoliaを試してみた](https://qiita.com/mogmet/items/943c0450957298f007ac)

Search with Algoliaはまだβ版であるのと、拡張性に疑問点があるので、開発中のアプリによってはCloud Functionsの使用を検討して下さい。

##補足
Seach with Algoliaの拡張機能の構成において、Indexable Fieldsを任意に設定することができます。任意ではあるのですが、Indexable Fieldsを設定しておかないと、「Record is too large」というエラーで弾かれる可能性があります。

どうもAlgoliaに登録できるレコードサイズはかなり少ない模様です。
そのため、**検索対象のデータに限定してAlgoliaに渡す必要があります。**
レコードサイズの目安としては、ドキュメントの下記部分が一つの参考になります。
[Reducing Record Size|Algolia](https://www.algolia.com/doc/guides/sending-and-managing-data/prepare-your-data/how-to/reducing-object-size/#:~:text=After%20removing%20attributes)
　
Algoliaには、あくまで検索対象のデータを格納するのが無難です。
よって検索後の表示に必要なデータも併せてAlgoliaに格納することは難しいです。
Algoliaで検索した後、表示に必要なデータは改めてFirestoreから持ってくる必要があります。

![スクリーンショット 2021-09-09 204133.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1766904/4ec8e387-237c-0cf0-d494-1725ff14f1fa.png)


#Algoliaの基本的な使い方
Javascriptで記述していきます。
ドキュメントの下記あたりから抜粋しています。
[Algoliaドキュメント](https://www.algolia.com/doc/api-client/methods/search/)
###データの取得
Algoliaからのデータの取得

```Javascript
index.search('検索キーワード', {
  page:0 //ページネーションのため
}).then(({ hits }) => {
  console.log(hits);
});
```
index.search('検索キーワード',query）が基本構文です。
ページネーションについては後述します。
[Algoliaドキュメント](https://www.algolia.com/doc/api-reference/api-methods/search/)

また、処理を行うJSファイルの任意の箇所に下記コードを記述しておきます。
'YourApplicationID'、 'YourSearchOnlyAPIKey'、'indexName'は各自のAlgoliaサイトの「API　Key」から入力します。

```Javascript　
const client = algoliasearch('YourApplicationID', 'YourSearchOnlyAPIKey');
const index = client.initIndex('indexName');
```

またAlgoliaコンソール上で検索対象のデータを指定するのを忘れないようにしましょう。
![検索対象.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1766904/2e26eee2-7b5d-8ff2-8c6c-bd4b5eb16083.png)

###ページネーション
ページネーションの実装は非常に簡単です。
デフォルトでは、1ページで20件ヒットします。

```Javascript
index.search('検索キーワード', {
  page:0 //1~20件目がヒット
　//page:1なら、21~40件がヒット
}).then(({ hits }) => {
  console.log(hits);
});
```

pageの値を変数で管理すれば、ページネーションが簡単に実装できそうです。
ヒット数は下記画面部分で任意に指定する事ができます。
![pagenation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1766904/7b7cc401-5236-e90a-3249-071e93f3817d.png)

##ソート機能
ソート機能は、Algoliaのコンソール上で設定することが可能で、index.serachで呼び出すと勝手にソートしたデータを取得してくれます。
![ソート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1766904/c5aea10a-ffa8-6495-559d-9289fb2445d0.png)

##最後
データの取得、ページネーション、ソートが出来れば、最低限の検索機能が実装できるのではないでしょうか。

その他にも、ユーザーランキングや検索対象のハイライトなどの機能も簡単に実装できそうです。　

私が個人開発したアプリでもAlgoliaを用いた全文検索機能を実装しておりますので、もし良ければデモ用にご利用下さい。

[個人開発したアプリ「Share Hondana」](https://sharehondana.web.app/)
python(mapとlambdaとオブジェクト化について)

#ラムダ式とMapとオブジェクト化についてここで整理しておく。

・まず、簡単な関数で考える。

```Python
#簡単な関数(その１)
def calc(a,b):　#calc関数に二つの引数aとbを渡す
  return a*b　#aとbを掛け合わせた後、返す。
calc(3,10)
>>>30
```
・上記の関数について、lambdaでは以下のように書き換えることができる。

```Python
#lambda関数（無名関数)
(lambda a,b:a*b)(3,10)
>>>30
```

・もう一つ簡単な関数を用意する。

```Python
#簡単な関数(その2)
def calc(x):
  return x*2

for num in [1,2,3,4]:
  print(calc(num))
>>>2
>>>4
>>>6
>>>8
```

・上記の関数について、lambdaもしくはmapを用いて以下のように書き換えることができる。

```Python
#map関数
list(map(calc,[1,2,3,4]))

#map関数とlambdaの組み合わせ
list(map(lambda x:x*2,[1,2,3,4]))

>>>[2,4,6,8]
```

・次に、クラスとそのオブジェクト化（インスタンス化）について整理しておこうと思う。

```Python
#クラスの作成
class calc: 
#__init__は特殊メソッドの一つでほかの言語でコンストラクタに該当する。
#コンストラクタとはオブジェクト化と同時、初期化されることをいう
#selfはあまり意識する必要はない。python特有の書き方であることを覚えておくとよい。
  def __init__(self,x,y):
    self.x=x 
    self.y=y
  
  #複数のメソッドを用意した。
  #メソッド1
  def add(self,a,b):
    return a+b
  #メソッド2
  def add_another(self): 
    return self.x+self.y
  #メソッド3
  def mutli(self,a,b): 
    return a*b

#値を渡したうえで、オブジェクト化
a=calc(3,4) 
#それぞれのメソッドを呼び出す
a.add_another(),a.add(2,12),a.mutli(1,9) 
>>>(7, 14, 9)
```



今回は、map,lambda,クラス・オブジェクト化について記事としてまとめた。
今後、pythonのテクニックについて、いろいろとまとめていきたい。
Rails | バリデーション validates と validate の違い

モデル(model)にバリデーションを書く際に使い分けすることがあったのでメモ。

## それぞれの違いについて

###validates
標準のバリデーションを書くときはこれ
>使い方はRailsガイドを参照
>https://railsguides.jp/active_record_validations.html

###validate
自分でオリジナルのバリデーション用メソッドを書くときはこれ

## validateの使用例
```ruby:model
validate :photo_size, :photo_length

private
  def photo_size
    photos.each do |photo|
      if photo.blob.byte_size > 5.megabytes
      photo.purge
      errors.add(:photos, "は1つのファイル5MB以内にしてください")
      end
    end
  end

  def photo_length
    if photos.length > 10
      photos.purge
      errors.add(:photos, "は10枚以内にしてください")
    end
  end
```

私はActiveStorageでバリデーションを作成した際に違いに気づき、疑問に思ったので残しておきます。
DjangoーCBVのソースコード分析してみる。

　Views層でFBVとCBVを書く時、なぜ同じ結果が得られるかの疑問はないでしょうか。そしてUrls層でルーティングを設定する場合もCBVとFBVの書き方が変わりますよね？　その原因をソースコードに合わせ、見ていきたいと思っています。

　CBVの根本的なところに入るための入口はどこにあるか、そしてどの流れでgetやpostを実行するのかを見てみましょう！

# 入口
```python
path('books1/', views.Books.as_view())
```
as_view()を入り口とし、後ろのソースコードを追跡します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1729963/33393447-8b9f-b099-b7fa-f27d1e604b22.png)

はい、では、`@classonlymethod`はviews.Books.as_view()と書く理由で、クラスメソッドのことです。

as_viewメソッドはviewが戻り値になり、このviewは実際にas_view内部の関数のことを指しています。

view関数の戻り値はdispatch関数なので、次はdispatchを追跡します。


**ここで、ちょっと補足しますが、pythonの継承関係について。このdispatchの例で言うと、dispatchを実行したい場合、最初はクラスのobject(インスタンス)から探します、そしてobjectが所属したclass、って継承されたclassの流れでdispatchを探します。今回はobjectにdispatch方法がないため、自然にViewクラスのdispatch方法を実行します。***

# dispatch

```python
   def dispatch(self, request, *args, **kwargs):
        # Try to dispatch to the right method; if a method doesn't exist,
        # defer to the error handler. Also defer to the error handler if the
        # request method isn't on the approved list.
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

```
これはViewクラスのdispatch方法です。引数について、selfはobjectのこと、requestはurlsで与えられたrequestのことです。

if文のところから見てみます。request.method.lower()は例えばPOSTやGETを小文字化するため、self.http_method_namesはなんやろ？？

実はViewクラスで定義され、

```python
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
```
となります。

次に、if分が成立したら、ようするにrequest methodがこのリストに入っているのであれば、次を実行します。

```python
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
```
getattrを使って、self(objectのこと)の中に、POSTやGETを小文字化した方法はあるかどうか。あるの場合はhandler変数に与え、なかった場合はself.http_method_not_allowedがhanlder変数に与えます(self.http_method_not_allowedはデフォルト値と考えてもいいだと思います))。

そしてdispatch方法に戻り、戻り値をチェックしてください。

```python
return handler(request, *args, **kwargs)
```
なんと！思った以上より分かりやすくて、単純にhandlerがreturnされただけ！

まとめると、as_viewから入り、dispatch方法を実行し、帰ってくるのはgetやpost方法になります。

# まとめ
　CBVは非常に重要です、特にDjango RestFrameWork上に大きいな役割を果たしています。また、DRFは実際、DjangoのViewを継承されていますので、DRFの理解を深まるためにはCBVの実行の流れも理解する必要があるだと思っています。

　それほど詳しい説明ではないですが、最後まで見ていただきありがとうございました。
wsl で ssl エラーになる

## 現象

wsl 側で ssl 通信しようとすると、「**Please install missing certificates.**」となって通信に失敗します。

```terminal
...
Connecting to update.code.visualstudio.com (update.code.visualstudio.com)|20.43.132.130|:443... connected.
ERROR: cannot verify update.code.visualstudio.com's certificate, ...:
  Self-signed certificate encountered.
To connect to update.code.visualstudio.com insecurely, use `--no-check-certificate'.
ERROR: Failed to download https://update.code.visualstudio.com/commit:507ce72a4466fbb27b715c3722558bb15afa9f48/server-linux-x64/stable to /home/ubuntu/.vscode-server/bin/507ce72a4466fbb27b715c3722558bb15afa9f48-1631168155.tar.gz
Please install missing certificates.
Debian/Ubuntu:  sudo apt-get install ca-certificates
```

## 解決策

windows システム側にある CA cert ファイル(筆者環境ではxxx.pem)を wsl 側の``/usr/local/share/ca-certificates/`` ディレクトリに保存。拡張子が ``pem``のものは``crt``に変更。その後、``update-ca-certificates``を実行することで、wsl 側に証明書を配置でき、ssl エラーが出なくなりました。

```terminal
$ sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
2 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
```
アジャイル開発をこれから行う上で予め、学んだほうがいいと指摘されたこと


※あくまで私の場合で、人それぞれ異なると思っております。


①PGの第二言語(Pythonともう一つ)

→対象未定



②AWS ※特にKubernetes

(1)Amazon EKS とは


https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/what-is-eks.html
 

(2)EKSworkshop.com


https://www.eksworkshop.com

 


③（アジャイル開発における？）プロダクトバックログの出し方を調べておく

→学習教材未定

④次世代の技術的潮流に対する自分の認識と確固たる考え（裏付けを自分で持った上で）

→学習教材未定

⑤テスト関連(自動化など？)

→学習教材未定
tableのtrをリンクにしつつ、その中に通常のリンクを置く (Pure.js)

data-hrefの中にリンクを置きたいとき、
もしくはボタン、チェックボックスを置きたいときの備忘として残します。

```html
<table>
    <thead>
        <tr>
            <th>リンク先</th>
            <th>通常リンク</th>
        </tr>
    </thead>
    <tbody>
        <tr data-href="https://qiita.com/" tabindex="0" role="link">
            <th>https://qiita.com/</th>
            <td class="object">
                <a href="#aaa">編集する</a>
            </td>
        </tr>
    </tbody>
</table>
```
- `tabindex="0"`でタブ操作を有効にしてます

```js
var td = document.querySelectorAll('tr[data-href] > *:not(.object)');

Array.prototype.forEach.call(td, function (e) {
    e.addEventListener('click', function() {
        window.location = e.parentNode.getAttribute('data-href');
    });
});
```

デモはこちら

https://codepen.io/gagegogi/pen/JjJWKQa
Nuxt.jsでモーダルウィンドウを実装

# 環境
* Nuxt.js
* portal-vue


# portal-vueの導入
```nuxt.config.js
export default {
  // 省略
  modules: [
    ['portal-vue/nuxt']
  ],
  // 省略
}
```

## コンポーネント
```vuejs
<template lang="html">
  <Portal to="modal">
    <transition name="modal" appear>
      <div name="modal" class="modal-group active">
        <div class="modal-mask" @click="$emit('close')"></div>
          <div class="modal active" tabindex="-1" id="edit">
            <form>
              <div class="modal-header">
                <slot name="title"></slot>
                <button
                  type="button"
                  class="btn"
                  data-dismiss="modal"
                  @click="$emit('close')"
                >
                  閉じる
                </button>
              </div>
              <div class="modal-body">
                <slot></slot>
              </div>
              <div class="modal-footer">
                <slot name="footer"></slot>
              </div>
            </form>
          </div>
      </div>
    </transition>
  </Portal>
</template>

<script>
export default {
  head: {
    bodyAttrs: {
      class: 'active-modal'
    }
  },
  data() {
    return {
      showModal: false,
    }
  },
  methods: {
    closeModal() {
      this.$emit('close')
    }
  }
}
</script>

<style lang="scss" scoped>
.modal-enter,
.modal-leave-active {
    opacity: 1;
    transition: opacity 300ms ease-out;

    .modal {
        transform: scale(1.1);
    }
}
</style>
```


```vuejs
<CommonModal v-if="showModal" @close="closeModal">
  <template #title>
    <h3 class="title">店舗追加</h3>
  </template>
  <p>モーダルウィンドウの中身</p>
  <template #footer>
    <button
      type="button"
      class="btn"
      @click="closeModal"
    >
      OK
    </button>
  </template>
</CommonModal>
```
ArduinoUNOとS-5851Aで温度を液晶ACM1602K-NLW-BBWに出力



x 原因は、わからないが液晶がちらつく場合は、ウェートを調整してね!!


x 74hc164を使用

目的
秋月で売っている安価なS-5851A(約110円)を使って温度を出力する。


![h_con54.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/f88abeee-a2dc-d958-6ac9-e88a556f92dc.jpeg)



![h_con39.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/35dcd021-6e56-f116-f200-0af5b95d82e3.jpeg)



```c++


#include <Arduino.h>
#include <Wire.h>

//*          *****  *****  
//*         *       *    * 
//*         *       *    *
//*         *       *    *
//*         *       *    *
//********   *****  *****

#define S5851A 0x48

#define DW   digitalWrite

#define swdclk   4 // 4pin
#define swdio    3 // 3pin
#define en       2 // 2pin

//              12345678   12345678   12345678   12345678
char b8[8] = {0b10000000,0b01000000,0b00100000,0b00010000,
              0b00001000,0b00000100,0b00000010,0b00000001 };

void seg1(int v,int rs)
{
    for(int jj=0;jj<8;jj++){
        if( (v & b8[jj]) == 0 ){
                DW(swdio,0); //ビットが0
        } else {
                DW(swdio,1); //ビットが1
        }//endif
        DW(swdclk,1);
        DW(swdclk,0);
    }//for
    
    DW(swdio,rs);   
    DW(en,1); delay(1);
    DW(en,0);       
}//seg1

//文字列の表示 nana_seg
int ns_printf(char *str1) {

    //文字の中身がゼロか
    while(*str1){

        //一文字出力
        seg1( *str1 ++ , 1  );

    } //while

    //戻り値
    return(0);
}//ns_printf

int lcd_int[]={
  0x30,0x30,0x30,0x38,0x08,0x01,0x06,0x08+0x04
};//lcd_init

void setup() {

  //GPIO初期化
  pinMode(swdclk,OUTPUT);
  pinMode(swdio,OUTPUT);
  pinMode(en,OUTPUT);
  DW(en,0);

  //液晶の初期化
  for(int ii=0;ii<8;ii++){ 
    seg1( lcd_int[ii] , 0);delay(2);
  }//for

  seg1( 0x80+0x00 , 0  ); //1ライン目にカーソルを移動
  ns_printf( "START" ); delay(500);
  seg1(   0x01  , 0  ); delay(5); //画面クリア

  //i2cの初期化
  Wire.begin(); //UNO

}//setup

char data_read[8]; //バッファー
int  tempval;      //温度

void loop() {

  //画面クリア
  //seg1(   0x01  , 0  ); 
  //delay(5);

  seg1( 0x80+0x00 , 0  ); //1ライン目にカーソルを移動

  //0番目のレジスター
  Wire.beginTransmission(S5851A);
  Wire.write(0);
  Wire.endTransmission();
  delay(1);

  //温度の読み込み
  tempval = 99;
  Wire.requestFrom(S5851A, 1);
  while(Wire.available())  {    // 要求より短いデータが来る可能性あり
    tempval = (int)Wire.read(); // 1バイトを受信
  }//while
  delay(1);

  //7セグに表示する為の変換
  int s = tempval;
  int n0,n10,n100,n1000;

  n10   = s / 10;            // 1234 -> 123
  n0    = s - (n10 *10);     // 1234 -  1230 -> 4
  n100  = n10 / 10;          // 123  -> 12
  n10   = n10 - (n100 *10);  // 123  -  120 -> 3
  n1000 = n100 / 10;         // 12   -> 1
  n100  = n100 - (n1000*10); // 12   -  10  -> 2

  //画面に表示
  //data_read[0] = '0' + n1000;
  //data_read[1] = '0' + n100;
  data_read[0] = '0' + n10;
  data_read[1] = '0' + n0;
  data_read[2] = 0;
  ns_printf(data_read);

  //1秒待つ
  delay(1000);

} //loop




```
pythonで移動平均計算するならbottleneckライブラリを使うべし

# 時系列データ(株価データ)のある値(終値)の移動平均を計算するのに、どのやり方が早いのかやってみた。

1. pandas
2. numpy
3. bottleneck(中身はnumpyが使われているらしい）


# PCスペック ryzen3600

# 結論. bottleneck

```python
import pandas as pd
import numpy as np
import bottleneck as bn

# 読み込み
path = 'データ.csv'
df = pd.read_csv(path)

# 終値
close = df['Close']

# pandas
%time ma_close = close.rolling(25).mean()

# numpy
def moving_average(a, n) :
    ret = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret[n - 1:] / n
%time ma_close = moving_average(close.to_numpy(), 25)

# bottleneck
def rollavg_bottlneck(a,n):
    return bn.move_mean(a, window=n,min_count = None)
%time ma_close = rollavg_bottlneck(close.to_numpy(), 25) 


```

```:%time
CPU times: user 332 µs, sys: 0 ns, total: 332 µs Wall time: 280 µs
CPU times: user 130 µs, sys: 0 ns, total: 130 µs Wall time: 100 µs
CPU times: user 42 µs, sys: 0 ns, total: 42 µs Wall time: 45.5 µs

```
pandas の groupby が Empty DataFrame で失敗する件

なんか pandas の groupby の計算がうまく動かないので、同じように困っている人（がもしいたら、、）のための情報です。

# うまくいく使い方

pandasでは、groupbyでグループ分けして、平均とかを計算できます。

```python
import pandas as pd
df = pd.DataFrame({
    'city' : ['tokyo', 'tokyo', 'osaka', 'osaka'],
    'month': ['April', 'May', 'April','May'],
    'price': [200, 300, 150, 250]
})
print(df.groupby('city').mean())
```

出力

```
       price
city        
osaka  200.0
tokyo  250.0
```

# うまく行かない場合

ところが、空の DataFrame を最初に作っておいて、行を追加して実行すると、うまく動きません。

```python
df0 = pd.DataFrame(columns=['city','month','price'])
df0 = df0.append({'city':'tokyo', 'month':'April', 'price':200}, ignore_index=True)
df0 = df0.append({'city':'tokyo', 'month':'May', 'price':300}, ignore_index=True)
df0 = df0.append({'city':'osaka', 'month':'April', 'price':150}, ignore_index=True)
df0 = df0.append({'city':'osaka', 'month':'May', 'price':250}, ignore_index=True)
print(df0.groupby('city').mean())
```

結果

```
Empty DataFrame
Columns: []
Index: [osaka, tokyo]
```

dfとdf0は、print しただけでは全く同じ。

```python
print(df)
print(df0)
print(type(df))
print(type(df0))
```

出力

```
    city  month  price
0  tokyo  April    200
1  tokyo    May    300
2  osaka  April    150
3  osaka    May    250
    city  month price
0  tokyo  April   200
1  tokyo    May   300
2  osaka  April   150
3  osaka    May   250
<class 'pandas.core.frame.DataFrame'>
<class 'pandas.core.frame.DataFrame'>
```

（あれ？良く見ると、空白１個分ずれているけど、これが何か悪さしているのかな？）

# 少し変えると動く

ちなみに、空の DataFrame の作り方によっては、うまくいく場合もある。

```python
df0 = pd.DataFrame({'city':[], 'month':[], 'price':[]})
df0 = df0.append({'city':'tokyo', 'month':'April', 'price':200}, ignore_index=True)
df0 = df0.append({'city':'tokyo', 'month':'May', 'price':300}, ignore_index=True)
df0 = df0.append({'city':'osaka', 'month':'April', 'price':150}, ignore_index=True)
df0 = df0.append({'city':'osaka', 'month':'May', 'price':250}, ignore_index=True)
print(df0.groupby('city').mean())
```

出力

```
       price
city        
osaka  200.0
tokyo  250.0
```

# 感想

pandasの公式マニュアルを見ても、empty DataFrame の作り方は見つからないので、そもそもこういう使い方が良くない使い方のような感じです。


# 解決

公式サイトを良く見たら、
https://pandas.pydata.org/docs/user_guide/groupby.html?highlight=empty#other-useful-features
「以下のように書け」と書いてありました。これなら動きます。

```python
df0.groupby(['city']).agg({'price': 'mean'})
```

何故こうなるかというと、price の型が異なっていて、

```
>>> df.dtypes
city     object
month    object
price     int64
dtype: object
>>> df0.dtypes
city     object
month    object
price    object
dtype: object
```

dfはすべてint64だと分かっているので、mean( ) の計算ができるけど、df0 は何が来るか分からないので mean( ) の計算は出来ないと認識されるため。agg を使うと、「そんなの良いから計算してよ」ということで計算してくれる。
（というのが私の理解）
ちなみに、「少し変えると動く」の方法だと、price は float だと推測される。型の推定のアルゴリズムが違うっぽい。


以上
[Github Actions] Python + Poetry

## こちらのコードを使って GitHub Actions を試す。

https://qiita.com/mykysyk@github/items/5f08658d0ca2f1c6b32a

## ソースを持ってくる

```
mkdir -p /tmp/$(date +%Y%m%d) && cd $_
git clone https://github.com/mykysyk/zip-code.git
cd /tmp/$(date +%Y%m%d)/zip-code
```

### こんな感じになる

```
/tmp/$(date +%Y%m%d)/zip-code
├── LICENSE
├── README.rst
├── poetry.lock
├── pyproject.toml
├── src
│   └── zip_code_app
│       ├── __init__.py
│       └── core.py
└── tests
    ├── __init__.py
    └── test_zip_code_app.py
```

## テンプレート作成 

```
git switch feat/github_actions
mkdir -p  /tmp/$(date +%Y%m%d)/zip_code_app.github/workflows
vi /tmp/$(date +%Y%m%d)/zip_code_app/.github/workflows/sample.yaml
```

```yaml:sample.yaml
name: Python Lint & Test
on: push
jobs:
  build:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        python-version: [3.9]
    steps:
      - uses: actions/checkout@v2
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v2
        with:
          python-version: ${{ matrix.python-version }}
      - name: Update pip
        run: |
          python -m pip install --upgrade pip
      - name: Install Poetry
        run: |
          pip install poetry
          poetry install --no-interaction
      - name: Lint with flake8
        run: |
          pip install flake8
          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
          flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
      - name: Test with pytest
        run: poetry run pytest -vv
```

## github に　push する

```
git push origin feat/github_actions
```

## Python の Lint　と TEST　が完了するのを確認

https://github.com/mykysyk/zip-code/actions/ 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24811/655c6242-7683-088d-c09d-f49060fbe605.png)

## 参考

https://docs.github.com/ja/actions/guides/building-and-testing-python
(Ecto.CastError) expected params to be a map with atoms or string keys, got a map with mixed keysの対処法

#結論

mapをectoにcastするときは、keyをstring（"hoge"）かatom(:hoge)に統一する。


# 背景


mapにMap.putで新しいkey, valueを追加しようとした時に、

```
(Ecto.CastError) expected params to be a map with atoms or string keys, got a map with mixed keys
```

というエラーに遭遇した。

元々のmapが

```
map = %{"hoge" => "hoge", "huga" => "fuga"}
```

で、それに対して

```
Map.put(map, :foo, :bar)
```

のように要素を追加した。

# 原因

エラー文でも指摘されているように元々のmapはkeyが""で囲まれたstringになっているのに対して、追加した要素はatomになっている。

ectoにmapを渡すときは、mapかstringかを統一しなければいけないために起きたエラーだった。
openmp & openmpi on mac from brew 


#なんかclangだと上手く行かないのでgccを使いたい

clangのopenmpはなんか上手くいかないので，openmp & open-mpiをgccで使いたい．しかしbrewで入れようとするとデフォのclangが邪魔をする．

世の中には
* [MacでMPIとopenmpをハイブリッドにつかえる環境を構築する](https://qiita.com/ymd_/items/c30d83d0f7642fb3af57) とか
* [MacにMPI+OpenMPハイブリッド並列環境を構築する](https://qiita.com/kaityo256/items/ae9329dae24ea8828ae0)とか
があるけど，ソースを落とすとかmakefileを追記するとかより，もっと簡単に解決したい(さぼりたがり

[clangでopenmpを使う方法](#clangでopnempを使う?)もあるようだけど，うまく並列できなかったりする．

#brewを使う

`brew install gcc` はすんで，今だとgcc-11あたりが入っているとする．
brewでopen-mpiがclangで構築されるのは
```$brew --env                                                             
HOMEBREW_CC: clang
HOMEBREW_CXX: clang++```
となっているからなので，これを
```$export HOMEBREW_CC=gcc-11
$export HOMEBREW_CXX=g++-11
$brew install open-mpi --build-from-source```
と指定してソースからbuildすると，
`$mpicc -v`
(中略)
```Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.1.0 (Homebrew GCC 11.2.0)```
とgcc-11で構築される．
あとはmpicc, mpic++でopenmpもopen-mpiも使える．

#clangでopnempを使う?

[Apple ClangでOpenMPを使う](https://qiita.com/ktgw0316/items/23235dd2533f488be7da) 参照．
オプションとか探してmakefileをいじってテストして頑張ればopenmp & open-mpiできるのかも？
Sorting Layerと3D Objectの関係

これはもともとは [自分のブログ](http://www.highriskrevolution.com/wp/gamelife/2020/08/27/sorting-layer%e3%81%ae%e3%83%86%e3%82%b9%e3%83%88%e3%81%97%e3%81%a6%e3%81%bf%e3%81%9f/) に掲載していた記事なのだけど、まあ自分のブログは技術的な記事をドカドカ掲載するような場所でもないので、こちらにも並行して掲載しておく。

ある日、質問された。
**「そういえば、unityでsorting layerと3D objectの関係ってどうなってるんでしょう？」**
Sorting Layerは3D objectとどう影響しあうのか、検索してみたけれど見つからない。
**「違うものだから挙動しない」なんて解説もあってが、テストした結果は違うｗ**

まあこんなのはテストしてみりゃいいやと思って、下のようなsceneを作ってテストしてみたら、抱腹絶倒というか**「エー」な結果**が得られたので、ちょっとブログに記事として残しておきたい。
![sorting_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96586/64f73c46-28ed-5582-1c53-57df05dfb330.jpeg)
さて。
sorting layerは以下のルールで動く。

- リストの下の方が描画の優先度は高い。
- 同一優先度ならばカメラに近い方が「上」

またunityでは言うまでもなく3D Objectにはsorting layerはつかない。そこで空objectにspriteをぶら下げて、そいつにsorting layerをひっつけ、その下に3D Objectを配置するという暴挙に出てみる。
![sorting_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96586/3b3b5665-9f22-7db4-519d-4fd3752573cf.jpeg)
結論として言うと**3D Objectにsorting layerは影響しないが、その前後を2Dobjectが動く時には、sorting layerはレンダリング結果に影響する**という世にもキカイな挙動になるのである。
下は動かしたときに得られるレンダリング結果の一部。
![sorting_02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96586/ab595137-c5ba-ae88-c955-12e9902de8ef.jpeg)
テストをしたプロジェクトを下に置いておく。
なんの役に立つかは想像もつかないが、まあないよりはいいテストだろう。

https://github.com/wasakidog/sortinglayertest
けっきょく、よはく。を読んで

#今回読んだ本

<h3><a href="https://www.amazon.co.jp/%E3%81%91%E3%81%A3%E3%81%8D%E3%82%87%E3%81%8F%E3%80%81%E3%82%88%E3%81%AF%E3%81%8F%E3%80%82%E4%BD%99%E7%99%BD%E3%82%92%E6%B4%BB%E3%81%8B%E3%81%97%E3%81%9F%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%AC%E3%82%A4%E3%82%A2%E3%82%A6%E3%83%88%E3%81%AE%E6%9C%AC-%EF%BD%89%EF%BD%8E%EF%BD%87%EF%BD%85%EF%BD%83%EF%BD%94%EF%BD%81%EF%BD%92%EF%BC%8D%EF%BD%85-ebook/dp/B07FW35F5R">けっきょく、よはく。</a></h3>

>デザインは「余白」が9割。カフェ(カフェポスター、レストランのメニュー…)、ビジネス(会社案内パンフレット、プレゼン資料…)、和もの(着物レンタル広告、展覧会ポスター…)、化粧品(雑誌広告、新商品店頭POP…)、季節もの(ハロウィン、クリスマス、バレンタイン…)、ラグジュアリー(分譲マンションチラシ、車のポスター…)、豊富なレイアウト例!誰も教えてくれなかった「余白」デザインがわかる本。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651883/9945228a-db25-f4ea-dad4-8989ad8fe534.png)

#なぜこの本を読んだのか

* デザインの勉強をしたかったから
* トレースなどをしていてブロックとブロックの間のマージンや、コンテンツ幅の左右にある余白を意識するようになり余白の使い方が気になったから
* 中身を書店で少しみてみたら、「NG」なデザインとそのデザインを修正する「OK」なデザインの両方が載っている上に、違うパターンの「OK」デザインが載っていて種類が豊富だったから

# 本を読んで学んだこと

### 余白に関して

* <h4>写真を切り抜いて余白を作る</h4>

`chapter1` 雑誌のパン屋さん特集

:::note alert
【NGデザイン】
・パンや店内の画像をたくさん使っていることで文字が小さくなったり、画像が小さくなったりしていて見にくい
・背景色が濃くなっていて文字や画像が見にくくなっている
:::

:::note info
【OKデザイン】
・全てのパン屋さんの画像を１つのパンの画像のみにし、サイズを大きくした上でパンの形に合わせて切り抜く
・背景色を濃い色から白色に変更する
:::

* <h4>罫線を余白にする</h4>

`chapter2` ヨガスタジオのフライヤー

:::note alert
【NGデザイン】
フライヤー内で使われている表の四方に罫線が使われている
:::

:::note info
【OKデザイン】
表の罫線を横線だけにして縦線を余白にしている
:::

* <h4>全面に画像を使うときには中央に情報をまとめる</h4>

`chapter8` サマーセールのポスター

:::note alert
【NGデザイン】
画像も文字情報も全面に配置されているのでごちゃごちゃしている
:::

:::note info
【OKデザイン】
画像だけ全面に配置し、中央に透かした白枠を配置してその上に文字を配置している
:::

* <h4>適度な余白で見ている人の目を休ませる</h4>

`Column` 余白 × 安心感

:::note alert
【NGデザイン】
情報量が多く余白が少ない上に文字の大きさがどれもほぼ同じでメリハリがなく見にくい
:::

:::note info
【OKデザイン】
表示する情報を重要なものに絞り、その中でも優先順位を決めてそれに合わせて文字の大きさに差をつけたり、文字の色に違いを出したりしている
:::

* <h4>コピーの周りに余白を置くことで印象を強くする</h4>

`Chapter9` 新築分譲タワーマンションのチラシ

:::note alert
【NGデザイン】
文字情報が一面のいたるところに配置されている上に文字の大きさが似通っているのでコピーが目立たない
:::

:::note info
【OKデザイン】
コピーとその他情報とで文字情報の位置を２つに分けた上で、コピーの周りに余白を作って目立たせている
:::

### 余白以外に関して

* <h4>色々な場面に合ったフォントや配色</h4>
例えば、オープンキャンパスのチラシのフォントは「**女子大系**」には`VDL ヨタG M`、「**体育大系**」には`Sefilia Bold`、「**医療・福祉系**」には`Orpheus`を使い分けるなど、他にも色々なパターンに合わせてのフォントが紹介されていた。
<br>
加えて、宿泊施設のHPを作る際の配色で「**リゾートホテル**」には`爽やかなブルー、エメラルドグリーン`、「**シティホテル**」には`重厚感のあるブルー、落ち着いたブラウン`、「**旅館**」には`自然の緑、黒、白`と紹介されていた。他にもチャプターに合わせて様々な配色パターンが紹介されていた。

* <h4>カーニング</h4>
`５／10 （日）`と書かれていたとき、日付と曜日の余白が広すぎて別の要素に見えてしまったり、`「」`を使ったときも文章中で広い余白が生まれてしまうことがある。これを調整することをカーニングという。

* <h4>行送り</h4>
行送りが狭すぎると窮屈で読みにくくなるが逆に広すぎても読み手は疲れてしまう。そのため、行送りは文字サイズの`1.5倍〜1.75倍`に設定すると適切になる

# 感想
この本の中では主にチラシやポスターなどの印刷物のデザインを紹介することが多かったのですが、Webサイトのメインビジュアルを作る際に応用できるようなことも多くとても勉強になりました。
また、色々なデザインを見ることで自分の中の引き出しも読む前より増えました。
今回取得したテクニックに加えてさまざまなフォントや配色なども今後の案件で応用していけたらいいなと思います。
とってもお手軽にエディタでUnreal Insightsのプロファイルを取る方法

#### Unreal Insightsとは、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463796/56bc3dc3-8409-a9a2-d535-8a87d6315879.png)
UE4に標準搭載されているとっても便利なプロファイリングツールですね。

以下の公式ドキュメントや、様々な記事でその便利さは多く語られていると思うのですが、
本当にとっても便利です、おすすめです。
**Unreal Insights公式ドキュメント**
https://docs.unrealengine.com/4.26/ja/TestingAndOptimization/PerformanceAndProfiling/UnrealInsights/
**[UE4] Unreal InsightsでCPUのボトルネックを調べて修正する**
https://qiita.com/donbutsu17/items/159dfaacfac6a4b1392d
**UE4.25 Update - Unreal Insights -**
https://www.slideshare.net/EpicGamesJapan/ue425-update-unreal-insights
**[UE4]UnrealInsightsとログ表示**
https://qiita.com/EGJ-Takashi_Suzuki/items/4d586d73556995e3dd40

そしてこちらのUnreal Insights。
最近その利便性にさらに拍車をかけまして、
4.26よりエディタでも使えるコンソールコマンドが追加されました。

それが、 **「Trace.Start」** と **「Trace.Stop」**。

使い方はとても簡単です。
Output Logに **「Trace.Start」** と打てばTraceが開始され、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463796/3bcc3c78-c1f9-4e0e-a7d6-c96fe69f997d.png)
**「Trace.Stop」** と打てば、Traceが終了し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463796/c5a44fdd-27bf-48ae-11e3-ac89738ae616.png)
Savedフォルダ以下にプロファイル結果、utraceデータが保存されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463796/8e95c2d9-3a1a-2d8a-d60c-2178732cfc6b.png)
あとは、Engineフォルダ以下にあるUnrealInsights.exeを立ち上げ、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463796/46e62b84-256b-76e0-4c1d-9f694f99930d.png)
こんなのが出てきますので、先程のutraceデータをドラッグ＆ドロップすると……、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463796/05ddf016-d67d-6e6a-fcc8-fa031bb919f4.png)
めちゃくちゃお手軽にプロファイリングを行うことが出来ます！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463796/8bc2507c-2715-d1be-beef-9231ecf6fea6.png)

**「Trace.Start GPU」** と後ろに指定チャンネルを追加することも出来ますので、
CPUとGPUの処理を同時に見比べたりなんかも楽ちんですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/463796/ae4654b1-e660-fac2-3d8e-c0722683979b.png)

とっても便利なので、皆様ぜひ使ってみて下さい。


---------------------------------------
#### 2022/06/01追記
UE5.0より **「Trace.Start」** だけでは自動的にCPUのプロファイルが追加されなくなりましたので、
**「Trace.Start CPU,GPU」** と明示的にプロファイルしたい対象を指定していただけましたら。
また、CPUのプロファイルをする際は事前に **「stat namedevents」** をコンソールコマンドで打っておくと、より多くの情報が取れますのでオススメです。
鈴木さんコメントでの補足ありがとうございます！
博多弁(?)でコマンドの由来まとめ！英語が分かるとプログラミングが優しい言語になる❤

#博多弁でプログラミング言語を英語で！


###コマンド一覧

|コマンド |英単語  |日本語  |
|---|---|---|
|ls  |list  |リスト見して❤|
|mv  |move  |動かしとくよ～|
|cd|change directory|ディレクトリを変えとってね|
|rm|remove|”取り除く”って覚えたけど”消す”かな|
|mkdir|make directory|ディレクトリ作ってね|
|rmdir|remove directory|ディレクトリを消しちょくれ|
|chown|change owner|オーナー変えろー！|
|chmod|change mode|モード変えるけんね|
|cat|concatanate|結合しちゃうよ///|

##随時追加していくけんねっ！
①初めての投稿

初めてQiitaに投稿をさせてもらいます。
具体的な方法とか、よくわからないので、最初はお試しということで、、、

#pythonを使ってHello World!!と出力させてみる。
プログラムを始めるとき、どんな言語でもたいてい初めはHello World!!の出力を行うと思います。
(自分もPythonやC言語、Javaを学びましたがそうでした。)
なので今回もお試しということでこれを行いたいと思います。
出力をさせるためには print()関数を使います。
使い方は以下の通りです。

```python:how_to_use_print_function.py
#print()関数の使い方。
print(出力させたい文字列)
```
文字列というのは、今では説明を省かせてください(お試しなので、、、)
簡単に説明すると、ダブルクオーテーション(")でくくった文字、
もしくは、シングルクオーテーション(')でくくった文字をPythonでは文字列といいます。
(ほかにも作成する方法はあるにはあるが、今回は紹介を省きます。)
それではこれらを使って出力させてみましょう！

```python:helloworld.py
#Hello World!!と出力させるプログラムです。
print('Hello World!!')
```

どうでしょうか？出力されたでしょうか？
今回は自分も初投稿だったので、簡単な内容かつ、説明もかなり省きました。
(記事書いてる最中,緊張しっぱなしでしたww)
こんな感じで簡単なプログラムから、アルゴリズムなどを紹介していけたらいいなと思います。
UnityにおけるThirdPesronCharactorのエラーに対策

#ThirdPersonCharactorを使おう

##エラー内容

###エラー文
ThirdPersonCharactorを使おうとしたところ次の様なエラーが出た。

```cs
Assets/Standard Assets/Utility/SimpleActivatorMenu.cs(11,16): error CS0619: 'GUIText' is obsolete: 'GUIText has been removed. Use UI.Text instead.'
```

詰まるところ、
**GUITextが消去された**から、代わりに**UI.Textを使え**ということの様だ。

###エラー内容
実際にプログラムを見てみると、

```cs
using System;
using UnityEngine;

#pragma warning disable 618
namespace UnityStandardAssets.Utility
{
    public class SimpleActivatorMenu : MonoBehaviour
    {
        // An incredibly simple menu which, when given references
        // to gameobjects in the scene
        public GUIText camSwitchButton;
        public GameObject[] objects;


        private int m_CurrentActiveObject;


        private void OnEnable()
        {
            // active object starts from first in array
            m_CurrentActiveObject = 0;
            camSwitchButton.text = objects[m_CurrentActiveObject].name;
        }
```

実際にはもっと続いているが、今回問題となる場所のみを取り出してきた。
次に、どこが問題なのかを調べた。

##対処法
対処法を調べたところ、次のサイトに乗っていた。

https://zenn.dev/ryohakosako/articles/896e7201603b7c


要約すると、
上のプログラム中で、GUITextとなっている部分をTextに変えれば良いとのことだ。
また、それに付随して

```cs
Using UnityEngine.UI;
```
の一文も追加すると動いてくれるらしい。

```cs
using System;
using UnityEngine;

using UnityEngine.UI;

#pragma warning disable 618
namespace UnityStandardAssets.Utility


{
    public class SimpleActivatorMenu : MonoBehaviour
    {
        // An incredibly simple menu which, when given references
        // to gameobjects in the scene
        public Text camSwitchButton;
        public GameObject[] objects;


        private int m_CurrentActiveObject;


        private void OnEnable()
        {
            // active object starts from first in array
            m_CurrentActiveObject = 0;
            camSwitchButton.text = objects[m_CurrentActiveObject].name;
        }
```

Unityの方を確認すると、確かにエラーが消えていることが分かる。

##蛇足

```cs
Using UnityEngine.UI;
```
の一文を追加しないと、

```cs
Assets/Standard Assets/Utility/SimpleActivatorMenu.cs(15,16): error CS0246: The type or namespace name 'Text' could not be found (are you missing a using directive or an assembly reference?)
```
というエラーが出てくる。
つまり、Textが見つからないという感じなので、先の文を追加してあげる必要がある。
Ansibleの基本

##Ansibleの基本
ansible-playbookコマンドの実行結果と戻り値をまとめました。

##Ansibleの実行結果
| 実行結果     | ステータス  | 処理に対する結果内容 |
|:------------|---------:|:-------------:|
| ok          | 成功      | すでに定義された状態になっているため、処理を行わなかった         |
| changed     | 成功      | タスクで指定したステータスと異なっていたため、変更を行った       |
| skip        | 成功      | タスクの実行条件に当てはまらなかったため、処理を行わなかった         |
| unreachable | 失敗      | ターゲットノードに接続ができなかった           |
| failed      | 失敗      | タスクを行った結果、何らかのエラーが発生し、定義された状態にならなかった       |

##レジスタ変数
共通の戻り値一覧

| 戻り値    | 型     | 概要                                                  |
|:----------|------:|:-----------------------------------------------------:|
| changed   | 真偽値 | タスクの状態変更ステータスが格納される                   |
| failed    | 真偽値 | タスクの失敗ステータスが格納される                       |
| msg       | 文字列 | タスクに対するメッセージが格納される                     |
| rc        | 数値   | コマンドモジュール群を利用した場合の終了ステータスが格納される           |
| skipped   | 真偽値 | タスクのスキップステータスが格納される                   |
| stderr    | 文字列 | コマンドモジュール群を利用した場合のエラー出力が格納される |
Compositional Layoutsで水平方向のスクロール出来た！けど、垂直方向にもスクロール出来てしまう時の対処法

# 現状
![CompositionalLayoutsHorizontal現状.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/755375/e99086e2-908a-ee3e-39b2-640e8c3a35bc.gif)
# 解決策
以下のコードをCollectionViewに対して設定することで解決しました。

```swift:ViewController
        collectionView.isScrollEnabled = false
```
![CompositionalLayoutsHorizontal改善.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/755375/613932cf-7412-d374-7b64-4c3ed038c338.gif)


# 検証したこと
以下のdelegateメソッドを呼んで水平スクロールと垂直スクロールの時の挙動を調べた。


```swift:ViewController
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print(#function)
    }
```

# 検証結果
水平スクロールの時は呼ばれていない。
垂直スクロールの時は呼ばれている。
→垂直スクロールが要らないのでスクロールの処理を無効にすることで解決
![CompositionalLayoutsHorizontal検証.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/755375/778b58f9-7e9f-d9cd-d785-9c8ccf7ac60f.gif)

# GitHub
https://github.com/tatsuya-ss/CompositionalLayoutsHorizontalInspection

# 最後に
この方法が最善とは思わないので、もし他の方法があれば教えていただきたいです。
お願いします！！！！！！！！！！！！！！！！
WSL2でVagrant+ansibleの環境整備

表題の件メモ。

## やりたいこと

* WSL2でansible+Vagrantでのサーバー構成環境をつくりたい。
* 同一ディレクトリ内でvagrant upとansible-playbookコマンド実行したい。  
（vagrant.exeだと、ansibleはWSL2側、Vagrantfileはwindows側にしないといけなかった。）

## Vagrantインストール手順（ざっくり）

### 0. 前提

* WSL2(Ubuntu)がインストール済のWindows10

### 1. VirtualBoxをインストールする（Windows版でよい）

https://www.vagrantup.com/downloads

### 2. WSL2のaptでvagrantをインストールする

```
$ apt install vagrant

# apt installで入ったバージョン
$ vagrant --version
Vagrant 2.2.6

```

### 3. 環境変数設定

`.bashrc`に以下を追加

```
export VAGRANT_WSL_ENABLE_WINDOWS_ACCESS=1
export VAGRANT_WSL_WINDOWS_ACCESS_USER=ユーザー名
```

### 4. virtualbox_WSL2プラグインインストール

3.までだけだと、vagrant up時に`Warning: Connection refused. Retrying...`が出てSSH接続できなかった。
プラグインをインストールしたら解決した。

```
$ vagrant plugin install virtualbox_WSL2

Installing the 'virtualbox_WSL2' plugin. This can take a few minutes...
Fetching vagrant-libvirt-0.5.3.gem
Fetching virtualbox_WSL2-0.1.3.gem
Parsing documentation for vagrant-libvirt-0.5.3
Installing ri documentation for vagrant-libvirt-0.5.3
Parsing documentation for virtualbox_WSL2-0.1.3
Installing ri documentation for virtualbox_WSL2-0.1.3
Done installing documentation for vagrant-libvirt, virtualbox_WSL2 after 1 seconds
Installed the plugin 'virtualbox_WSL2 (0.1.3)'!
```

### 5. Vagrantfile修正

デフォルトだと/vagrantがマウントできずにこけるようである。

```
$ vagrant up
Bringing machine 'hostname' up with 'virtualbox' provider...
There are errors in the configuration of this machine. Please fix
the following errors and try again:

vm:
* The host path of the shared folder is not supported from WSL. Host
path of the shared folder must be located on a file system with
DrvFs type. Host path: .
```

Vagrantfileに以下を追加する。（ホストの.とvmの/vagrantをsyncしない）

```
h.vm.synced_folder ".", "/vagrant", disabled: true
```

### 6. vagrant upする

できる。

## ansibleインストール手順（ざっくり）

### 1. anyenvからのpyenvインストール

anyenv

```
$ cd ~
$ git clone https://github.com/riywo/anyenv ~/.anyenv
$ echo 'export PATH="$HOME/.anyenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(anyenv init -)"' >> ~/.bash_profile
$ exec $SHELL -l
```

pyenv

```
$ anyenv install pyenv
```

virtualenv

```
git clone https://github.com/pyenv/pyenv-virtualenv.git $(pyenv root)/plugins/pyenv-virtualenv
```

### 2. pythonインストールしてansibleのvirtualenv作る

```
$ pyenv install 3.9.7
$ pyenv virtualenv 3.9.7 ansible
$ pyenv versions
$ pyenv versions
  system
  3.9.7
  3.9.7/envs/ansible
  ansible
```

### 3. 対象ディレクトリでpythonバージョン指定

```
$ cd the_ansible_dir
$ pyenv local ansible
$ pyenv versions
  system
  3.9.7
  3.9.7/envs/ansible387
* ansible387 (set by /root/the_ansible_dir/.python-version)

$ cat .python-version
ansible
```

### 4. ansible-playbookとかansible-galaxyとかする

できる。

## 参考

* https://zenn.dev/lapi/articles/2021-06-10-vagrant_wsl2
* https://blog.fuga.jp/?p=2179
* https://www.vagrantup.com/docs/other/wsl
python初学者の備忘録　論理演算子について

#### [←目次](https://qiita.com/dondondon/items/cd45224ca93e65c976cc)

:::note info
python,Qiita初心者なので、備忘録として記載していきます。
なにせ初心者なので、知識不足はご理解ください。
知識を深めながら追記していきたいと思います。
:::

# 論理演算子とは
`基本`

+ 複数の式や値を比較し、真偽値の場合bool型を返す
+ bool型だけでなく、数値や文字列、リストなども真偽値として処理される

:::note alert
and, orの返り値はbool値とは限らない
:::

##### 以下の演算子がある
 
| 論理演算子| 記号 | 説明 |
|:-:|:-:|:-:|
| 論理積 | and(かつ) | すべての条件が真(True)のとき真(True)になる | 
| 論理和 | or(または) | 条件が1つでも真(True)であれば真(True)になる | 
| 否定 | not | 上記の結果を反転させる |


##### 真偽表

| x| y | and(かつ) | or(または) |
|:-:|:-:|:-:|:-:|
| 真 (True) | 真 (True) | 真 (True) | 真 (True) |
| 真 (True) | 偽 (False) | 偽 (False) | 真 (True) | 
| 偽 (False) | 真 (True) | 偽 (False) | 真 (True) | 
| 偽 (False) | 偽 (False) | 偽 (False) | 偽 (False) | 

:::note info
論理演算子の優先順位
「not」>「and」>「or」
:::

##### and(かつ)

```python
>>> 4 < 5 and 6 < 7
True
>>> True and True
True
>>> True and False
False
>>> False and True
False
>>> False and False
False
```

##### or(または)

```python
>>> 4 < 5 or 6 < 7
True
>>> True or True
True
>>> True or False
True
>>> False or True
True
>>> False or False
False
```

##### not(否定)

```python
>>> not True
False
>>> not False
True

>>> not (4 < 5 and 6 < 7)
False
# 上と同じ条件文
>>> not(4 < 5) and not(6 < 7)
False

>>> not (4 < 5 or 6 < 7)
False
# 上と同じ条件文
>>> not(4 < 5) or not(6 < 7)
False
```

# bool型以外のオブジェクトの場合

##### Pythonの論理演算において、偽(False)とみなされるのは以下のオブジェクト

+ 定数: NoneとFalse
+ 数値型におけるゼロ: 「0」, 「0.0,」 「0j」(複素数), Decimal(0), Fraction(0, 1)
+ 空のシーケンスまたはコレクション: '', (), [], {}, set(), range(0)

:::note info
上記以外はすべて真(True)とみなされる
:::

```python:bool関数()で真偽値の確認
>>> bool(10)
True
>>> bool(0)
False
>>> bool('0')
True
>>> bool('False')
True
>>> bool('')
False
>>> bool([False])
True
>>> bool([])
False
```

##### and, orの返り値はbool値とは限らない※notは除く
:::note warn
【論理演算子：必ずBool値を返す】という先入観
他言語の影響から、論理演算子は必ずBool値を返すという先入観があり、ご指摘を受けました。
pythonやJavaScript等はそれ以外の値も返すようです。。。
:::



##### ショートサーキット（短絡評価）

:::note info
Pythonでは標準でショートサーキット（短絡評価）が実装されている。
※対義語としてロングサーキット(完全評価)がある
(ユーザー定義関数を実装するなどが必要)
●and
・左辺評価時、真であれば右辺以降も評価される
・左辺評価時、偽であれば右辺以降は評価されない
●or
・左辺評価時、真であれば右辺以降は評価されない
・左辺評価時、偽であれば右辺以降も評価される
:::

```vb:VB.NETで例えると
AndAlso
OrElse
```
```c#:C#で例えると
&&
||
```

```python:pythonの真同士の評価
>>> x = 10
>>> y = 100

# -- 右辺まで評価されたため右辺の値を返す --
>>> x and y
100 
>>> y and x
10

# -- 左辺で評価されたため左辺を返す --
>>> x or y
10
>>> y or x
100
```

```python:pythonの偽同士の評価
>>> x = 0
>>> y = 0.0

# -- 左辺で評価されたため左辺を返す --
>>> x and y
0
>>> y and x
0.0

# -- 右辺まで評価されたため右辺の値を返す --
>>> x or y
0.0
>>> y or x
0
```

:::note info
bool型で扱いたい場合は以下のようにすればよい
:::

```python:bool関数()を使用する
>>> bool(x and y)
False
```

##### 文字列絡みのケース
```python:bool型以外のオブジェクトの扱い
# -- 最後に評価された値を返す --
>>> 'Hit' and False
False
>>> 'Hit' and True
True
>>> 'Dead' and 'Alive'
'Alive'
>>> 'Dead' or 'Alive'
'Dead'
```

##### おまけ
```answer.py
# -- 三項演算子のようにも使えるカモ --
answer = ''
ok = ''
answer = ok or "NG"
# -- 三項演算子だと --
answer = "OK" if ok == ('OK') else "NG"
print(answer)
# -- 出力結果 --
"NG"

answer = ''
ok = 'OK'
answer = ok or "NG"
# -- 三項演算子だと --
answer = "OK" if ok == ('OK') else "NG"
print(answer)
# -- 出力結果 --
"OK"
```
定義が存在するのにビルド時に「error CS0117」と怒られる

今回は割とうっかりミスな気もしますが、とりあえず備忘録として残しておくことにしました。
（内容でPlayBillingLibraryって単語が出てきちゃってますが、根本的には関係ないですっ）
（PlayBillingLibraryって単語で記事にきちゃった人、ごめんなさい...）


# 問題の概要

## そもそも何をしようとしていたか
- Android用にPlayBillingLibrary3.0以上を実装しようとしていた。<br>
（2021年11月までに対応しないとアプリのアップデートができないので）

## 問題の発生
1 : PlayBillingLibrary3.2.0のunitypackageをインストール （[ここからDLした](https://developers.google.com/unity/archive)）
2 : [手順](https://developer.android.com/google/play/billing/unity?hl=ja)に従い競合の解決
3 : 特にエラーも出なかったのでApkのビルドを開始

結果下記のエラーが発生

```
Assets/GooglePlayPlugins/com.google.play.billing/Runtime/Scripts/Internal/GooglePlayBillingUtil.cs(27,44): error CS0117: 'Debug' does not contain a definition for 'unityLogger'
```
訳 : 「Debug ってクラスに unityLogger なんてものはないぞ」と。

## 問題の詳細
ビルド時に、下記の部分でエラーが発生。

``` GooglePlayBillingUtil.cs
// クラスパス : Assets/GooglePlayPlugins/com.google/play.billing/Runtime/Scripts/Internal/GooglePlayBillingUtil.cs
/*前略*/
using UnityEngine;

public class GooglePlayBillingUtil : MonoBehaviour
{
	private const string GooglePlayBillingLoggingTag = "Google Play Store: ";
	private readonly ILogger _logger = Debug.unityLogger;	// <- こいつがエラー
	private static readonly List<Action> _callbacks = new List<Action>();
	private static volatile bool _callbacksPending;
	/*後略*/
}
```

ただ、ビルド前だとエラーは出ていないし、`unityLogger`にカーソルを当てるときちんと定義にジャンプすることが可能だった。

# 問題の解決

## 原因
自前クラスにネームスペースを切ってないDebugクラスがあり、しかも`#if !UNITY_EDITOR` でクラス全体を覆っていたためでした。

ビルドするときだけ、Debugクラスが認識されるようになっており
結果「Debug.unityLogger」が指すDebugクラスがどれかわかなくなってしまってたようです。

## 対応内容
`Debug.unityLogger` に `UnityEngine`をつけてあげたところ、ビルドが通るようになった。

``` GooglePlayBillingUtil.cs
// クラスパス : Assets/GooglePlayPlugins/com.google/play.billing/Runtime/Scripts/Internal/GooglePlayBillingUtil.cs
/*前略*/
using UnityEngine;

public class GooglePlayBillingUtil : MonoBehaviour
{
	private const string GooglePlayBillingLoggingTag = "Google Play Store: ";
	//private readonly ILogger _logger = Debug.unityLogger;	// <- こいつがエラー
	private readonly ILogger _logger = UnityEngine.Debug.unityLogger; // <- これでエラー回避できた
	private static readonly List<Action> _callbacks = new List<Action>();
	private static volatile bool _callbacksPending;
	/*後略*/
}
```

本当は自前クラスにnamespaceをつけるなりするべきなのでしょうが、如何せん手間がかかっちゃうので今回はこのような対応で済ませることに。

# 最後に
今回は自前クラスが悪さをしていたわけですが、場合によっては外部ライブラリが似たような悪さをしないとも限らないので
もしビルド時だけ「error CS0117」と怒られる箇所があった場合は、namespaceを指定して回避すると良いかもしれません。
外部キーを持つテーブルをマイグレーションした時のエラー'Failed to open the referenced table'の解決方法

# 経緯
Laravel8で```sail artisan migrate```を実行したときに出たエラーです。Usersテーブル(親テーブル)と、外部キーuser_idを持つPostsテーブル(子テーブル)があるとして書いていきます。2つのテーブルのためのマイグレーションファイルを作り、テーブルを設定し、```sail artisan migrate```と実行すると

```
SQLSTATE[HY000]: General error: 1824 Failed to open the referenced table 'users' (SQL: alter table `posts` add constraint `posts_user_id_foreign` foreign key (`user_id`) references `users` (`id`))
```
というエラーが出ました。
# 原因
エラーの内容は、「『posts』テーブルは外部キー『user_id』を持ってて『users』テーブルを参照しようとしているけど見つからないよ」というものです。これは、Postsテーブル(子テーブル)のマイグレーションファイルを作った後に、Usersテーブル(親テーブル)のマイグレーションファイルを作ったことが原因です。
本来は、
①usersテーブルが作成される。
②postsテーブルが作成される。
③user_idがusersテーブルを参照する。
という流れなのですが、このときは
①postsテーブルが作成される。
②user_idがusersテーブルを参照する。。。
となり、usersテーブルを参照したいけど、まだusersテーブルは作ってないから出来ない！という状態になってしまっています。

# 解決策
先にUsersテーブル(親テーブル)後でPostsテーブル(子テーブル)を作ればPostsテーブルはUsersテーブルを参照できて、問題が解決します。ですので、```sail artisan migrate:rollback```(後述しますが、このときにもエラー発生します。)でロールバックした後に、現在の状態では、マイグレーションファイルの名前の日付が、子テーブルのほうが早くなっていますが、日付順を入れ替えます。
### before
```2021_09_07_234727_create_posts_table.php``` ```2021_09_08_234727_create_users_table.php```
### after
```2021_09_08_234727_create_users_table.php``` ```2021_09_09_234727_create_posts_table.php```

これで再度マイグレーションをすると無事成功します。
## ロールバック時のエラー
ロールバックを実行しようとすると、

```
SQLSTATE[HY000]: General error: 3730 Cannot drop table 'users' referenced by a foreign key constraint 'posts_user_id_foreign' on table 'posts'. (SQL: drop table if exists `users`)
```

というエラーが出るかもしれません。(この例では起きないですが)。これは、Postsテーブル(子テーブル)が消されるまえにUsersテーブル(親テーブル)を消そうとしているため、「Postsテーブルのuser_idがUsersテーブルを参照してるから消せないよ」と言っているものです。usersテーブルのマイグレーションファイルにある、down時の設定をし忘れていることが原因で、ロールバック時に子テーブルを先に削除するように設定すると解決します。

### before

```php:2021_09_08_234727_create_users_table.php
    public function down()
    {
        Schema::dropIfExists('users');
    }
```

### after

```php:2021_09_08_234727_create_users_table.php
    public function down()
    {
        Schema::dropIfExists('posts');
        Schema::dropIfExists('users');
    }
```

# 参考
https://stackoverflow.com/questions/52377469/failed-to-open-the-referenced-table
https://stackoverflow.com/questions/32524101/laravel-change-migration-order
Ubuntu18.04でDrogger GPSを接続する

DroggerのGPSモジュールは、Androidアプリから簡単に動作する．
しかし，Ubuntu側と接続することが難しかった．

Bluetooth接続を行うと，すぐに切断されてしまうのである．
調べると，
使用するBluetoothプロファイルをSPPにしなけらばならないらしい．

標準ではL2CAPを優先的に利用したがる様子なので，
RFCOMMを明示的に利用し，SPPを使うことで解決した．

SPPの詳細は以下のサイトを参照のこと．
このサイトは他のプロファイルについても，とても分かりやすい．

https://www.wdic.org/w/WDIC/SPP

#環境

- Ubuntu 18.04.3LTS
- DG-PRO1RWS

#Bluetooth接続

スクリプトを書いたので，こちらをクローンする．

```
git clone https://github.com/ousagi-sama/spp-connect-bash-for-ubuntu18.04.git
cd spp-connect-bash-for-ubuntu18.04
```

以下の情報を元に，接続する.
- ID : 仮想ポートのID、基本的には0でよい
- ADDRESS : 接続するデバイスアドレス
- BAUDRATE : ボーレート

なお，DG-PRO1RWSのボーレートは，230400である．

```
bash spp-connect.sh [ID] [ADDRESS] [BAUDRATE]
```

一度通信行えば.spp-connect-configにその情報が保存されるため、
次回からは、IDなどの情報を省略できる

```
bash spp-connect.sh
```

仮想デバイスは/dev/rfcomm[ID]として表示される．
接続を切断するときは、Enterで仮想デバイスが解放される．

#接続後の確認

参考程度に，接続を確認したい場合，catコマンドを利用すると楽．

```
cat /dev/rfcomm[ID]
```

#接続用Scriptの中身

git cloneしたくない人用に以下にその内容を残す．
※ただし，最新版ではない可能性があるため注意．

```bash
#!/bin/bash
#
#使い方:#
#    spp-connect <[id] [address] [baudrate]>#

if [ $# -eq 3 ];then
    id=$1; address=$2; baudrate=$3
    echo "$id $address $baudrate" > .spp-connect-config
elif [ $# -eq 0 ];then
    if [ ! -e ".spp-connect-config" ];then 
        echo ".spp-connect-config is not found."
        exit 1
    fi
    set $(cat .spp-connect-config)
    id=${1}; address=${2}; baudrate=${3}
else
    text=$(cat $0 | grep "^#.*#$")
    echo -e "\033[33m${text//#}\033[32m"
    exit 1
fi

# 仮想ポートの作成
sudo rfcomm bind $id $address
sudo chmod 777 /dev/rfcomm$id
sudo stty -F /dev/rfcomm$id $baudrate cs8

read -p "Press [Enter] key to resume."

# 仮想ポートでの解除
sudo rfcomm release $id
```

#参考

[spp-connect-bash-for-ubuntu18.04](https://github.com/ousagi-sama/spp-connect-bash-for-ubuntu18.04)
[DG-PRO1RWS](https://www.bizstation.jp/ja/drogger/)
PermissionError: [Errno 13] Permission denied を回避する【VSCode】


## 事象

vscode で``F5``を押してデバッグ実行しようとすると、``PermissionError: [Errno 13] Permission denied``が発生して、デバッグモードが終了する。

```terminal
...
  File "/home/ubuntu/.pyenv/versions/3.7.10/lib/python3.7/socketserver.py", line 452, in __init__
    self.server_bind()
  File "/home/ubuntu/.pyenv/versions/3.7.10/lib/python3.7/http/server.py", line 137, in server_bind
    socketserver.TCPServer.server_bind(self)
  File "/home/ubuntu/.pyenv/versions/3.7.10/lib/python3.7/socketserver.py", line 466, in server_bind
    self.socket.bind(self.server_address)
PermissionError: [Errno 13] Permission denied
```

## 回避策

``launch.json``に``"sudo": true``を追加してあげる。

```json:launch.json
{
    "version": "0.0.1",
    "configurations": [
        {
            "name": "Flask",
            "type": "python",
            "request": "launch",
            "module": "flask",
            "stopOnEntry": false,
            "sudo": true,
            "cwd": "${workspaceRoot}/flask",
            "args": [
                "run",
                "--port",
                "80",
                "--no-reload",
                "--host=0.0.0.0"
            ]
        }        
    ]
}
```
【OBS Studio】ブラウザソースでDiscordを開いたとき、ビデオ通話画面の背景を透明にするCSS

ブラウザソースで`https://discord.com/app`を開く。

CSSはこちら

```css
body,
[class*=app],
[class*=bg],
[class*=chat],
[class*=callContainer],
[class*=fullScreen],
[class*=uploadArea],
[class*=gradient],
[class*=indicators] {
    background: none !important;
}

[class*=overlay],
[class*=border],
[class*=notice],
[class*=art],
[class*=art]+[class*=header] {
    display: none !important;
}
```

追加した後、「対話」ボタンで操作していく
QRコードでログインすればログインできる。

別のデバイスで通話に参加してから、そのアカウントでログインすると良い。


※追記
しゃべってない時に影を取得したいときのCSS
緑のクロマキーで抜きます。

```css
body,
#app-mount,
[class*=app],
[class*=bg],
[class*=background],
[class*=chat] {
    background: #000 !important;
}

div[class*=sidebar],
div[class*=overlay],
[class*=bg],
nav[class*=guilds],
[class*=indicators],
[class*=gradient] {
    display: none !important;
}

div[class*=speaking] {
    background-color: #0F0;
}
```