OmniAICreator/Qiita-1.07M huggingface dataset:
(レジ直出力)18番で高速Lチカを考える。(実機で動くかは、不明)



x オンラインコンパイラーを使ってロジックのテスト


目的
カラー液晶駆動の為にWR(18)を
目にも止まらない速度でLチカする

元ネタアドレス

https://prototype09.com/archives/177


参考


```c++


#define GPIO_0to31SET_REG   *((volatile unsigned long *)GPIO_OUT_W1TS_REG)
#define GPIO_0to31CLR_REG   *((volatile unsigned long *)GPIO_OUT_W1TC_REG)

#define pin3 5

GPIO_0to31SET_REG = 1<<pin3;
GPIO_0to31CLR_REG = 1<<pin3;


```


プログラム


```c++



#include <iostream>
using namespace std;

//#define GPIO_0to31SET_REG   *((volatile unsigned long *)GPIO_OUT_W1TS_REG)
//#define GPIO_0to31CLR_REG   *((volatile unsigned long *)GPIO_OUT_W1TC_REG)

unsigned long GPIO_0to31SET_REG = 0;
unsigned long GPIO_0to31CLR_REG = 0;

//WR信号を出力
void GPIO_WR(int o_a) {


   //ゼロの場合は、クリア　それ以外は、セット
   if( o_a == 0 ) {

     GPIO_0to31CLR_REG = GPIO_0to31CLR_REG | (1<<18);

   } else {

     GPIO_0to31SET_REG = GPIO_0to31CLR_REG | (1<<18);

   } //end if

} // end GPIO_WR

//ビットパターンを画面に出力
void bit_o(int rrr_a)
{



    printf("333222222222211111111110000000000\r\n");
    printf("210987654321098765432109876543210\r\n"); 
    printf("     ***       ** *        **    \r\n"); 

    printf("00000");
    
    for(int i = 0;i<(27+1);i++){
        
        //printf("%d:%d\r\n",i,(27-i));
        
        printf("%d",  (rrr_a & (   1<<(27-i)  )) >> (27-i)   );
        
    }//for
    
    printf("\r\n");

} //bit



//#define GPIO_D18_P 18
//#define GPIO_D18(s) digitalWrite(GPIO_D18_P,s)
//#define GPIO_WR(y)    GPIO_D18(y) //WR=0

//メイン処理
int main(void){
    

    // Your code here!
    printf("START\r\n");

    //WR信号を出力
    GPIO_WR(1);//WRB=0; 10
    //GPIO_WR(0);//WRB=0; 10

    //ビットを画面に出力
    bit_o(GPIO_0to31SET_REG);

    //bit_o(GPIO_0to31CLR_REG);    

   
//   while(1){
//
//    GPIO_WR(1);//WRB=0; 10
//    GPIO_WR(0);//WRB=0; 10
//
//  
//   
//  }


} //end main



```

結果

```

START
333222222222211111111110000000000
210987654321098765432109876543210
     ***       ** *        **    
000000000000001000000000000000000


```
PC用SPAMフィルターの今

## はじめに
この頃自宅PCにSPAMメールが多く届きます。SPAMメール対応はプロバイダ（どのプロバイダかは内緒）任せなのですがあまり精度が良くないらしく、SPAMメールの多さに閉口し自分でPCにSPAMフィルターを設定しました。
その時、いろいろと調べましたのでメモとして残しておきます。
なお本記事は、SPAMフィルターの設定手順ではありません。
## 環境など
Windows10
メーラー:Becky!2
ベイジアンSPAMフィルターを使いたい。フリーなものが良い。
## 調べたSPAMフィルター
+ BkASPil for Becky!2　(Becky!用のスパムメールプラグイン)
2011/2/13 サービス終了。ダウンロードもできないようなのであきらめた。
+ 深海魚フィルター　(Becky!用のスパムメールプラグイン)
2009/08/26 公開終了（窓の杜より）。あきらめ。
+ MSweeper　(Becky!用のスパムメールプラグイン)
「深海魚フィルター」の後継。ベイズ理論を使っているようなので良いかなと思ったが、有料（税込2,200円/年[執筆時点]）なのであきらめた。
+ BkBsfilWrapper　(Becky!用のスパムメールプラグイン)
日本語対応の学習型ベイジアン・スパム・フィルター bsfilter を使用したSPAMフィルター。
最新版は bsfilter-1.0.19（日付のタイムスタンプは2013/11/3）。これは ruby で書かれており、実行版のbsfilter.exe は同梱されていなかった。その前の bsfilter-1.0.17（日付のタイムスタンプは2010/11/20）には、bsfilter.exe が同梱されていたが、最新版でなく怖いので bsfilter-1.0.17 はあきらめ。bsfilter-1.0.19 を動かすために ruby を入れるのも躊躇し、あきらめた。
+ POPFile
これは、Becky!用のスパムメールプラグインではなく proxy型の自動メール振り分けツール。SPAMフィルターとして使用できる。最新版は 1.1.3（2011/9/6）で古い。
以前使用経験あり。
## 採用したSPAMフィルター
結局、POPFileを使用することにした。
ダウンロード、設定はトラブルなく行えた。古いけど問題なく作動中である。
以上
popstateが動かない事象についてのまとめ

## 初めに
ブラウザバックされないように```postate```で検知して処理をしようしているのに、検知する時としない時の違いがわからず苦戦しました。
今回はその原因究明と解決策をまとめておきます。

## ChromeやAndroidWebViewではpopstateは動かない
厳密にはクリックやタップなどのユーザーアクションが実行されない限り、popstatは動きません。
これはセキュリティ観点の問題であり、Chrome はデフォルトの「戻る」ボタン動作のハイジャックを意図的に防止しているからです([こちら](https://news.sophos.com/en-us/2019/05/09/chrome-plans-to-save-you-from-sites-that-mess-with-your-back-button/)で言及されています)

## vue3で困ったこと
VueアプリケーションはSPAであり、vue内で何度パスが変わっても単一ページとして一回でもユーザーがクリックすれば、その後の　popstateは検知してくれました。
しかし、外部サイトから遷移したときに、外部サイトに戻れないようにするためにpopstateでブラウザバックを検知させようとしても、今までのクリックイベントはリセットされてしまうので、再度クリックしないと検知してくれません。
なので、外部サイトから遷移してきたユーザーが、クリックせずにブラウザバックすると戻れてしまうのでとても困りました。

## 解決策を考えてみた
外部サイトと遷移先ページの間にダミーページを挟み、外部サイトから遷移したダミーページでHistoryAPIのpushを使って、目的のページに遷移させようとしました。そうすることでブラウザバックしても、いったんはダミーページを挟むので、そこで何かしらの処理をしようと考えました。

しかしこの方法は解決に繋がりません。
なぜなら、JSのHistoryAPIを用いて履歴操作された追加ページも、その間ユーザーがクリックしていなければ、ブラウザバックしても無視されてしまうからです。(aページ→bページ(ここでcページにpush)→cページ→ブラウザバックするとaページへ遷移)※1
vue-routerも内部的にはHistoryAPIに依存しているんですね。※2
[※1](https://groups.google.com/a/chromium.org/g/blink-dev/c/T8d4_BRb2xQ/m/WSdOiOFcBAAJ) [※2](https://github.com/vuejs/router/blob/c5c191585540e3fec2325a704903d0b5471f26e1/packages/router/src/history/html5.ts#L263-L307)

## 解決策
alertや戻るボタンを独自で作って、ユーザにクリックを促すようにするしかない。です笑
AWS Lambda × Ruby 「ランタイムに合わせたbundle install面倒くさい...」「デプロイパッケージが大きすぎて...」という困り事

# AWS Lambdaでライブラリを利用するのって地味に面倒くさい
AWS lambdaでライブラリを利用するときに面倒だと思った以下2点についての対処法
- zipフォルダを用意するまで
- 「デプロイパッケージが大きすぎて、インラインコード編集を有効にできません。...」の回避

## zipフォルダを用意するまで
### Dockerfileを準備
```Dockerfile:Dockerfile
# AWS Lambdaで利用するランタイムのバージョンを指定する
FROM ruby:2.7

WORKDIR mkdir /var/www

# /var/wwwの部分は/appなどなんでも良い
COPY /app/* /var/www

RUN bundle config --local set path 'vendor/bundle'

RUN bundle install
```
### docker container を起動
`$ docker image build -t XXX:YYY`
`XXX`のところは`sample_app`
`YYY`のところは`latest`
などでよい

```
$ docker container run -it -p 8000:8000 --name sample_app sample_app:latest

root@b1b2ee43a8bd:/# 
```
### bundle install

```
$ cd var/www

$ bundle config --local set path 'vendor/bundle'

$ bundle install
```
### Docker Desktop for Macでライブラリのフォルダをダウンロードする
該当のcontainerを選択
![スクリーンショット 2023-10-06 12.01.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427079/ec234667-c5e0-f56b-e468-032e04bb6329.png)

![スクリーンショット_2023-10-06_9_56_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427079/bd07a2ed-54d6-415a-06ef-d88b55eccab3.png)
### zipにしてアップロード
ドキュメントに記載されている通りのフォルダを作成する
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/packaging-layers.html

![スクリーンショット 2023-10-06 12.19.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427079/84158bbd-94cd-4972-24c7-b63e3ff6bec1.png)


##  「デプロイパッケージが大きすぎて、インラインコード編集を有効にできません。...」の回避
### その他のリソース＞レイヤー＞レイヤーの作成
![スクリーンショット_2023-10-06_12_20_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427079/85e3435f-3062-c528-5d35-98f85178193b.png)

### ライブラリのフォルダをzipにしてアップロード
![スクリーンショット 2023-10-06 12.21.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427079/72b2d24b-4d25-7d2d-d96b-f259df46406b.png)

### レイヤー＞レイヤーの追加
![スクリーンショット_2023-10-06_12_21_55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427079/4489c8d0-0b58-f17d-9677-ee2e7ba2935e.png)

![スクリーンショット_2023-10-06_12_22_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427079/3b098bfb-7dbb-2acf-18dc-df009acf544f.png)

レイヤーが追加されていることを確認
![スクリーンショット_2023-10-06_12_23_50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/427079/3f64ca3d-368c-2ce9-1d2e-2377d729aecd.png)
[23c SQL新機能] テーブルごとのアクセス権付与がすごく便利に！

## 環境
・Oracle Database 23c
　※DBエディションやOSによって差異はなし

## やれること
23cより前までは、テーブルごとにSELECT権限を付与する必要がありました。
23cでは、1つのSQLで一括付与できます。
また権限付与したあとに作られたテーブルであっても自動付与されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2551183/0711f908-098c-38bd-3370-e07a3b3863cd.png)


## 構文
:::note info
GRANT select any table
  ON schema <テーブル所有ユーザ名> TO <権限付与先ユーザ名>;
:::


## 実行例

#### (1) テーブル所有者（objuser）のテーブル確認
table11,table12,table13が存在することを確認します。
table14はありません。
```
SQL> connect objuser/Password_1_2_3@db23
Connected.

SQL> select * from table11;

        ID
----------
        11

SQL> select * from table12;

        ID
----------
        12

SQL> select * from table13;

        ID
----------
        13

SQL> select * from objuser.table14;
select * from objuser.table14
                       *
ERROR at line 1:
ORA-00942: table or view does not exist
Help: https://docs.oracle.com/error-help/db/ora-00942/
```


#### (2) SELECTユーザ（seluser）から参照できないことを確認
ALL_TABLESビューを参照し、参照できないことを確認します。
念のため、table11をSELECTしてみます。
```
SQL> connect seluser/Password_1_2_3@db23
Connected.

SQL> select table_name from all_tables where owner='objuser';

no rows selected

SQL> select * from objuser.table11;
select * from objuser.table11
                       *
ERROR at line 1:
ORA-00942: table or view does not exist
Help: https://docs.oracle.com/error-help/db/ora-00942/

```

#### (3) SELECT権限の一括付与
objuserが所有するテーブルに対するSELECT権限を一括付与します
```
SQL> connect objuser/Password_1_2_3@db23
Connected.

SQL> grant select any table on schema objuser to seluser;

Grant succeeded.
```

#### (4) SELECTユーザ（seluser）から参照できるを確認
ALL_TABLESビューを参照し、参照できるようになったことを確認します。
念のため、テーブルのSELECTも行います。
table14はまだ存在しないため参照できません。
```
SQL> connect seluser/Password_1_2_3@db23
Connected.

SQL> select table_name from all_tables where owner='objuser';

TABLE_NAME
---------
TABLE11
TABLE12
TABLE13

SQL> select * from objuser.table11;

        ID
----------
        11

SQL> select * from objuser.table12;

        ID
----------
        12

SQL> select * from objuser.table13;

        ID
----------
        13

SQL> select * from objuser.table14;
select * from objuser.table14
                       *
ERROR at line 1:
ORA-00942: table or view does not exist
Help: https://docs.oracle.com/error-help/db/ora-00942/

```


#### (5) テーブル所有者（objuser）に新しいテーブルを作成します。
table14を作成します。
table14に対するSELECT権限の付与操作はここでは行いません。
```
SQL> connect objuser/Password_1_2_3@db23
Connected.
SQL> create table table14 (id number);

Table created.

SQL> insert into table14 values(14);

1 row created.

SQL> commit;

Commit complete.
```


#### (6) 新しく作成されたテーブルもSELECTユーザ（seluser）から参照できるを確認 
table14に対するSELECT権限の付与操作を行いませんでしたが
スキーマに対して一括設定されているため、table14も参照できるようになりました。
```
SQL> connect seluser/Password_1_2_3@db23
Connected.
SQL> select table_name from all_tables where owner='objuser';

TABLE_NAME
--------------------------------------------------------------------------------
TABLE11
TABLE12
TABLE13
TABLE14

SQL> select * from objuser.table14;

        ID
----------
        14

SQL>
```
URLを指定してダミー画像を表示できるサービス

🌻URLを指定してダミー画像を表示するサービス

💡Lorem Picsum
https://picsum.photos/

~~~
<img src="https://picsum.photos/200/300">
~~~
こうやって指定するだけで、👇のようにダミー画像が表示できます！

<img src="https://picsum.photos/200/300">

・サイズ指定できます！
・ランダム表示できます！
・特定の画像を表示することもできます！
・グレースケール、ぼかしもできます！
・おしゃれな写真がたくさん！

めちゃ便利！！


これで、ダミー画像を表示するとき

・どこかで良い感じの画像を探してきて
・ダウンロードして
・正しいフォルダに配置して
・パス指定して
　（パス間違えて表示されないまでセット）
・めんどくさーーーーい！！

となるのを防げます！！

## 使い方

### サイズ指定、ランダム画像
~~~
<img src="https://picsum.photos/200/300">
~~~
https\://picsum.photos/(幅)/(高さ)

→「https\://picsum.photos/」に続けて、幅と高さを指定しよう！

<img src="https://picsum.photos/200/300">


### サイズ指定（正方形）、ランダム画像
~~~
<img src="https://picsum.photos/200">
~~~
https\://picsum.photos/(一辺の長さ)

→「https\://picsum.photos/」に続けて、一辺の長さを指定しよう！

<img src="https://picsum.photos/200">


### 画像ID指定
~~~
<img src="https://picsum.photos/id/237/200/300">
~~~
https://picsum.photos/id/(画像ID)/(幅)/(高さ)
https://picsum.photos/id/(画像ID)/(一辺の長さ)


→「https\://picsum.photos/id/」に続けて、画像のID、サイズを指定しよう！

画像のIDは👇から調べられます。
https://picsum.photos/images

<img src="https://picsum.photos/id/237/200/300">


### グレースケール
~~~
<img src="https://picsum.photos/200?grayscale">
~~~
https\://picsum.photos/(お好みの指定)?grayscale

→末尾に「?grayscale」を付けよう！

<img src="https://picsum.photos/200?grayscale">


### ぼかし
~~~
<img src="https://picsum.photos/200?blur">
~~~
https\://picsum.photos/(お好みの指定)?blur

→末尾に「?blur」を付けよう！

<img src="https://picsum.photos/200?blur">



試してみてね！
辞書（dict型）でkey指定で値を取得するのとget()メソッドを使うのは何が違うのか【Python】

Pythonのコード改修をしていて`dict.get('key3').get(key3f)`のような記述があり、「なんでや！`dict['key3']['key3f']`でええやろ！`.get`の4文字余分やろ！」と思ったので、なぜこういったコーディングになるのかの理由を調べた。

# 結論
keyで指定すると`KeyError`になりエラーハンドリングしていない場合は途中終了するが、`get()`の場合は`None`が返ってくるため、場合によっては途中終了しない。

>##### get(key[, default])
>Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.

（[公式ドキュメント](https://docs.python.org/3.12/library/stdtypes.html#mapping-types-dict)より）

# 検証
以下、検証。

## 環境
```
$ python --version
Python 3.12.0
```

## key指定する場合
以下のようなdictが入ったlistをの要素をprintするだけのコードを実行してみる。
```python:print_dict_by_key.py
places = [
    {
        'name': '東京',
        'attraction': '東京スカイツリー'
    },
    {
        'name': '神奈川',
        'attraction': '横浜中華街'
    },
    {
        'name': '栃木'
    }
]

for place in places:
    print(place['name'])
    print(place['attraction'])
```
### 実行結果
栃木にはattractionの要素がないため`KeyError`となる。
```
$ print_dict_by_key.py
東京
東京スカイツリー
神奈川
横浜中華街
栃木
Traceback (most recent call last):
  File "file_to_path\print_dict_by_key.py", line 17, in <module>
    print(place['attraction'])
          ~~~~~^^^^^^^^^^^^^^
KeyError: 'attraction'
```

## get()メソッドを使う場合
以下のようにprint部分を変更
```python:print_dict_by_get.py
places = [
    {
        'name': '東京',
        'attraction': '東京スカイツリー'
    },
    {
        'name': '神奈川',
        'attraction': '横浜中華街'
    },
    {
        'name': '栃木'
    }
]

for place in places:
    print(place.get('name'))
    print(place.get('attraction'))
```
### 実行結果
`None`が出力され、エラーにならない。
```
$ python rint_dict_by_get.py
東京
東京スカイツリー
神奈川
横浜中華街
栃木
None
```
# 検証からわかること
dict内に指定したkeyが存在することが保証されている場合は（コードの見やすさ的にも）key指定の方が良いが、そうでない場合は`get()`を使った方が安全と言える。

例えば、外部サービス等からdictが渡ってきて、それをログにひたすらprintするシステムがあり、渡ってくるdictの形式が状況によって変動する、もしくは渡ってくるdictの全容がそもそもわからない、という場合など。

## ハンドリングのしやすさ
`get()`の場合は`None`が返ってくるので、以下のような方法を使えばわざわざ`try-except`しなくて済む。

:::note warn
この`None`は`<class 'str'>`ではなく、`<class 'NoneType'>`なので注意。
:::
### ①if分岐
`None`が返ってくるとなると以下のifを思いつくだろう。
```python:print_attraction.py
places = [
    {
        'name': '東京',
        'attraction': '東京スカイツリー'
    },
    {
        'name': '神奈川',
        'attraction': '横浜中華街'
    },
    {
        'name': '栃木'
    }
]

for place in places:
    print('----')
    print('Q：' + place.get('name') + 'の魅力は？')
    if place.get('attraction') is None:
        print('- ないんだな、それが')
    else:
        print('- ' + place.get('attraction'))
```
#### 実行結果
```
$ python print_attraction.py
----
Q：東京の魅力は？
- 東京スカイツリー
----
Q：神奈川の魅力は？
- 横浜中華街
----
Q：栃木の魅力は？
- ないんだな、それが
```


### ②get()メソッドのdefaultを使う
get()メソッドは第二引数にデフォルト値を渡すことができ、`None`が返ってきた時にデフォルト値を出力するようにすることも可能。

上記コードのループ部分を以下のように書き換えても同様の処理になる。
```python
for place in places:
    print('----')
    print('Q：' + place.get('name') + 'の魅力は？')
    print('- ' + place.get('attraction', 'ないんだな、それが'))
```
コードがスッキリするためこっちの方が好み。

# まとめ
dictのkey状態がよくわからなかったら`get()`の方が安全だが、`<class 'NoneType'>`なところは注意が必要そう。

## 参考
- [Mapping Types — dict - Python 3.12.0 documentation](https://docs.python.org/3.12/library/stdtypes.html#mapping-types-dict)
【Python3.9】Pyintallerでコンパイルされた.pyファイルをデコンパイルする方法

## Python3.9をデコンパイルする方法がわからない
Python3.9でコンパイルされたexeファイルをコードに戻す方法が見当たらない!
そんな悩みを抱えた私がひたすら調べたこと・成功した方法をまとめていく

## 方法
### pyinstxtractorをダウンロードしよう
下記のGithubからzip形式でダウンロードし解凍しておいてください
https://github.com/WithSecureLabs/python-exe-unpacker

### exeファイルを展開しよう
先ほどダウンロードしたファイル内から「pyinstxtractor.py」を見つけてください。
そのファイルを使って展開します。
```cmd:cmd
>>cd "pyinstxtractorのカレントディレクトリのパス"
>>python pyinstxtractor.py "展開したいexeファイルのパス"
```
実行して「You can now use a python decompiler on the pyc files within the extracted directory」と表示されれば完了です。exeファイルのカレントディレクトリに「〇〇.exe_extracted」というフォルダが生成されているので、開いてください。

そして「exeファイル名」だけの拡張子のないファイルがあるので「exeファイル名.pyc」と拡張子をつけてあげてください。


### 欠損値の補完
exeファイル名.pycの状態ではバイナリデータが欠損しており、このままデコンパイルするとエラーになります。ここでは詳しいやり方は省略しますが下記のサイトがとても分かりやすいので参考にしてください。しかしデコンパイルの方法は違うので、欠損値を補完したらこのサイトに戻ってきてくださいね。
https://manumaruscript.com/exe-decompile/

### デコンパイル
まず初めに下記のライブラリをインストールします。
```:cmd
>>pip install pydumpck
```

インストールが完了したら、インストールしたライブラリのフォルダを開きます。
```:cmd
>>where python
C:\Users\ユーザー名\AppData\Local\Programs\Python\Python37\python.exe
```
実行するとpythonの実行ファイルのパスが出てくるので
「C:\Users\ユーザー名\AppData\Local\Programs\Python\Python37\」に「Lib\site-packages\pydumpck\pyc_checker\lib_pycdc\pycdc.exe」を加え、パスを作成します。

そのパスを下記のようにcmdに入力し、デコンパイルを行います。
```:cmd
>>C:\Users\ユーザー名\AppData\Local\Programs\Python\Python37\Lib\site-packages\pydumpck\pyc_checker\lib_pycdc\pycdc.exe exeのファイル名.pyc
```

実行するとコンソール上にソースコードが表示されるはずです。
もし一部文字化けしている場合は、
```:cmd
>>chcp 65001
```
と入力し、再度デコンパイルを行うと正しく表示されるかと思います。


### 番外編
デコンパイル中に下記のエラーが発生した場合は、バイナリでPythonのバージョンを表す先頭2文字を「61」から「55」に変更することで解決する可能性が高いです。55はPython3.8のバージョンを表しています。
![スクリーンショット 2023-10-25 200357.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553823/6101ea62-bbd5-eac4-64e3-2746015bcaa6.png)


## まとめ
久しぶりに作成したので綺麗ではありませんが、皆さんのお役に立つことを願っています。
【Ruby on rails】グループ作成　その２　グループ参加と退出

# はじめに
プログラミングを勉強して2か月ほどです。
初心者なので間違っているところがあれば、ご指摘いただけると幸いです。
※@ki_87(kei i)さんの記事を参考に作成しました。
　とても分かりやすく参考になりました。
　ありがとうございます。

# グループの参加と退出
今回は作成したグループへの参加と退出を実装していきます。
グループの作成については下記の記事で行っています。

https://qiita.com/kenty1116/items/2807833bc375ead197af

それでは作成していきましょう！！！

# Let's start!!!
## ルーティングの設定
```routes.rb
Rails.application.routes.draw do
  devise_for :users
  root to: "groups#index"
  resources :groups do
    get "join" => "groups#join"
    delete "exit" => "groups#exit"
  end
end
```
## コントローラーの記述
```groups.controller.rb
class GroupsController < ApplicationController
  
  before_action :set_group, only: [:show, :edit, :update, :destroy]

  def index
    @groups = Group.all
  end

  def new
    @group = Group.new
  end

  def create
    @group = Group.new(group_params)
    @group.owner_id = current_user.id
    if @group.save
      redirect_to root_path
    else
      render :new, status: :unprocessable_entity
    end
  end

  def show
  end

  def join
    @group = Group.find(params[:group_id])
    unless @group.users.include?(current_user)
      @group.users << current_user
    end
  end

  def update
    if @group.update(group_params)
      redirect_to group_path(@group)
    else
      render :edit, status: :unprocessable_entity
    end
  end

  def exit
    @group = Group.find(params[:group_id])
    @group.users.delete(current_user)
    redirect_to root_path
  end

  private

  def group_params
    params.require(:group).permit(:group_name, :introduction, :group_image).merge(user_id: current_user.id)
  end

  def set_group
    @group = Group.find(params[:id])
  end
end

```
`@group.users << current_user`
この記述により、@group.usersにcurrent_userを追加しています。
`<<`はシフト演算子と言われる演算子のようです。
私も詳しくは説明できませんが、`<<`の記述であれば右に書かれた記述が左にシフトする(左に追加される)というイメージで問題ないと思います。

`before_action :set_group, only: [:show, :edit, :update, :destroy]`
この記述は`@group = Group.find(params[:id])`が複数のアクションで使用されているので`set_group`にまとめて`before_action`で呼び出しています。

# Rails7での注意点
退出ボタンを実装する際に注意点が必要です。
`<%= link_to "Exit", group_exit_path(group.id), data: { turbo_method: :delete }, class: "btn btn-purple mx-3" %>`
上記の記述の中で`data: { turbo_method: :delete }`がポイントになります。
Rails7より前のバージョンでは`method: :delete`という記述でdestoroyアクションが実行されたようですが記述方法が変わっているので注意です！

#### data: { turbo_method: :delete }とは
そもそも、なぜ`destroy`アクションが実行されるときに`data: { turbo_method: :delete }`という記述が必要なのか？
この記述をしないと、httpメソッドの`get`が呼び出されてしまうからのようです。`data: { turbo_method: :delete }`と記述することで`delete`が呼び出されます。


# 終わりに
みなさん実装お疲れ様でした！！！
グループへの参加と退出が実装出来たかと思います。
分かりづらい点が多かったと思いましたが、最後まで読んでいただきありがとうございました！！
EinhornでSidekiqをマルチプロセス化する

この記事は
===

* Sidekiqはとてもよくできているワーカサーバなのですが、シングルプロセスでしか動作しません
* さらにRubyは1プロセス1CPUコアしか使えないので、結果```Sidekiqは1コアしか使えない```という弱点が発生します
* [einhorn](https://github.com/contribsys/einhorn)というなんでもかんでもマルチプロセス化してしまう神ライブラリを使うと、簡単にSidekiqの弱点を補えます

einhorn?
===

* 簡単にいうとUnicornやPumaみたいな、master/worker形式のマルチプロセス化をしてくれるライブラリです
* 今回はSidekiqに使いますが、Rubyに限らず、どんなプログラムでも対応可能です
* サンプルだと```sleep```コマンドをマルチプロセス化していて動きが面白いです

インストール
===

* Rubyでできています

```ruby:Gemfile
gem 'einhorn'
```

起動
===

* ```einhorn```コマンドを頭につけるだけなのでとても簡単です
* ```-n```オプションで生成したいプロセスの数を指定します

```bash:
bundle exec einhorn -n 2 sidekiq -C config/sidekiq.yml
```

運用
===

* 起動すると管理用のmasterプロセスが1個、処理本体のworkerプロセスがn個上がってきます
* workerプロセスが死ぬと、自動でmasterプロセスがworkerプロセスを上げ直してくれます
* masterプロセスへ```SIGTERM```を送ると、順次workerプロセスへ```SIGTERM```を伝搬してシャットダウンしてくれます

ちなみに
===

* SidekiqにはProとEnterpriseという有償版もあり、そちらはマルチプロセスに対応しているのですが、中身はeinhornなんだそうです
* そういう意味では公式お墨付きという感じですね

終わりに
===

* einhornはpython系サーバとかnode系サーバとか、他にも色々使い道あるかもしれないですね
【恥メモ】自己目標設定前の準備段階のメモ

    • 【過去の自分】前期の自己評価とマネージャーフィードバックと最終結果を見返してみる
        ○ 今後も継続してほしいことはなにか？
            § 前回はなぜこれができた？
            § 今後継続する機会はあるか？
        ○ 今後チャレンジが必要なことはなにか？
            § 前回はなぜこれができなかった？
            § 今後チャレンジする機会はあるか？
    • 【定義と比較１】現レベルの期待役割を確認する
        ○ 現時点でチャレンジが必要なことはあるか？
            § 今後チャレンジする機会はあるか？
    • 【定義と比較２】次レベルの期待役割を確認する
        ○ 現時点でまだできていないことはあるか？
            § 今後チャレンジする機会はあるか？
    • 【自分を見る】セルフブランディングを考える
        ○ 「●●といったら私」に入る●●はなにか？
        ○ これまで働いてきて夢中になれたものはなにか？
        ○ これまで働いてきて達成感を感じたことはなにか？
        ○ 逆にどうしても苦手なことはなにか？
        ○ 身近にいる「この人のようになりたい」は誰か？
    • 【周りを見る】身の回りの人たちからの期待役割
        ○ PJや部の状況を考え、周りは自分にどんな振る舞いを期待している？どうなってほしいと思っている？
    • 【未来を見る】1年後にどういうフィードバックを受けていることが理想か具体的に考えてみる
        ○ その時マネージャーからどういうコメントをもらっている？
        ○ その時自分自身はどういう気分になっている？

なんでこの黒いフォーマットになってしまったのかはわからない。。。
npm run serveとnpm run devの違い

# npm run serve？ npm run dev?
VueでWebアプリケーションを開発していた際にフロントエンドのデバッグとして`npm run serve`と`npm run dev`があることを知った．
両者の違いを備忘録として残す．

#### 先に結論
##### npm run serveを使う時 
`Vue CLI`でプロジェクトを作った場合
##### npm run devを使う時
`vite`でプロジェクトを作った場合

## Vue CLI と viteって何
`Vue CLI`と`vite`はどちらも**Vueアプリケーションのビルドツール**

#### Vue CLI
Vueでプロジェクトを作る際に
```
vue create プロジェクト名
```
のコマンドを使う場合は**Vue CLI**によって構成される

:::note warn
注意
**Vue CLI**でプロジェクトをビルドしたい場合は以下のコマンドで**Vue CLI**をインストールしておく
:::
```
npm install -g @vue/cli
```

#### vite
Vueでプロジェクトを作る際に
```
npm create vue@latest
```
のコマンドを使う場合は**vite**で構成される．

## ちなみに
ターミナルもしくはコマンドラインで
```
npm run
```
を実行すると，ビルドツールの使い方がわかる．

#### Vue CLIでの例
![スクリーンショット 2023-10-06 11.24.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629034/d66bb1cc-bd33-9517-766d-27d82fc2225d.png)

## 結論
なんで`npm run serve`でデバッグできないの!!となったら，
一旦`npm run`を走らせて自分のビルドツールを確認しましょう．
どうやらviteを使った方が高速化されるらしいので，主流はviteなんですかね?:expressionless:
【恥メモ】独り立ちしている人の意識・姿勢・スキル図解

思いつきで書きました。メモです。
![PXL_20230328_015615539.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3329659/437e62b6-4b7e-6b94-2c63-ef8ace4bb871.jpeg)

なんか足りないものがあるかな？
デザインパターン　Observerパターン

# Observerパターン
変化するデータや状態に関心を持つオブザーバ（観察者：**Observer**）と、その変化を通知するサブジェクト（観察対象：**Subject**）から構成される。

一対多の依存関係を定義し、あるオブジェクト（サブジェクト）の状態が変化すると、それに依存する複数の他のオブジェクト（オブザーバ）に自動的に通知される。

## サブジェクト（Subject）
観察（Observe）の対象となる情報。

**状態が変化する**。

観察対象となるデータの唯一の所持者であり、観察対象に何らかの変更があった場合、オブザーバに対してその変更を**通知する**。

また、オブザーバを管理する（オブザーバの新規登録や、オブザーバの解除）役割も併せ持つ。


サブジェクトがオブザーバについて知っているのは、`Observer`インターフェースを実装しているということだけ。通知した情報がどのように利用されるのかなどは一切知らない（**疎結合の関係**）。

<<学習memo：発行者（Publisher）が管理している対象をサブジェクト（Subject）と言っているのかと思っていましたが、サブジェクト自身が発行者であるという理解の方が、定義的には正しいのかもしれません。または、Publish-Subscribeパターンというものも存在し、Publish-Subscribeパターンでは発行者が、この記事でいうサブジェクトの役割を果たすようです。>>

## オブザーバ（Observer）・リスナー（Listener）
対象となる情報を観察（Observe）する主体。

購読者、サブスクライバ、リスナーとも呼ばれる。

サブジェクトの更新通知を**受信する**。

観察情報の購読（サブスクライブ）は、`Subject`が提供する`registObserver()`によって行う。観察対象を購読することで、オブザーバになることができる。

反対に、`removeObserver()`によって、途中で購読を解除して、オブザーバを辞めることもできる。

## 必要なインターフェース
```java:Subject.java
public interface Subject {
    public void registObserver(Observer observer);
    public void removeObserver(Observer observer);
    public void notifyObservers();
}
```
```java:Observer.java
public interface Observer {
    public void doSomething(int data);
}
```
## インターフェースの実装
```java:SubjectSample.java
import java.util.ArrayList;
import java.util.List;

public class SubjectSample implements Subject {

    // サブジェクトはオブザーバを保持している
    private List<Observer> observers;

    // 観察対象の情報
    private int data = 0;

    public SubjectSample() {
        observers = new ArrayList<Observer>();
    }

    // 購読を開始する
    @Override
    public void registObserver(Observer o) {
        observers.add(o);
    }

    // 購読を解除する
    @Override
    public void removeObserver(Observer o) {
        observers.remove(o);
    }

    // 観察対象に変更があったことをオブザーバに対して通知する
    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            // 引数に更新データを渡しても良いし、
            // getData()などを用意して、更新があったことだけを通知し、オブザーバ側から取得させても良い。
            observer.doSomething(data);
        }
    }

    // 観察対象のデータが変更される処理
    public void updateData(int newData) {
        data = newData;
        notifyObservers();
    }
}
```
```java:ObserverSample.java
public class ObserverSample implements Observer{

    // サブジェクトへの参照を保持しておくと、あとで解除も行うことができる
    private SubjectSample subject;

    public ObserverSample(SubjectSample subject){
        this.subject = subject;
        // 購読を開始する
        subject.registObserver(this);
    }

    // 観察対象に更新があった場合、サブジェクト側で呼び出されるメソッド
    @Override
    public void doSomething(int data) {
        System.out.println("情報が更新されたため、何らかの処理を実行します。");
        System.out.println("最新のデータは" + data + "です。");
    }
}
```
```java:Main.java
public class Main {
    public static void main(String[] args) {
        SubjectSample subject = new SubjectSample();
        ObserverSample observer = new ObserverSample(subject);

        // 観察対象の情報を変更する　→ サブジェクトが変更を通知　→ オブザーバが通知を受信
        subject.updateData(100);
        // >>情報が更新されたため、何らかの処理を実行します。
        // >>最新のデータは100です。
    }
}
```

# 参考
・<a href="https://amzn.asia/d/8MCJXIB" target="_blank" rel="noopener noreferrer">Head Firstデザインパターン ―頭とからだで覚えるデザインパターンの基本</a>
WEBスクレイピングする時のお作法まとめ

# WEBスクレイピングする時のお作法まとめ
WEBスクレイピングを利用すれば、ウェブページから自動的に情報を抽出することができます。

通常、WEBスクレイピングはプログラムやスクリプトを使用して行われ、ウェブページのコンテンツを解析し、特定のデータを抽出したり、データベースに保存したりします。この技術を使用すれば、手動でのコーディングなしに、Webサイトから迅速にデータを取得することができます。

# WEBスクレイピングは違法？
WEBスクレイピングの対象は、Webサイトに掲載されている情報です。

基本的には、Webサイトが公開している情報を参照しているだけであり、スクレイピングによる情報の収集自体は違法ではありません。

ただし、気をつけるポイントはありますのでポイントをまとめます。

# WEBスクレイピングする際に気をつけるべきポイント
WEBスクレイピングは非常に便利ですが、相手側のサービスに迷惑をかけないことを前提にサービス設計することが重要です。

スクレイピングを行う際に注意すべき事柄の一つに岡崎市立中央図書館事件があります。

> 岡崎市立中央図書館事件は、2010年3月頃に岡崎市立図書館の蔵書検索システムにアクセス障害が発生し、利用者の一人が逮捕された事件です。利用者は攻撃の意図は持っておらず、問題の原因は図書館側のシステムの不具合にあったことから大きな論議を呼び起こしました。逮捕された人物は取調べの後、Librahackというサイトを立ち上げて事件を解説したことから、Librahack事件とも呼ばれます。

開発者が開発したスクレイピングサービスで蔵書検索システムに「1秒に1アクセス」という頻度でアクセスし、情報を取得しようとしたところ、蔵書検索システムが特殊な仕様であったため、一般のユーザーがWebサイトからの閲覧ができない状態になりました。

このように、WEBスクレイピングを行い相手側のサービスに迷惑をかけることを避けるための注意が必要です。


## 2.できる限りAPIを利用する
WebサイトのHTML構造は、A/BテストやUIの改善のために頻繁に変更されることがあります。

その結果、HTMLの構造が変わるとスクレイピングが失敗する可能性が高まり、都度プログラムの修正が必要になる場面が増えます。

一方で、公開APIを利用する場合、リクエストとレスポンスの構造が安定しているため、プログラムでのデータ取得がスムーズに行えます。

スクレイピングを行いたいサイトがAPIを提供している場合、APIを利用する方が安定して運用できるので、その利用がおすすめです。


## 3.相手にもメリットがある状況にする
スクレイピングされる側のサイトにも何らかのメリットがある状況を作ることが重要です。

スクレイピングの行為だけでは、以下のようなデメリットを相手側に与える可能性があります：

**1. リクエストによるサーバー負荷**
**2. スクレイピングだけでは直接の収益に繋がらない**

一方、以下のような取り組みを行うことで、スクレイピングの対象となるサイト側にもメリットを提供できるかもしれません：

**1. リクエストがサーバーに負荷をかけないような方法でアクセスし、SEOの向上に寄与する**
**2. スクレイピングだけでなく、対象となるサイトのサービスを積極的に利用し、収益向上に貢献する**

スクレイピングを行う際は、単に情報を取得するだけでなく、相手側にも何らかのメリットを提供することを心がけると良いでしょう。

## 4.相手側の利用規約を事前に確認する
スクレイピングを行う前に、目的とするウェブサイトの利用規約を必ず確認しましょう。

多くのウェブサイトは、自らの利用規約の中でスクレイピングの行為に関するポリシーやガイドラインを定めています。

利用規約は法的効力を持つドキュメントであり、スクレイピングが明示的に禁止されているサイトをスクレイピングした場合、違反となります。

その結果、法的措置を取られる可能性もあるため、十分な注意が必要です。

## 5.負荷に配慮する
スクレイピングによって、目的とするウェブサイトのサーバーに過度な負荷をかけることは避けるべきです。短時間に大量のアクセスを行うと、サーバーに負荷がかかり、その結果サービスが停止することも考えられます。

そこで大事なのが相手側のサービスの負荷をできる限り抑えてスクレイピングすることです。

**1. スクレイピングの速度を調整する**
短時間で大量にアクセスすると、サーバーに大きな負荷がかかる可能性があります。そのため、アクセスの間隔をあける、夜間やトラフィックが少ない時間帯にスクレイピングを行うなど、負荷を軽減する工夫が必要です。

**2. 必要なデータのみを取得する**
無駄なページや情報を取得しないようにし、効率的にデータを収集することで、サーバーへの負荷を軽減できます。

このように相手側の負荷を考えてスクレイピングすることが重要です。


## 6.個人情報の取り扱いに注意する

>平成29年の改正個人情報保護法施行により、ほとんどすべての事業者が「個人情報取扱事業者」として個人情報保護法の適用を受けることになりました。この法律では、個人情報※1を取得する際には、利用目的を本人に明示する必要があります。スクレイピングで個人情報に該当する情報を取得する場合、各人に個別に利用目的を明示することは現実的ではないでしょうから、プライバシーポリシー等で利用目的をあらかじめ公表しておくことが違法とならないためには重要となります。
https://pig-data.jp/blog_news/blog/scraping-crawling/scrapinglaw/

個人情報は本人の同意の上でないと取得できないのでスクレイピングで個人情報を取得することはNGです。

個人情報はスクレイピングで取得しないようにしましょう。

# まとめ
WEBスクレイピングは、ウェブ上の情報を効率的に収集する手段として非常に有効ですが、それに伴うリスクや責任も大きいです。上記のポイントを踏まえながら、適切な方法でスクレイピングを行うことが求められます。
Laravel Requestで403 Forbiddenエラーが発生

初心者SEの備忘録。。

[発生状況]
Laravelにて、、
実行すると403 Forbiddenエラーが発生。
どうやら、バリデーションを行うために作成したFormRequestを使うとエラーが出るみたい。

```app\Http\Controllers\TestController.php
public function update(Int $id, TestRequest $request) 
//↑引数が、Request ＄requestだとエラーは出ない、TestRequest $requestだとエラーが出る
    {
        return $this->TestService->updateTestInfo($id, $request);
    }
```

Requestファイルは、コマンドで作成したもの。特に変なところはない。
```
php artisan make:request TestRequest
```

と思ったら、ここに原因があったーー！

[解決方法]
作成したRequestファイル内のauthorize()のreturnをfalse⇒trueにする。

```app\Http\Requests\TestRequest.php
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true; //ここをfalse⇒trueに
    }
```

authorize()：フォームリクエストの利用が許可されているかどうかを示すメソッド。
returnでtrueだと許可、falseだと未許可ということ、みたい。

以上。
またこのエラー出た、、、と毎回苦戦しているエラー紹介 with flutter

こんにちは！
今回は私がflutterで開発をしてきて、実際に起きたエラーを紹介したいと思います。
最後まで読んでいただけたら嬉しいです！

## ```The argument type 'List<String>' can't be assigned to the parameter type 'String'.```
### 原因
このエラーは、List<String> のオブジェクトをString のパラメータに直接割り当てようとした場合に発生します。つまり、データ型が一致していないことを示しています。特に関数やメソッドの引数に対して、期待された型と異なる型のデータを渡した場合によく発生します。関数が文字列を受け入れることを期待しているのに、リスト（複数の文字列のコレクション）を渡そうとしていることが原因です。

### 解決策
・関数を呼び出すときに、String型のデータを渡すように修正します。リスト内の文字列を抽出して渡すか、リストの代わりに文字列を使う必要があります。
・関数がリストを受け入れるように設計されている場合、関数のパラメータをList<String>型に変更することが必要です。関数が文字列を受け入れる場合、関数の定義を変更して、リストを受け入れるようにします。


## ```The following FormatException was thrown building 〜(・・・) ```

### 原因
このエラーは、文字列から別の型に変換しようとしたときに発生する一般的なエラーです。エラーメッセージは、特定の文字列が予期しない形式である場合に投げられます。具体的には、文字列をパースして数値や日付などの別のデータ型に変換しようとしたとき、文字列の形式が期待されていない場合にこのエラーが発生します。

### 解決策
・エラーメッセージを確認
・文字列の形式を確認
・例外処理を追加
・データのバリデーション
・デバッグ

## ``` A value of type 'Null' can't be assigned to a parameter of type 'String' in a const constructor.Try using a subtype, or removing the keyword```

### 原因
このエラーは、 const コンストラクタを使用する際に、文字列型のパラメータに null 値を渡そうとした場合に発生します。const コンストラクタはコンパイル時に評価されるため、パラメータには定数である必要があります。

### 解決策
・nullチェック: パラメータにnullが渡される可能性がある場合、nullチェックを追加してください。これにより、nullでないことが確実になります。
・const コンストラクタを非 const コンストラクタに変更: パラメータに null を許容する必要がある場合、const コンストラクタを使用するのではなく、通常のコンストラクタを使用してください。通常のコンストラクタでは実行時に評価されるため、null を受け入れることができます。


## 終わり
以上で、私がよく苦戦しているエラーの紹介について終わります。
今回も読んでいただきありがとうございました！
では、また次の記事で！
Decimalの小数の表示

## Decimalの仕様

##### 範囲

**79,228,162,514,264,337,593,543,950,335**
から
**-79,228,162,514,264,337,593,543,950,335**
まで。

##### 最大桁数
28から29桁の数字

## 小数について
[小数](https://ja.wikipedia.org/wiki/%E5%B0%8F%E6%95%B0)

.NETのDecimalは、整数部1桁の表示は必要と思われます。そのため、小数は、小数第28位まで表示できるようです。

## カスタム数値形式文字列

- 整数部は3桁ごとに「,」で区切る。
- 小数部は省略せず数字がある桁は全て表示する。ただし、小数部の数字は0で終わらない。

このように表示できる、カスタム数値形式文字列は、次です。

```
{0:#,0.############################}
```

整数部は、#,0で、3桁ごとに「,」で区切り表示します。
小数部は、「.」以下に「#」が28個あります。
これにより、次のように、整数部最小1桁・小数部最大28桁、あわせて最大29桁の数字を表示できます。

```
0.1234567890123456789012345678
```

## コード

```C#
//整数部1桁、小数第4位までの数値
Console.WriteLine(0.1000); //doubleの場合
Console.WriteLine(0.1000m);
Console.WriteLine(0.1000m.ToString("#,0.############################"));
Console.WriteLine(0.1000m.ToString("#,#.############################"));
//0.1
//0.1000
//0.1
//.1


//整数部1桁、小数第28位までの数値
Console.WriteLine(0.1234567890123456789012345678m);
Console.WriteLine(0.1234567890123456789012345678m.ToString("#,0.############################"));
Console.WriteLine(0.1234567890123456789012345678m.ToString("#,#.############################"));
//0.1234567890123456789012345678
//0.1234567890123456789012345678
//.1234567890123456789012345678


//整数部1桁、小数第29位までの数値
Console.WriteLine(0.12345678901234567890123456789m);
Console.WriteLine(0.12345678901234567890123456789m.ToString("#,0.############################"));
Console.WriteLine(0.12345678901234567890123456789m.ToString("#,#.############################"));
//小数第29位の「9」が四捨五入されて、小数第28位の「8」が「9」になります。
//0.1234567890123456789012345679
//0.1234567890123456789012345679
//.1234567890123456789012345679


Console.WriteLine(0.1234m);
Console.WriteLine(0.1234m.ToString("#,0.############################"));
Console.WriteLine(0.1234m.ToString("#,#.############################"));
//0.1234
//0.1234
//.1234


Console.WriteLine(1234.5678m);
Console.WriteLine(1234.5678m.ToString("#,0.############################"));
Console.WriteLine(1234.5678m.ToString("#,#.############################"));
//1234.5678
//1,234.5678
//1,234.5678


//整数部9桁、小数第21位までの数値
Console.WriteLine(123456789.012345678901234567899m);
Console.WriteLine(123456789.012345678901234567899m.ToString("#,0.############################"));
Console.WriteLine(123456789.012345678901234567899m.ToString("#,#.############################"));
//小数第21位の「9」が四捨五入されて、小数第20位の「9」が「10」となり一桁繰り上がり、小数第19位の「8」が「9」になります。
//123456789.01234567890123456790   //小数部が0で終わる
//123,456,789.0123456789012345679   //小数部は0で終わらない
//123,456,789.0123456789012345679



//整数部28桁、小数第4位までの数値
Console.WriteLine(1234567890123456790123456790.1234m);
Console.WriteLine(1234567890123456790123456790.1234m.ToString("#,0.############################"));
Console.WriteLine(1234567890123456790123456790.1234m.ToString("#,#.############################"));
//小数第1位まで表示されます。
//1234567890123456790123456790.1
//1,234,567,890,123,456,790,123,456,790.1
//1,234,567,890,123,456,790,123,456,790.1
```

### 参考
- [Decimal Struct](https://learn.microsoft.com/en-us/dotnet/api/system.decimal?view=net-6.0)
- [浮動小数点数値型 (C# リファレンス)](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types)
mongoDB-shellの日本語文字化けでくっそ苦労した

# 環境

- 移行元

  - OS:Ubuntu 22.06

  - DB:mongo 4.0.3

- 移行先

  - OS:Amazonlinux 2

  - DB:mongo 4.0.3

- クライアント
  - OS:windows 10
  
  - Shell:Powershell


# 概要

- mongoDump、restoreを使ってデータベース移行を行ったら移行先のmongoShellで文字化けが発生した。

# 事象の整理

| DB     | AP           | ターミナル   | 接続方法 |
| ------ | ------------ | ------------ | -------- |
| 移行元 | 文字化けなし | 文字化けなし | SSH      |
| 移行先 | 文字化けなし | 文字化けあり | AWS SSM  |

# なぜ移行元は文字化けしなかったのか

- おそらくOpenSSHの文字コードを使ってる

# なぜ移行先は文字化けしたのか
- PowerShellがshift-jisだったから

# まとめ
- 同じShellで片っぽが文字化けしていなかったのでPowerShellの文字コードは問題ないと思っていた。

# WindowsはShift-jisをいつまで使うねん。勘弁してくれ
Arduino IDEのエラー　ModuleNotFoundError: No module named 'serial'　の個人的備忘録

## 原因
Arduino IDEはpythonを使っている

普通に"pip install pyserial"してもpython3(anaconda)にインストールされる
Arduino IDEがanacondaのpythonとOSに元々入ってるpython3とpython2のどれを使っているかは知らないが
3つそのどれかにpyserialがないため（←今回はOS純正のpython3だった）

## python2にpipがあるか確認
```
python2 -m pip --version
```

## 無い時
```
python2
```
でpython2.xのマイナーバージョン確認

```
sudo curl -kL https://bootstrap.pypa.io/pip/2.7/get-pip.py | python2.ｘ
```
※ x は上記のマイナーバージョンの数字

## python2にpipがインストールできたか確認
```
python2 -m pip --version
```

## pyserialインストール
```
python2 -m pip install pyserial
```

## OS純正python3にpyserialが入っているか確認
```
python3 -m pip list
```
##　無い時
```
python3 -m pip install pyserial
```
wslでelixir その78

# 概要
wsl(wsl2じゃない)で、elixirやってみた。
練習問題やってみた。

# 練習問題
Livebookで、sin波を表示せよ。

# 方針
- vegalite使う

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/ef15305c-f077-0d08-6cf2-e847c3c0d3aa.png)


# サンプルコード

```
data = 1..100
  |> Enum.map(fn x ->
    :math.sin(x / 5)
  end)
VegaLite.new(width: 400, height: 400)
|> VegaLite.data_from_values(iteration: 1..100, score: data)
|> VegaLite.mark(:line)
|> VegaLite.encode_field(:x, "iteration", type: :quantitative)
|> VegaLite.encode_field(:y, "score", type: :quantitative)
```

以上。
リモートPCの画面サイズが変更できない！！時の対処法


初心者SEの備忘録です。。。。

【発生事象】
リモートPC（社内）
在宅PC（自宅）

在宅PCからリモートPCへ接続して作業をしていると…とあるアプリケーションを実行すると、在宅用PCのディスプレイが小さいため、サイト画面が見切れてしまっている。
しかもそのサイトは、画面サイズが変更できないかつスクロールできない仕様になっているため、画面縮小できない＝画面が見切れたまま！
リモートPCの画面サイズ設定は変更できない。自宅にディスプレイもないため詰み。

【対処法】
在宅PCの画面サイズを縮小したら解決。

【手順】 
１．在宅PC（リモート接続前）でホーム画面左下のWindowsボタンを押下し、「設定」を開く
２．「システム」を押下
３．ディスプレイタブの拡大縮小とレイアウトにて
　　「テキスト、アプリ、その他の項目のサイズを変更する」のパーセンテージを小さくする（例：150％→100％）

以上！


在宅PCの画面サイズを変更したら、リモート側の画面表示サイズも変わってくれるんですね。。勉強になりました。
aws cli

## Lambda
### 全リージョンからlambdaのランタイムと名前を取得
`aws account list-regions --region-opt-status-contains ENABLED ENABLED_BY_DEFAULT --query "Regions[].{Region: RegionName}" --output text | xargs -L1 aws lambda list-functions --query 'Functions[*].[Runtime,FunctionName]' --output text --region`
APIGatewayでrobots.txtを公開する

APIGatewayからtext/plainを出力したいだけなのに、えらい苦労したのでメモ。
Lambda、APIGatewayの使い方はある程度分かっている前提です。


# Lambda関数を作る

APIGatewayからrobots.txtを出力するだけのLambdaを作ります。
以下Pythonのソースコード。

```python
def lambda_handler(event, context):
    return {
        'statusCode': 200,
        'headers': {
            'Content-Type': 'text/plain'
        },
        'body': """
User-agent: *
Disallow:
        """
    }
```

Pythonからはapplication/jsonが返却されるのでAPIGatewayでそれを受け取ってtext/plainに変換します


# APIGatewayの設定

ここで苦労した。新しいコンソールになって分かりづらいにも程がある。
旧コンソールとやれることは同じなので慣れていきましょうー

## robots.txt用のリソースを作成

このボタンからrobots.txt 用のリソースを作成します。
この作業だけで惑わす表示がいっぱい！

**このボタンを押して**
![スクリーンショット 2023-10-06 9.29.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42110/911de200-2728-013b-5479-ad9ef13914d9.png)


**こう入力する**
リソースパスは"/"を選択。
リソース名にrobotx.txtと入力。

![スクリーンショット 2023-10-06 9.29.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42110/c33d2b5a-fefb-1800-9319-63982638be5d.png)


## GETメソッドを追加

GETを追加しましょう。

![スクリーンショット 2023-10-06 9.31.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42110/66553304-0636-ab2e-9efa-d46eb32fe420.png)


メソッドタイプはGET
統合タイプはLambda関数
Lambda関数は、さっき作った関数のARNを入力。

![スクリーンショット 2023-10-06 9.32.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42110/c3e822e1-0a0d-ead7-ca16-aa3f4f28a557.png)


## 統合レスポンスを追加

さっき作ったrobots.txt > GETリンクを押すとこんな画面になるので、統合レスポンスを押して、
マッピングテンプレートを作成

![スクリーンショット 2023-10-06 9.42.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42110/c07927a8-93d1-ad48-12f0-c15feef4bfa3.png)



## マッピングテンプレートを追加
コンテンツタイプを application/json
テンプレート本文は以下

```text
#set($inputRoot = $input.path('$'))
$inputRoot.body
```

![スクリーンショット 2023-10-06 9.45.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42110/57354bee-d4e7-774f-6d83-a12f3ae6c7ed.png)


## メソッドレスポンスを編集

レスポンスをJSONからtextへ捻じ曲げます。

![スクリーンショット 2023-10-06 9.46.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42110/f47c2129-3df0-0641-ba4f-2ec4d61deed2.png)


コンテンツタイプをtext/plainに変更

![スクリーンショット 2023-10-06 9.48.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42110/07354bc3-2616-3525-0e4c-ffe15980ed51.png)


# APIをデプロイして公開

ステージ名はprdとかがいいでしょう。
公開するとURLが発行されるのでアクセスしてみましょう

https:// 発行されたURL/prd/robotx.txt

以上！
nRF9160とESP32で LTEとESPNowを利用して４台の電子ペーパーの表示制御を行った話

# ハードウエア構成

nRF9160 1台
ESP32(PSRAMなし、内蔵フラッシュ4MiB) 5台
電子パーパー 4枚
・13.3inch 赤白黒(960x680) x1
・7.3inch 7color(800x480) x1
・7.3inch 赤黄白黒(800x480) x1
・7.5inch 赤白黒(800x480) x1

LTE-ESPNowゲートウェイデバイス
　nRF9160とESP32をUART接続（1MBps）
　（AC電源駆動を想定）

ESPNow電子ペーパーデバイス
　各電子ペーパーとESP32をSPI接続
　（バッテリー駆動を想定 待機時アヴェレージ電流 100uA程度）

# 概要

以下のサイトのWiFiデモを改造して、

https://www.waveshare.com/wiki/E-Paper_ESP32_Driver_Board


1) クラウド側で画像データを生成し、ZIP圧縮して、LTE-ESPNowゲートウェイに通知
2) ダウンロード開始（CRC16-CCITTとMD5で整合性チェック）
3) LTE-ESPNowゲートウェイでバッファリングして電子ぺーパーデバイスからのアップリンク待ち
4) ESPNowでP2P配信（暗号化あり）
5) 電子パーパーデバイスでZIP解凍（minizライブラリ）して画像表示

といった流れのシステムを作った。

ESP32はPSRAM付き（できればESP32-S3）が理想だったが、諸事情によりPSRAMなしかつ標準の内蔵フラッシュ４MiBでおこなった。

ゲートウエイのESP32では４並列で動作させる必要があるので無理があるかと思ったが、
意外にもそれなりの動作を実現できることを確認できた。

電子ペーパー側のデバイスは、バッテリー駆動が必須だったため
待機時のアヴェレージ電流が100uA程度という要求があったので、
ESPNowとディープスリープモードを併用して、省電力化を実現できた。

通信方式も、LoRaWANを参考に、定期的にアップリンクをおこないその後ダウンリンクの受付時間を確保する方式で、省電力化を実現できた。


# 以上
CentOS Stream 9にAnsible 2.9の最新版を導入する


# 1. CentOS Stream 9 環境情報確認

```txt:CentOS Stream 9 環境情報
[root@syasuda-centos9 ~]# dnf update -y
[root@syasuda-centos9 ~]# cat /etc/redhat-release
CentOS Stream release 9
[root@syasuda-centos9 ~]# uname -a
Linux syasuda-centos9 5.14.0-361.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Aug 24 13:40:45 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
```

# 2. pipのインストール

```txt:pipのインストール
[root@syasuda-centos9 ~]# dnf install -y python3-pip
```

# 3. virtual env環境の作成
以下では、pipをglobal levelで更新するとシステムに影響を与える可能性があるので、virtual env環境を構成し、そこでAnsible 2.9の最新モジュールを導入するようにする。

```txt:virtual env環境を作成。
[root@syasuda-centos9 ~]# python --version
Python 3.9.17
[root@syasuda-centos9 ~]# python -m venv env1
[root@syasuda-centos9 ~]# source env1/bin/activate
```

# 4. 各種モジュールの導入・更新

## 4-1. pipを最新化

```txt:pip
(env1) [root@syasuda-centos9 ~]# pip install pip --upgrade
```

## 4-2. ansible2.9台の最新版の導入

参考: https://tekunabe.hatenablog.jp/entry/2020/12/05/ansible_29

```txt:ansible2.9
(env1) [root@syasuda-centos9 ~]# pip install ansible~=2.9.0
```

## 4-3. Jinja2を最新化

```txt:Jinja2
(env1) [root@syasuda-centos9 ~]# pip install Jinja2 --upgrade
```

（参考） Jinja2を最新に更新したのは、Ansible 2.9におけるbooleanのvalidation checkで以下のようなエラーが発生したためである。この問題の回避には、Jinja2を最新化する必要がある。
https://github.com/robertdebock/ansible-role-tomcat/issues/37

```
TASK [validate_parms : Validate GUI flag] ********************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"msg": "The conditional check 'setup_vsi_gui is boolean' failed. The error was: template error while templating string: no test named 'boolean'. String: {% if setup_vsi_gui is boolean %} True {% else %} False {% endif %}"}
```

## 4-4. selinuxの導入

```txt:selinuxの導入
(env1) [root@syasuda-centos9 ansible]# pip install selinux
```

（参考）
virtual env環境で以下のようなSELinux系の操作のエラーを避けるため。これはselinuxを導入しておけば良いらしい。
https://copyprogramming.com/howto/ansible-playbook-to-check-the-condition-of-selinux-status

```
TASK [start_sno_install : Create marker file] *************************************************************************************************************************************************************************************
fatal: [localhost]: FAILED! => {"changed": false, "msg": "Aborting, target uses selinux but python bindings (libselinux-python) aren't installed!"}
```

# 5. 最終形

```txt:導入済みバージョン
(env1) [root@syasuda-centos9 ansible]#  pip list |grep -e pip -e ansible -e Jinja2 -e selinux
ansible      2.9.27
Jinja2       3.1.2
pip          23.2.1
selinux      0.3.0

(env1) [root@syasuda-centos9 ~]# ansible --version
ansible 2.9.27
  config file = None
  configured module search path = ['/root/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /root/env1/lib64/python3.9/site-packages/ansible
  executable location = /root/env1/bin/ansible
  python version = 3.9.17 (main, Aug  9 2023, 00:00:00) [GCC 11.4.1 20230605 (Red Hat 11.4.1-2)]
```
TypeError: datetime64 type does not support sum operations の対応方法

### これをやったらエラーが出た

```python
join_data.dtypes
```

実行結果
```
detail_id              int64
transaction_id        object
item_id               object
quantity               int64
payment_date          object
customer_id           object
customer_name         object
registration_date     object
customer_name_kana    object
email                 object
gender                object
age                    int64
birth                 object
pref                  object
item_name             object
item_price             int64
price                  int64
dtype: object
```

この`join_data`というデータから、`Payment_month`(月別)ごとの`price`(売上)を集計したい。

```Python
join_data.groupby("payment_month").sum()["price"]
```

実行結果
```
TypeError: datetime64 type does not support sum operations
```

どうすれば、エラーが起きなくなる？
### 結論
**`sum(numeric_only=True)`と書いて、数値型のみ演算させる**

### 考察
前提として、Anaconda下で仮想環境をアクティベートしています。バージョンは以下の通り。
Anaconda: 23.9.0
Python: 3.9.18
Pandas: 2.0.3

調べていたところ、この記事を紹介していただいたので読んでみました。
https://github.com/pandas-dev/pandas/issues/52558

すると、「`sum(numeric_only=True)`とすることで解決できるよ」というコメントがありました。

そのコメントに従ってやってみたら、解決できました。

多分、`sum()`のまま実行すると`sum(numeric_only=None)`と同義になり、数値型以外の型まで演算してしまう。それが原因でエラーが起きたと推察しています。
wslでelixir その77

# 概要
wsl(wsl2じゃない)で、elixirやってみた。
練習問題やってみた。

# 練習問題
Livebookで、論理回路の真理値表を表示せよ。

# 方針
- Kino.DataTable使う
- 2入力、1出力
- nand,nor,and,or,xor実装

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/ab8706d5-c811-3b41-70bb-abacd620c6f2.png)


# サンプルコード

```
defmodule Ronri do
	def start_link do
		Agent.start_link(fn ->
			[a: [0, 0, 0, 0], b: [0, 0, 0, 0], c: [0, 0, 0, 0], m: 3]
		end, name: __MODULE__)
	end
	def init(o, p) do
		Agent.update(__MODULE__, fn v ->
			d = [0, 1, 0, 1];
			v = List.keyreplace(v, :a, 0, {:a, d})
			d = [0, 0, 1, 1];
			List.keyreplace(v, :b, 0, {:b, d})
		end)
	end
	def nand(o, p) do
		Agent.update(__MODULE__, fn v ->
			d = [1, 1, 1, 0];
			List.keyreplace(v, :c, 0, {:c, d})
		end)
	end
	def nor(o, p) do
		Agent.update(__MODULE__, fn v ->
			d = [1, 0, 0, 0];
			List.keyreplace(v, :c, 0, {:c, d})
		end)
	end
	def xor(o, p) do
		Agent.update(__MODULE__, fn v ->
			d = [0, 1, 1, 0];
			List.keyreplace(v, :c, 0, {:c, d})
		end)
	end
	def and0(o, p) do
		Agent.update(__MODULE__, fn v ->
			d = [0, 0, 0, 1];
			List.keyreplace(v, :c, 0, {:c, d})
		end)
	end
	def or0(o, p) do
		Agent.update(__MODULE__, fn v ->
			d = [0, 1, 1, 1];
			List.keyreplace(v, :c, 0, {:c, d})
		end)
	end
	def print() do
		Agent.get(__MODULE__, fn v ->
			{_, a} = List.keyfind(v, :a, 0)
			{_, b} = List.keyfind(v, :b, 0)
			{_, c} = List.keyfind(v, :c, 0)
			0..3 
			|> Enum.map(fn n -> 
			    [{0, Enum.at(a, n)}, {1, Enum.at(b, n)}, {2, Enum.at(c, n)}]
			end)
			|> Kino.DataTable.new()
		end)
	end
end



Ronri.start_link
Ronri.init(0, 1)

Ronri.xor(0, 1)
Ronri.print

```

以上。
[C#]null合体代入演算子の個人的な使いどころ

# はじめに
本記事は個人的に感じたnull合体代入演算子の使いどころについてまとめます。

# null合体代入演算子とは
`??=`で表現される演算子です。
オペランドの左辺の変数やプロパティなどがnullの場合に、右辺の値が左辺に代入されます。
詳細は[こちら](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/assignment-operator#null-coalescing-assignment)を参照してください。

# 個人的に感じた使いどころ
インスタンス作成後に別途初期化用のメソッドで初期化をする際に使えると感じました。
例えば、以下のように電話帳(TelephoneDirectory)クラスで初期化用のinitメソッドで連絡先一覧(Contacts)プロパティを初期化するケースを考えます。
Initメソッドは複数回呼び出される可能性があるため、連絡先一覧(Contacts)プロパティはnull(初期化されていない)の場合のみ初期化しています。
```csharp
/// <summary>
/// 電話帳
/// </summary>
public class TelephoneDirectory
{
	#region プロパティ

	/// <summary>
	/// 連絡先一覧
	/// </summary>
	public IList<Contact>? Contacts { get; private set; }

	#endregion

	#region 初期化

	/// <summary>
	/// 初期化する
	/// </summary>
	public void Init()
	{
		// 複数メソッドが呼び出された場合を想定して、連絡先一覧が初期化されていない場合のみ初期化する
		if (Contacts == null)
		{
			Contacts = new List<Contact>();
		}
	}

	#endregion
}
```
これをnull合体代入演算子を利用するとInitメソッドは以下のようになります。
if文を使わずに簡潔に記載できます。
```csharp
/// <summary>
/// 初期化する
/// </summary>
public void Init()
{
	// 複数メソッドが呼び出された場合を想定して、連絡先一覧が初期化されていない場合のみ初期化する
	// null合体代入演算子
	Contacts ??= new List<Contact>();
}
```
この例のようにnull合体代入演算子は初期化用のメソッド内での初期化処理で活用できると感じました。

# まとめ
本記事では個人的に感じたnull合体代入演算子の使いどころについてまとめました。
初期化用のメソッド内での初期化処理を簡潔に記載する際に活用できると感じました。
AZ-104試験対策のまとめ「データ転送編」

## はじめに
Microsoft Azure Administrator(AZ-104)合格に向けた勉強でまとめた内容を本記事では投稿しています。
試験範囲の内容を私が勉強していく中で、
・忘れてしまっていた内容
・他の知識と絡めて理解した方が良い内容
・Microsoft Learnの読解に苦戦した内容
などをできるだけ分かりやすく簡潔にまとめることを意識しています。
Azureの勉強を一から始め、AZ-104取得を目指している方の参考になれば幸いです。
※本記事の内容が必ず試験に出るわけではないこと、ご注意ください

## 試験対策のまとめ
### **Azure Import/Export**
データディスクを用いてAzure Blob StorageやAzure Filesにデータのインポート/エクスポートが可能  

* インポート/エクスポートの方法
    - **インポート**：Azureデータセンターにデータをコピーしたディスクドライブを送付してインポートしてもらい、ディスクドライブが返送される
    - **エクスポート**：Azureデータセンターにディスクドライブを送付してエクスポートしてもらい、ディスクドライブが返送されるので自身でエクスポート実施
    ※データディスクはMicrosoftからの提供も可能

<br>

* データの暗号化  
ドライブ上のデータはAES256ビットBitLockerドライブ暗号化を使用して暗号化  
    - インポートの場合の暗号化方法
        - ドライブ準備時にWAImportExportツールを実行する際、*dataset.csv*ファイルを使用する時にオプションを指定
        - ドライブの準備中、WAImportExportツールコマンドラインを実行する時、*driveset.csv*に暗号化キーを指定(暗号化を手動で有効化)
    - エクスポートの場合の暗号化方法
        - データがコピーされたドライブがBitLockerで暗号化され、返送される  
        その暗号化キーはAzure Portalを介して提供  
        WAImportExportツールでキーを使い、ドライブのロックを解除

<br>

* 参考
    - https://learn.microsoft.com/ja-jp/azure/import-export/storage-import-export-service


## 次の試験対策のまとめ
* **AZ-104試験対策のまとめ「Azure Monitor編」**

https://qiita.com/fsd-jume/items/97b8296658298ea498a4

## 学習内容をまとめたページの一覧

https://qiita.com/fsd-jume/items/6eda24728c9c03c36bc3