OmniAICreator/Qiita-1.07M huggingface dataset:
pickleの動作をカスタマイズするためのメソッドをテストする

[pickle](https://docs.python.org/3/library/pickle.html)は、ファイルなどに保存するために、Python オブジェクトをバイト列にシリアライズするための標準ライブラリです。Pickle可能なオブジェクトはpickable、シリアライズされたバイト列からオブジェクトを再生成することをUnpickleと呼んでいます。

Pickleは、デフォルトでは`__dict__`に登録されているオブジェクトの属性をシリアライズしようとしますが、pickle用のスペシャルメソッドを定義することにより、その挙動をカスタマイズすることができます。特に、pickleするオブジェクトが、pickle出来ないオブジェクトをメンバーとして持っている場合や、そのオブジェクトが引数をとるコンストラクタで生成される場合などに使用します。

| メソッド          |  使用目的                                       |   
|:-----------------|-----------------------------------------------:|
| `__getnewargs__()` |  Unpickleする際にコンストラクタに渡す引数を指定する。|
| `__getstate__()`   |  `__dict__`ではなく、このメソッドが返すオブジェクトをpickleする。|
| `__setstate__(state)`   | pickleされていたオブジェクトをstateとして受け取り、unpickle時の処理を行う。 |

次のコードはこれらのメソッドの挙動を確認するためサンプルスクリプトです。

```py:
import pickle

class Foo:
    def __new__(self, a):
        print('in __new__:', a)
        self = object.__new__(self)
        # self.a = a
        return self
    
    def __init__(self, a):
        print('in __init__:', a)
        self.a = a

    def __getstate__(self):
        print('in __getstate__:', self.a)
        state = self.__dict__.copy()
        return state

    def __getnewargs__(self):
        print('in __getnewargs__:', self.a)
        return (2 * self.a,)

    def __setstate__(self, state):
        self.__dict__.update(state)
        print('in __setstate__:', self.a)


foo = Foo(3)
bar = pickle.dumps(foo)
baz = pickle.loads(bar)
print(baz.__dict__)
```
このスクリプトの実行結果は次のようになります。

```pycon:
in __new__: 3
in __init__: 3
in __getnewargs__: 3
in __getstate__: 3
in __new__: 6
in __setstate__: 3
{'a': 3}
```
pickleのマニュアルに書いてあるとおりですが、下記のことが分かります。

- `__getnewargs__`は、Pickleする時に呼ばれる。つまり、Unpickleされるときに`__new__`に渡す引数の情報は、Pickleされている。
- `__new__`には、`__getnewargs__`が返した値が渡されている。
- Unpicleでは`__init__`は呼ばれない。
div要素とspan要素の違い

span要素やdiv要素は意味をもたない要素であるが、どんな違いがあるのか、復習がてらまとめてみました。
この２つは実務上ではcssで装飾したいときなどに使われます。そしてclass属性をつけることができます。class属性とはその要素が所属するグループ名のことで自由につけることができます。
まず例として

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>これはテスト</title>
    <style>
      .red{
        color: red;
      }
      .blue{
        color: blue;
      }
    </style>
  </head>
    <body>
      <p>私はただいま<span class ="red">html</span>と<span class ="blue">css</span>を学習中です</p>
    </body>
</html>
```
結果

![e7c1b584cbef8f2be9ffe18436f48640.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1301666/23c1bfa2-6f00-5a4e-c19e-e93bef5513ec.png)
このようにclass属性を定義してcssで色をつけることができます。

ではdivとの違いは何かというとまず１つ目は改行がされるかどうか、ということです。

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>これはテスト</title>
  </head>
    <body>
      これは<div>div</div>要素です
      <hr>
      これは<span>span</span>要素です
    </body>
</html>
```
![ec7a3ab54efc4258403c521f68ad08d1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1301666/2080a4c7-98f8-76fc-415f-7c7a05a69e6b.png)

このようにdiv要素は改行されますが、span要素は改行されません。
もう１つはdivの中にspanは書けますが、spanの中にdivは書けないので注意です。

```html
OK
<div><span>テスト</span></div>

NO
<span><div>テスト</div></span>
```

まとめると

div要素とspan要素はどちらも意味をもたない要素
div要素は改行されるがspan要素は改行されない
div要素は中にspan要素を書けるがspan要素の中にdiv要素は書けない
OSI参照モデルまとめ

# 1. OSI参照モデルとは
Open Systems Interconnection
異なる機種間でも通信できる世界標準のプロトコルスタック。
ISOによって策定された。

# 2. プロトコルとは
通信をおけるルール。
送信側と受信側で同一のプロトコルでなければデータの疎通ができない。

１つの通信プロセス中でも多くのプロトコルが存在する。
ある通信における様々なプロトコルを役割ごとにまとめたものを、
プロトコルスタック(ネットワークアーキテクチャ、プロトコルスイート)と呼ぶ。

昔は各メーカー毎に独自のプロトコルスタックを作っていたため、各企業の製品同士でしかネットワークを構築できなかった。
これを解消するためにOSI参照モデルが策定された。

# 3. 7つの層(レイヤ)
OSI参照モデルは通信に必要なプロトコルを7つのレイヤに分割し、それぞれのレイヤで必要になるプロトコルを定義したプロトコルスタック。

レイヤ毎にプロトコルをを階層化することで各プロトコルの役割が明確になり、変更が必要でも最小限のプロトコルの変更で済むといったメリットがある。

![OSI参照モデルの各階層の名称や役割について](https://www.itmanage.co.jp/column/osi-reference-model/img/osi_reference_model.png "OSI参照モデルの各階層の名称や役割について")

# 4.各レイヤについて
### 4.1 物理層 (レイヤ1)
機器や電気信号に関するプロトコル群
2つのノード間で確実に通信するために必要なプロトコルが定義される。

* ケーブルの種類や長さに関するルール
* 電圧レベル、電圧変化に関するルール
* 通信速度、電気信号の符号化に関するルール

送出データの電気的な変換などを行う。

　利用例:UTPケーブル、光ファイバーケーブル
　プロトコル:RS-232,UTP,無線

### 4.2 データリンク層 (レイヤ2)
隣接ノード間の通信に関するプロトコル群
2つのノード間で確実に通信するために必要なプロトコルが定義される。

* データの送信元と宛先の識別に関するルール(物理アドレス)
* データ衝突の検知や回避に関するルール
* 送出するためのデータ加工に関するルール

直結された相手との通信路を確保し、データの誤り訂正や再送要求などを行う。

　利用例:イーサネット
　プロトコル:PPP,Ethernet

### 4.3 ネットワーク層 (レイヤ3)
エンドツーエンド(データの送信元と送信先を結ぶ通信経路全体)の通信に関するプロトコル群
ネットワークをまたいだエンドツーエンドの通信をするために必要なプロトコル群が定義される。

* データの送信元と宛先の識別方法に関するルール(論理アドレス)
* 経路選択の方法に関するルール
* データの送出方法に関するルール

相手までデータを届けるための経路選択やネットワーク上で個々を識別するためのアドレス管理を行う。

　利用例:IP
　プロトコル:IP,ARP,RARP,ICMP

### 4.4 トランスポート層 (レイヤ4)
通信における信頼性に関するプロトコル群
通信の方式、通信状態の確認方法、異常発生時の対処など、通信が期待通り行われるためのプロトコルが定義される。

* 確実な伝送orスピードどちらを優先するか
* 以上があったときの対処に関するルール
* 送信量の調節に関するルール

ネットワーク層から流れてきたデータの整列や誤り訂正などを行い、送受信されたデータの信頼性を確保する。

　利用例:TCP,UDP
　プロトコル:TCP,UDP,NetWare/IP

### 4.5 セッション層 (レイヤ5)
アプリケーション間の接続に関するプロトコル群
アプリケーション同士の論理的な接続に関するプロトコルが定義されている。

* 2者間の接続開始から終了までのルール
* 接続の維持や回復に関するルール
* 接続状況の管理に関するルール

通信の開始や終了といった通信プログラム同士の接続を管理し、通信経路の確立を行う。

　利用例:HTTPS
　プロトコル:TLS,NetBIOS

### 4.6 プレゼンテーション層 (レイヤ6)
ユーザが見て触れるデータに関するプロトコル群
文字・画像・動画といったユーザが扱うデータの変換に関するプロトコルが定義される。

* 文字コードに関するルール
* 画像や動画に関するルール
* データの圧縮や暗号化に関するルール

アプリケーションとネットワークの仲介を行う。

　利用例:HTML
　プロトコル:SMTP,FTP,Telnet

### 4.7 アプリケーション層(レイヤ7)
アプリケーション固有のプロトコル群
電子メール・Webページの閲覧・ファイル転送といったユーザが操作するアプリケーションの動作を支えるプロトコルが定義される。

* 電子メールの送受信に関するルール
* メールサーバーの利用に関するルール
* 添付ファイルの送信に関するルール

通信を利用するために必要なサービスが人やプログラムに対して提供する。

　利用例:www , メール
　プロトコル:HTTP,FTP,DNS,SMTP,POP

# 5. カプセル化/非カプセル化

* 送信データは高レイヤから低レイヤへと順に処理され、レイヤ1で信号として伝送される。
    * 送信データがレイヤ7~レイヤ2まで処理していく際、**ヘッダ** という制御情報を付加する。
    * データリンク層(レイヤ2)ではヘッダに加えて **トレーラ**というエラーチェック用の情報も付加する。

　レイヤ毎にヘッダ、トレーラを付加していく処理を**カプセル化**という。


* 受信データは低レイヤから高レイヤへと順に処理され。レイヤ7ｍｐアプリケーションへと伝達される。
    * 受信データはレイヤ2からレイヤ7まで処理する毎に、送信時に付加されたヘッダ、トレーラを外す。

　レイヤ毎にヘッダ、トレーラを外していく処理を**非カプセル化**という。

* データ＋ヘッダ＋トレーラのデータ単位を**PDU(Protocol Data Unit)**という。
    * PDUはどのレイヤのヘッダがふかされているかで呼び名が変わる。
 
| 1 | 2 |
|:-:|:-:|
|   レイヤ5~7|   データ|
|   レイヤ4(トランスポート層)|   セグメント|
|   レイヤ3(ネットワーク層)|   パケット|
|   レイヤ2(データリンク層)|   フレーム|
|   レイヤ1(物理層)|   ビット、シンボル|

# 参考

https://youtu.be/5QHXbxZIUDg

https://www.itmanage.co.jp/column/osi-reference-model/

https://qiita.com/naoyakenkyo/items/50e9817792fb5c8133d7
個人メモ laravel 時間を指定してバッチを実行する

# 目的

- 作成したバッチ処理を時間を指定して動かす方法をメモ的にまとめる

# 方法

1. `アプリ名ディレクトリ/todos/app/Console/Kernel.php`のschedule()メソッドに記載することで時間を指定してバッチ処理を実行する事ができる。
1. 実行タイミングの指定方法は下記に詳しく記載されている。
    - [https://readouble.com/laravel/6.x/ja/scheduling.html](https://readouble.com/laravel/6.x/ja/scheduling.html)
a[target="_blank"]::after で付加した画像の位置がおかしい時の対処法

## 焦った話
いつも通り何も考えずに `a[target="_blank"]`に外部リンクですよー、という意味の画像をつけて確認.....
![スクリーンショット 2021-05-03 19.20.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1142160/0f92ddd9-df5a-3786-23f3-7da3af7347c5.png)
※わかりやすいように、`a`と`a::after`それぞれに色を薄くつけてあります。

謎の空白が空いているではありませんか。しかも、この画像、後続の文にがっつり被ってしまいます。

仕方ない、教えてgoogle先生！ざっと調べました。出てきません。私、焦りました。

## 原因
デベロッパーツールで一つずつ確認し定期ました。すると、思いもよらないやつが原因でした。そいつは、`text-indent`だったのです。

```html:HTML
<style>
    p {
      text-indent: 1em;
    }
</style>
<p>
  <a href="https://google.com" target="_blank">Google</a>
</p>
```

以上のように、`p`に`text-indent`を適用して、 `p`の子要素に`a`を配置していました。その影響で、`a`にも`text-indent`が継承されてしまい、謎の空白が空いてしまったのですね。

## 解決
ということで、`a`の`text-indent`を無効にしてあげればよさそうですね！

```html:HTML
 <style>
    p {
      text-indent: 1em;
    }

    a {
      text-indent: 0;
    }
  </style>

<p>
  <a href="https://google.com" target="_blank">Google</a>
</p>

```

意図した通りになりました！
![スクリーンショット 2021-05-03 20.03.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1142160/af36dac1-9590-fc90-232e-c5593f9b33e4.png)

## おわりに
`text-indent`は継承しないで欲しいですね。気をつけましょう！
Ruby 問題① ハッシュの基礎について

#はじめに#
こんにちは

3月から学習を始めてアウトプットを兼ねて投稿させていただきます。

#問題#

```ruby.rb
  puts menu.keys
  puts menu.values
```
上記のメソッドを記述した時、

```ruby.rb
餃子
炒飯
ラーメン
240
450
650
```
とターミナルに表示なれるような変数studentを作成するためのコードを考えて下さい。

#ヒント#
・ハッシュの生成
・keyとvalueの存在

##解答##

```ruby.rb
menu = {餃子:240, 炒飯:450, ラーメン:650,}

puts menu.keys
puts menu.values

餃子
炒飯
ラーメン
240
450
650
```

#解説#

**ハッシュとは**
~~ハッシュとは「データ」とそれに対応する「名前」のセットを要素として持つ値です。~~
ハッシュとは、キーと値の組み合わせをセットにして複数のデータを管理することが出来ます。

```
変数 = {}
#hashの宣言
#{}波括弧を使います
```

**シンボルとは**
シンボルは、見た目は文字列のようですが、実際の中身は数値になっている値です。

複数のデータを持つことのできる値という点では、配列と同じです。
しかし、ハッシュは「キー」で管理する事ができます。

試しに配列の場合を記述します。

```ruby.rb
menu = ["餃子",240,"炒飯",450,"ラーメン",650]

puts menu

餃子
240
炒飯
450
ラーメン
650
```
挙動は似ているようで読みにくい場合があります。
ハッシュとの大きな違いは、値にそれぞれキーを設定できるという事です。

##hashの利用について##
railsでUserモデルでユーザーのデータを取得したと考える事にします。

```
User = ["name","tom","age",25,"address","tokyo"]

puts User[1] #=> tom
puts User[3] #=> 25
puts User[5] #=> tokyo
```
配列だと呼び出す時に[添字]で呼び出すため何を呼び出しているかが見にくいです。

```
User = {name:"tom", age:25, address:"tokyo"}

puts User[:name] #=> tom
puts User[:age] #=> 25
puts User[:address] #=> tokyo
```
この記述だと何の値を呼び出しているかが一目でわかります。
AWS Lightsailでphpmyadminにアクセスする

Lightsailで、LAMP環境を立ち上げたのですが、phpmyadminへのアクセスを試みたものの、
セキュリティ設定がされているようで、試行錯誤したので、メモを残しておきます。

先人たちの教えに基づき、あれこれ試したものの、肝心のフォルダがないという惨劇。。。
``/opt/bitnami/apps`` のフォルダが、、、ない。
![2021-05-03_12h35_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626057/1a03db2c-3382-d45f-58ff-980b815c7fa3.png)

https://kanikuriimkorokke.com/archives/510

# 設定ファイルの場所
で、結局、どこに目当てのファイルがあったかというと、、、ここにありました！！

> /opt/bitnami/apache/conf/bitnami/phpmyadmin.conf

``Require local`` を 以下の用意に書き換えます。
![2021-05-03_12h41_34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626057/090616c3-41a1-5fbb-0443-73bd7013961d.png)

```
Alias /phpmyadmin "/opt/bitnami/phpmyadmin"
<Directory "/opt/bitnami/phpmyadmin">
  Options -Indexes +FollowSymLinks -MultiViews
  #AllowOverride All
  AllowOverride None
  #Require local
  <IfVersion < 2.3>
  Order allow,deny
  Allow from all
  Satisfy all
  </IfVersion>
  <IfVersion >= 2.3>
  Require all granted
  </IfVersion>
  ErrorDocument 403 "For security reasons, this URL is only accessible using localhost (127.0.0.1) as the hostname."
  # AuthType Basic
  # AuthName phpmyadmin
  # AuthUserFile "/opt/bitnami/apache/conf/users"
  # Require valid-user
</Directory>

```

変更したら、以下のコマンドで、apacheを再起動します。

> sudo /opt/bitnami/ctlscript.sh restart

# アクセスの確認
``http://[IPアドレス]/phpmyadmin`` にアクセスしてみます。
以下のログイン画面が表示されれば、設定は成功です！！

![2021-05-03_12h47_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/626057/80022753-0309-36e2-b52a-ceabda557cef.png)


# 補足: ユーザー名とパスワードの取得
phpmyadminのアクセスに必要なパスワードは、以下の手順で取得できます。

> ホームディレクトリで、以下のコマンドを実行

> ``cat bitnami_credentials``

> パスワードが表示されるのでコピーする。

ユーザー名は、``root`` です。
対称クロネッカー積におけるvecからsvecに変換する行列Qの実装について

# 0. はじめに
「21世紀の線形計画」といわれる半正定値計画(SDP)を実装し理解を深めようと思います。多くの教科書では半正定値計画の理論的なところを触れているので、実装例を備忘録の連載という形で書いていきます。21世紀の皆様に少しでもお役に立てれば幸いです。

# 1. vecからsvecに変換する行列Q
$vec$から$svec$に変換する行列Qの実装でつまづいたので備忘録として残す。

変換行列Qの用途をわかりやすいように、2x2の実対称行列$A$を実例として説明する。
実対称行列なので$a_{21}=a_{12}$である。

```math
A = \left(
    \begin{array}{cc}
      a_{11} & a_{12}  \\
      a_{21} & a_{22} 
    \end{array}
  \right)
```

ここで、実対称行列$A$を$vec$作用素でベクトル化すると$vec(A)$をえる。

```math
vec(A) := (a_{11},a_{21},a_{12},a_{22})^T
```

実対称行列の対称的な構造に着目し、変数を減らすことができる$svec$を導入する。例えば、2x2実対称行列において、$vec$の場合変数は4であるが、svecの場合変数が3と減らせることがわかる。

```math
svec(A) := (a_{11},\sqrt{2} a_{21},a_{22})^T
```

$vec$と$svec$の内積が等しいことを確認するため、vecの内積を下記に示す。

```math
trace(AA^T)=vec(A)^Tvec(A)=a_{11}^2 + a_{21}^2 + a_{12}^2 + a_{22}^2 = a_{11}^2 + 2a_{21}^2 + a_{22}^2 
```

$svec$の場合でも下記のように$vec$と同じ内積値を得られることがわかる。

```math
svec(A)^Tsvec(A)= a_{11}^2 + 2a_{21}^2 + a_{22}^2 
```

このように$svec$が次元削減において便利であることがわかったと思う。大規模な実対称行列のサイズであれば、より演算回数が減らすうえで有用であることが予想できる。

便利さがわかれば$vec$から$svec$に変換する変換行列$Q$を生成する関数が欲しくなると思う。
2x2の実対称行列の場合の変換行列$Q$は下記で与えられる。


```math
Qvec(A) = svec(A)
```

```math
Q^Tsvec(A) = vec(A)
```


```math
Q = \left(
    \begin{array}{cccc}
      1 & 0 & 0 & 0 \\
      0 & \sqrt{2} & \sqrt{2} & 0 \\
      0 & 0 & 0 & 1
    \end{array}
  \right)
```



# 2. 実装

任意の実対称行列における変換行列$Q$の実装例を下記に示す。

```python
import numpy as np

def set_vec2svec_matrix(n):
  Q = np.zeros((np.int(n*(n+1)/2) , np.int(n**2)))

  for k in range(n):
      for l in range(n):
          if k == l:
              i = np.int(n*l -l*(l+1)/2 + k)
              j = np.int(n*l + k)
              Q[i,j] = 1
          elif k > l:
              i = np.int(n*l -l*(l+1)/2 + k )
              j = np.int(n*l + k)
              Q[i,j] = 1/np.sqrt(2)
          elif k < l:
              i = np.int(n*k -k*(k+1)/2 + l)
              j = np.int(n*l + k)
              Q[i,j] = 1/np.sqrt(2) 
  return Q
```

2x2の実対称行列における変換行列Qにおける実行方法を下記に示す。

```
$ n = 2
$ Q = set_vec2svec_matrix(n)

#[[1.         0.         0.         0.        ]
# [0.         0.70710678 0.70710678 0.        ]
# [0.         0.         0.         1.        ]]
```

参照

- [On the Kronecker](https://www.math.uwaterloo.ca/~hwolkowi/henry/reports/kronthesisschaecke04.pdf)
vbaとRとWolframAlphaとCVXPYで(wikipedia非線型計画法)「3次元の例」 をやってみたい

(参考)
English版

https://en.wikipedia.org/wiki/Nonlinear_programming#3-dimensional_example

日本語版

https://ja.wikipedia.org/wiki/%E9%9D%9E%E7%B7%9A%E5%BD%A2%E8%A8%88%E7%94%BB%E6%B3%95#3次元の例

#vbaで

#Rで

#WolframAlphaで
https://www.wolframalpha.com/input/?i=maximize+++x*y+%2B+y*z%2C+x%5E2+%E2%88%92+y%5E2+%2B+z%5E2+%3C%3D2+%2Cx%5E2+%2B+y%5E2+%2B+z%5E2+%3C%3D10&lang=ja


https://www.wolframalpha.com/input/?i=plot3D+%7BRe%5Bsqrt%28-x%5E2+-+y%5E2+%2B+10%29%5D%2CRe%5B-sqrt%28-x%5E2+-+y%5E2+%2B+10%29%5D%2CRe%5Bsqrt%28-x%5E2+%2B+y%5E2+%2B+2%29%5D%2CRe%5B-+sqrt%28-x%5E2+%2B+y%5E2+%2B+2%29%5D++%7D+++++%2C++%7Bx%2C-sqrt%2810%29%2Csqrt%2810%29%7D%2C%7By%2C-sqrt%2810%29%2Csqrt%2810%29%7D++++++++&lang=ja


#CVXPYで
[解決]Access to XMLHttpRequest at from origin has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

##結論
RailsAPIとVue間でデータを共有しようとしたところエラー。**API側のCORS設定がうまく行っていなかったことが原因だった。**

## 詳しく

エラーのコード

```ruby
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
```

CORSが正常に機能したコード

```ruby
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'http://localhost:8080', 'http://localhost:3002/v1/blogs/'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
```



originを全て受け入れる(*)にすると、CORSのセキュリティ的にエラーが出るようになっているらしい。だから、クライアント側のoriginを明記すると、CORSが機能する。

#####参考
https://web.dev/cross-origin-resource-sharing/
https://qiita.com/att55/items/2154a8aad8bf1409db2b
https://qiita.com/residenti/items/3a03e5e0268b354284b7
vbaとRとWolframAlphaとCVXPYで(wikipedia非線型計画法)「2次元の例」 をやってみたい

(参考)
English版

https://en.wikipedia.org/wiki/Nonlinear_programming#2-dimensional_example

日本語版

https://ja.wikipedia.org/wiki/%E9%9D%9E%E7%B7%9A%E5%BD%A2%E8%A8%88%E7%94%BB%E6%B3%95#2次元の例


#vbaで

#Rで

#WolframAlphaで
https://www.wolframalpha.com/input/?i=maximize+x+%2B+y%2Cx+%3E%3D+0%2C+y+%3E%3D+0%2C+x%5E2%2By%5E2+%3E%3D1%2Cx%5E2%2By%5E2+%3C%3D2+&lang=ja


https://www.wolframalpha.com/input/?i=x+%3E%3D+0%2C+y+%3E%3D+0%2C+x%5E2%2By%5E2+%3E%3D1%2Cx%5E2%2By%5E2+%3C%3D2+&lang=ja

#CVXPYで
PWAにアプリショートカットを導入する(長押しオプション)

こんばんは。ちょっとしたTipsでござる。
今回はPWAを更にネイティブアプリにすべく**アプリショートカット**を導入しましょう。
App-Shortcutとはアプリを長押ししてら出てくるやつのことです。
↓こんなやつ
![Screenshot_20210503_182553_com.huawei.android.launcher.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513165/4a30099c-d411-7e2f-89bf-ed0b0c36385b.jpeg)
アプリショートカットはChrome84からPWAでも使えるようになりました。
超簡単にユーザーの生産性向上につながると思うので実装したほうが良いかも知れませんってことでやりましょう。
今回は[私のブログ](https://0115765.com)で導入してみました。

# 事前事項
詳細な仕様などは公式サイトを見てください。

https://web.dev/app-shortcuts/

※PWA系の記事で毎回書いてるんですけど**SSL対応が必須**です。テストでやりたいなら[GitHub Pagesとかを使う](https://0115765.com/archives/2085)といいかも知れません。
## 対応状況
対応しているプラットフォームは以下のとおりです。(2021/05/03現在)

| プラットフォーム | 是非 |
|:-:|:-:|
| Windows | **○（Chrome85およびEdge85）** |
| MacOS | × |
| Android | **○（Chrome85）** |
| iOS | × |
| ChromeOS | **△**(記事下部参照) |

Apple系がまだ非対応ですね。

# 導入方法
導入はとてもかんたんで`manifest.json`に`shortcuts`配列を追加するだけです。ServiceWorkerの変更とかも不要です。

```json:manifest.json
{
  "name": "Player FM",
  "start_url": "https://hoge.com",
  "shortcuts": [
    {
      "name": "Open Play Later",
      "short_name": "Play Later",
      "description": "View the list of podcasts you saved for later",
      "url": "/play-later",
      "icons": [{ "src": "/icons/play-later.png", "sizes": "192x192" }]
    },
    {
      "name": "View Subscriptions",
      "short_name": "Subscriptions",
      "description": "View the list of podcasts you listen to",
      "url": "/subscriptions",
      "icons": [{ "src": "/icons/subscriptions.png", "sizes": "192x192" }]
    }
  ]
}
```

名前とURLだけは必須です。アイコン等はアプションですが設定しとくといいでしょう。
※アイコンには現時点ではSVGファイルはサポートされていません。代わりにPNGを使用してください。
# 動作確認
### ブラウザで確認
ちゃんと反映されてるか確認したい場合、ブラウザの開発者モードのApplicationタブでチェックで来ます。
![画像_2021-05-03_192957.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513165/5459c650-2489-2d3a-69d1-70f6b7a2c4f4.png)

### Android
Androidではネイティブアプリと同様に長押しで表示できます。設定したアイコンと名前、リンク先が反映されています。
![Screenshot_20210503_172156_com.huawei.android.launcher.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513165/22c07b21-bb96-e602-1860-fd7efb8c55da.jpeg)
### Windows
Windowsの場合は**スタートメニューを右クリック**もしくは**タスクバーのアイコンを右クリック**で表示できます。
![スクリーンショット 2021-05-03 183319.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513165/ab80e01a-13b9-5ee3-a1c5-f24e8f98b0e6.png)![スクリーンショット 2021-05-03 184058.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513165/dfb55144-0628-8f45-7256-90fd155143a2.png)
### MacOS
Macはやっぱ非対応でした。
![スクリーンショット 2021-05-03 18.53.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513165/5cb7c03d-5d40-78f2-c444-afbb5cf7d8f0.png)
### ChromeOS
ChromeOSではChrome 92以降の実験機能で利用できるそうです。
`chrome://flags/#enable-desktop-pwas-app-icon-shortcuts-menu-ui`をONにするだけでできます。
公式より↓
![画像_2021-05-03_193504.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/513165/67c69229-2ace-9dc4-9e07-0f3fc37aef10.png)

かんたんなので皆さんも是非お試しあれ。


【ブログ】https://0115765.com/
【Twitter】**[@tomox0115](https://twitter.com/tomox0115)**
[Django / python] 関連テーブルの複数の属性を条件にしてgroup_byする（ORMで）


# やりたいこと

- 複数のテーブルを結合し、その結果できた表に対して`GROUP_BY`、`SUM`を適用したい。
- `GROUP_BY`の条件とするのは、関連テーブル側に存在するカラム。

# どういうこと？

以下のようなテーブルがあると考えます。

## テーブル

### 1. Orders(注文)

|カラム名|型 |
|:---|:------|
|id  |integer|
|date|date   |

### 2. Items(商品)

|カラム名|型 |
|:---|:------|
|id  |integer|
|name|string |


### 3. OrderedItems(注文された商品)

|カラム名|型     |
|:-------|:------|
|id      |integer|
|order_id|integer|
|item_id |integer|
|count   |integer|

このようなテーブルがあるとして、それぞれの日(date)にどの商品(name)がどれだけ(count)注文されたのかを集計したい。

そこで次のようなSQLが思い浮かぶ....

## SQL

※このSQLは実際に実行したわけではないので、このSQLには誤りが含まれている可能性がかなり高いです。
あくまでもイメージです。

```sql:SQLのイメージ
SELECT orders.date,
       items.name,
       SUM(ordered_items.count) AS total
FROM ordered_items
    LEFT JOIN orders ON ordered_items.order_id = orders.id
    LEFT JOIN items ON ordered_items.item_id = items.id
GROUP BY orders.date, items.name;
```

こんな感じのSQLを`Django`のORM経由で実行したい。

# これでできた [^1]

```python:python
from django.db.models import Sum

OrderedItem.objects \
    .values('order__date', 'item__name') \
    .annotate(total=Sum('count'))
```

## ポイント？

どうやら、

- GROUP_BYしたいとき  
`values`メソッドの引数に、GROUP_BYする際の条件とするカラム名を文字列で指定する
- 他のテーブルのカラムを条件にしてGROUP_BYしたい場合  
`values`に指定するカラム名の前に、`{テーブル名}__`をつける  
=> `'{テーブル名}__{カラム名}'`のようになる
- 複数条件で`GROUP_BY`したい場合  
`values`メソッドの引数として、それらの条件を全て列挙する

で良さそう。

# 感想

このあたりの情報をはっきりと記載している記事がなかなか見つからなかった、、、

特にポイントの3つ目については、記載されている記事を見つけられず、仕方なく勧で書いたらたまたま動いてしまった、という状況でした...ԅ(º﹃ºԅ)

そんなわけでメモ化。
（次回スムーズに同じことができる自信がない...）


# 参考にした記事

- [Django データベース操作 についてのまとめ](https://qiita.com/okoppe8/items/66a8747cf179a538355b#%E9%9B%86%E8%A8%88%E3%81%99%E3%82%8B)
- [Django 1対多(one2many)でリレーションされた子テーブルの集計値を取得する](https://igreks.jp/dev/django-1%E5%AF%BE%E5%A4%9Aone2many%E3%81%A7%E3%83%AA%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%95%E3%82%8C%E3%81%9F%E5%AD%90%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AE%E9%9B%86%E8%A8%88/)
- [Django ORMでGROUP BY ... COUNTまたはSUMを実行する方法](https://www.javaer101.com/ja/article/1048907.html)
- [Djangoで、集計処理](https://blog.narito.ninja/detail/84/)

より役に立ったと感じた順に記載しています

そしてなぜDjangoの公式サイトがヒットしない......？
公式サイトが全然頼りにならない...


[^1]: 実際には、もっとわけのわからない名前のテーブルに対してこの処理実行しており、今回はわかりやすくするためにテーブル名やカラム名を変えているので、完全にこの通りに書いても動かない可能性もあります。
【Dockerfile】DockerにConda環境を構築し、仮想環境をActivateする

CUDAイメージ上に Miniconda or Anaconda 環境を構築し、conda or pip を仮想環境にインストール、アクティベートするところまでをDockerfileで完結させるためのDockerfileを記載します。
# 実現したいこと

- CUDAイメージ上にMiniconda(Anaconda)環境を構築
- Dockerfile内で、任意の仮想環境を作成し、パッケージをインストール
- Docker run (attach)した際に、Dockerfileに記載した仮想環境下に入る(conda activateする必要がない)

# Dockerfile

```dockerfile:Dockerfile
FROM nvidia/cuda:11.2.1-devel-ubuntu20.04

RUN apt-get update && apt-get install -y \
    sudo \
    wget \
    vim
WORKDIR /opt

RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && \
    sh Miniconda3-latest-Linux-x86_64.sh -b -p /opt/miniconda3 && \
    rm -r Miniconda3-latest-Linux-x86_64.sh

ENV PATH /opt/miniconda3/bin:$PATH

COPY <env_file_name>.yml .

RUN pip install --upgrade pip && \
    conda update -n base -c defaults conda && \
    conda env create -n <env_name> -f <env_file_name>.yml && \
    conda init && \
    echo "conda activate <env_name>" >> ~/.bashrc
    
ENV CONDA_DEFAULT_ENV <env_name> && \
    PATH /opt/conda/envs/<env_name>/bin:$PATH

WORKDIR /

CMD ["/bin/bash"]
```

# NVIDIA Containers / Miniconda
[NVIDIA CUDA Image 一覧はこちらから](https://ngc.nvidia.com/catalog/containers/?orderBy=modifiedDESC&pageNumber=1&query=&quickFilter=&filters=)
[Miniconda 一覧はこちらから](https://repo.anaconda.com/miniconda/)
JavaScriptでテキスト入力チェック方法

JavaScriptでテキスト入力チェック方法です。
今回はテストプログラムでテストしています。

```HTML
<!DOCTYPE html>
<html lang="ja">
<style>
    body {
        padding:50px;
        background-color: greenyellow;
    }
    
    body #title {
        color:red;
        font-size : 50px;
    }
    body #name {
        width:200px;
    }

    body #mail {
        width:400px;
    }

    body #button1 {
        background-color: blue;
        
}

</style>
<head>
  <meta charset="utf-8">
  <title>sample</title>
</head>

<body>
  <h1 id = "title">送信フォーム</h1>
  <form action = "index4.html" method="POST">
  Name:<input type="text" id="name"><br>
  Mail:<input type="email" id="email"><br>
  Detail:<textarea id="detail" value="detail" cols="50" rows="3" maxlength="150"></textarea></br>
  year：<select id = "year">
      <option value="2021" id="2021">2021年</option>
      <option value="2022" id="2022">2022年</option>
      <option value="2023" id="2023">2022年</option>
  </select>
  month:<select id = "month">
    <option value="1" id="1">1月</option>
    <option value="2" id="2">2月</option>
    <option value="3" id="3">3月</option>
    <option value="4" id="4">4月</option>
    <option value="5" id="5">5月</option>
    <option value="6" id="6">6月</option>
    <option value="7" id="7">7月</option>
    <option value="8" id="8">8月</option>
    <option value="9" id="9">9月</option>
    <option value="10" id="10">10月</option>
    <option value="11" id="11">11月</option>
    <option value="12" id="12">12月</option>
  </select>
  <input type="button" id="button1" value="送信" onclick="func1()">
  </form>
  <div id="div1"></div>
  
  
  

<script language="javascript" type="text/javascript">
  //変数の定義
  const name = document.getElementById('name');
  const mail = document.getElementById('email');
  const detail = document.getElementById('detail');
  const button1 = document.getElementById('button1');
  
  //入力チェック処理
  const func1 = () => {
    if((name.value.length == 0 ) && (mail.value.length == 0 ) && (detail.value.length == 0 )){
      alert('名前、メールアドレス、詳細が入力されていません。');
    } else if ((name.value.length == 0 ) && (mail.value.length == 0 )){
      alert('名前、メールアドレスが入力されていません。');
    } else if ((mail.value.length == 0 ) && (detail.value.length == 0 )){
      alert('メールアドレス、詳細が入力されていません。');
    } else if ((name.value.length == 0 ) && (detail.value.length == 0 )){
      alert('名前、詳細が入力されていません。');
    } else if (name.value.length == 0){
      alert('名前が入力されていません。');
    
    } else if (mail.value.length ==0){
      alert('メールアドレスが入力されていません。');
    }  else if (detail.value.length ==0){
      alert('詳細が入力されていません。');
    } else {
      if (window.confirm('送信してもよろしいですか？')) {
        div1.innerText = `Name:${name.value}、Mail:${mail.value}、Detail:${detail.value}、year:${year.value}、month:${month.value}`;
      }
    } 
  }   
  </script>
</body>
</html>
```

length=0の部分でテキストに文字が入っていない場合はアラートメッセージで確認を促すようにしています。
外側の一時的なローカル変数を参照する際の注意点

PyQtでウィジットを作成するときなど、関数やメソッドをコールバックとして利用するために気軽に`lambda`を作成してパラメータを減らしたものをコールバックとしてセットするというようなことをすることがあります。
この場合、次の例のように、コールバックを生成するメソッド内の一時的なローカル変数を
コールバックの中で参照すると、その変数は自由変数となり、意図しない結果となる場合があるので注意が必要です。

```python
class Parent:

    def __init__(self):
        self.children = {"foo": Child(), "bar": Child()}

        for name, child in self.children.items():
            child.get_name = lambda: self.get_child_name(name)

    def get_child_name(self, name):
        return name


class Child:
    pass
```
`p.children["foo"].get_name()`は、`"foo"`を返すことを期待しますが、実際は`"bar"`を返します。

```python
>>> p = Parent()

>>> p.children["foo"].get_name()
'bar'
```
`__init__`の中で、`name`はforループで使用する一時的なローカル変数として使っていますが、2つの`Child`オブジェクトの`get_name`属性に代入される`lambda`の中で使用されているため、自由変数になります。
forループの中で最初の`Child`オブジェクトに`lambda`をセットするときは`name`には`"foo"`がセットされていますが、その後もその`lambda`は、同じ`name`変数を参照し続けるため、`name`にはその後`"bar"`がセットされて意図しない動作となるわけです。下記のとおり、`p.children["foo"].get_name()`と`p.children["bar"].get_name()`が同じ変数を参照していることが分かります。

```python
>>> p.children["foo"].get_name() is p.children["bar"].get_name()
True
```

**関連記事**
* [自由変数の名前やデータを、関数オブジェクトの中で調べる](https://qiita.com/fumitoh/items/915df59bb3d1f753d9fb)
* [ネストした関数定義のユースケース](https://qiita.com/fumitoh/items/efb44fbf3f88e8194a8d)
AWS CDKでStep Functionsを構築するまでの道のり

# StepFunctions で Lambdaを制御するシステムを AWS CDK で構築する

試行錯誤しているうちにChromeのタブが増えすぎてどのサイトを見ていたか忘れてしまう悪い癖があるので、どんな思考回路でどのリンクを参考にしたかまとめる。

## 試行錯誤

### まず CDK で簡単にリソースを作成する（S3）

とりあえずこれ参考にやってみる。
<https://dev.classmethod.jp/articles/trying-aws-cdk-tutorial-with-typescript/>

と思ったが依存関係がごちゃごちゃして意味がわからないので綺麗にする。
<https://qiita.com/non0311/items/664cf74d9ff4bad9cf46>

### Lambda 作る

こいつ参考にpythonのLambda構築する。
<https://dev.classmethod.jp/articles/aws-cdk-lambda-dependency-at-python/>

デプロイしたが怒られた。

```
TestCdkStack failed: Error: This stack uses assets, so the toolkit stack must be deployed to the environment (Run "cdk bootstrap aws://unknown-account/unknown-region")
```

指示に従う。


できた。

### StepFunctions（本命）

こいつ参考に作成。stepfunctionについても知らないこと多かったから助かった。
<https://dev.classmethod.jp/articles/developers-io-2020-connect-aws-cdk-aws-step-functions/>

## 感想

嬉しい。
Discord BOTを特定チャンネルで呼びかけると反応するようにするメモ


久々にDiscrod BOTを触ってみてます。なんか前にも同じようなメモ書いた気もしてる...

> ![スクリーンショット 2021-05-03 18.03.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/6bd16d9d-e148-ef07-8c9f-e82cf0f09f34.png "スクリーンショット 2021-05-03 18.03.37.png")

こんな感じにユーザーが発言したチャンネルによって処理のハンドリングをさせたいってメモです。

## 基本

自分用おさらいとしてメモ残しておきます。

Discord.jsのトップページのサンプルと一緒ですが、 **サンプルの方はパッと見トークンをどこに入れれば良いのか分からないので注意。**

> [Discrod.js](https://discord.js.org/#/)

```js
const Discord = require('discord.js');
const client = new Discord.Client();

client.on('ready', () => {
  console.log(`Logged in as ${client.user.tag}!`);
});

client.on('message', msg => {
  if (msg.content === 'ping') {
    msg.reply('Pong!');
  }
});

client.login(`アクセストークン`);
```

`client.login()`の箇所に取得したアクセストークンを指定して起動すればOKです。

Webhook URLなど指定せずにローカルで試せます。

まずはこれでping/pongを試せます。

## チャンネルの取得をしてハンドリング

`msg.channel.id`でチャンネルのIDを指定するか、`msg.channel.name`でチャンネル名を指定するかが良さそうです。

### チャンネルIDの場合

チャンネルIDでハンドリングしたい場合は`msg.channel.id`を利用します。

Discordのチャンネルを開いてる時のURLは以下のような形式です。

> `https://discord.com/channels/XXXXXX/YYYYYY`

**この`XXXXXXX`の部分が`サーバーごとのID`になり、`YYYYYY`の部分がチャンネルごとのID**になります。

ちなみにDMの場合は以下のような形式になります。

> `https://discord.com/channels/@me/YYYYYY`


URLの最後の`YYYYYY`がチャンネルIDになるので以下のようにハンドリング出来ます。

```js
//省略

//特定の部屋での発言
if(msg.channel.id === 'YYYYYY'){
  msg.channel.send('今日の会議の議事録はこちらです！ https://hogehoge.com');
}

//それ以外の部屋では反応しない
else{
  msg.reply('この部屋で設定されている会議はありません。');
}

//省略
```

### チャンネル名の場合

チャンネル名でハンドリングしたい場合は`msg.channel.name`を利用します。

> ![スクリーンショット 2021-05-03 18.21.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/806e50e4-c951-29da-ff1a-79477c64c0f5.png "スクリーンショット 2021-05-03 18.21.16.png")

↑のように表示されてる名前です。ただ、この場合名前が合ってればという判定なので、運用で部屋の名前を変更した際などに想定している動作にならなくなるので注意が必要そうです。

```js
//省略

//特定の部屋での発言
if(msg.channel.name === 'n0bi-playground'){
  msg.channel.send('今日の会議の議事録はこちらです！ https://hogehoge.com');
}

//それ以外の部屋では反応しない
else{
  msg.reply('この部屋で設定されている会議はありません。');
}

//省略
```

## コード全体

```js
const Discord = require('discord.js');
const client = new Discord.Client();

client.on('ready', () => {
  console.log(`Logged in as ${client.user.tag}!`);
});

client.on('message', msg => {
  if (msg.content === 'ping') {
    msg.reply('Pong!');
  }

  if(msg.content === '会議URL教えて'){
    if(msg.channel.id === 'YYYYYY'){
      msg.channel.send('https://hogehoge.com/hugahuga');
    }else{
      msg.reply('現在の部屋では設定されてるURLがありません。');
    }
  }

});

client.login(`アクセストークン`);
```

### 補足: 発言させる`msg.channel.send`と`msg.reply`

どっちもBOTを発言させるときですが、以下の違いがあります。

* msg.channel.sendを使うとそのチャンネルでBOTが発言

> ![スクリーンショット 2021-05-03 18.58.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/58dab7c7-fd83-8e7b-b45a-0d916cd2c2ec.png "スクリーンショット 2021-05-03 18.58.25.png")

* msg.replyで発言者にリプライ付きでBOTが返信

> ![スクリーンショット 2021-05-03 18.57.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/bc7d2d58-378f-28ac-82a5-4f177246a6d6.png "スクリーンショット 2021-05-03 18.57.10.png")
PHPのインストールから'Hello Worldを表示するまで'の環境構築

#はじめに

この度PHPを学習することとなりまして、環境構築を行いましたので手順を書き出しておきます。

###作業環境
MacbookPro - macOS Catalina 10.15.7

#PHPのインストール
まずなんと言ってもPHPがないことにははじまりません。
実はMacには最初からPHPが入っているのですが、最新バージョンではない可能性があるので、
初期装備とは別にPHPをインストールしていきます。

###Homebrewのインストール
パッケージ管理システムのHomebrewをインストール。

```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```

###PHPのインストール
Homebrewでインストール可能なバーションを確認します。

```
% brew search php
==> Formulae
brew-php-switcher   php@7.2             phplint             phpunit
php                 php@7.3             phpmd
php-code-sniffer    php@7.4             phpmyadmin
php-cs-fixer        phpbrew             phpstan

```

この中で一番最新のphp7.4をインストールします。

```
brew install php@7.4
```

インストール後に出てくる指示の通りパスを通します。

```
echo 'export PATH="/usr/local/opt/php@7.2/bin:$PATH"' >> ~/.bash_profile
echo 'export PATH="/usr/local/opt/php@7.2/sbin:$PATH"' >> ~/.bash_profile
```

バージョンを確認してみて自分の望んだバージョンが表示されれば成功です。

```
% php -v
PHP 7.4.18 (cli) (built: Apr 30 2021 09:55:21) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.18, Copyright (c), by Zend Technologies
```

#MAMPをインストール

MAMPはローカルのサーバー環境を提供してくれるオープンソースソフトウェアです。
これを用いてHello Worldを表示できるようにしていきます。

MAMPの[公式サイト](https://www.mamp.info/en/mac/)にアクセスしてFreeDownloadのボタンを押しましょう。
するとインストーラーがダウンロードされるので、インストーラーを開いて手順を進めるとインストールが完了します。

MAMPを開くとこんな感じの画面が表示されます。

![スクリーンショット 2021-05-03 18.45.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/769439/78a1d2a6-6429-1dcf-ff35-973269f4b4e3.png)

Document rootと書いてあるところに記載されているパスのディレクトリに入ったファイルが表示されるようになっています。
web serverにApacheとNginxを選択することができます。今回はApacheを使用します。

#hello_world.phpを作成

ターミナルからMAMPで指定されているhtdocsにvimを用いてhello_world.phpを作成します。

```
cd /Applications/MAMP/htdocs
touch hello_world.php
```

テキストエディタでhello_world.phpを開いて、以下を入力してください。

```
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<?PHP echo 'Hello World' ?>
</body>
</html>
```

ここまで準備ができたらMAMPを開いて右上のstartボタンを押します。
起動が完了してボタンが緑色に変わったらブラウザを開きましょう。

以下にアクセスするとHello Worldが表示されるはずです。

```
localhost:8888/hello_world.php
```

お疲れさまでした！！！
macOS Big Surでgrpcioやcryptographyのインストールに失敗したらSYSTEM_VERSION_COMPAT=1をつける

# tl;dr

**`SYSTEM_VERSION_COMPAT=1 pip install grpcio cryptography`**

# 問題が生じた環境

* macOS Big Sur (11.2.3)
* Python 3.8.0
* pip 20.2.4
* grpcio 1.37.0
* cryptography 3.4.6

# 経緯

Pythonのパッケージ解決をしていたら [grpcio](https://pypi.org/project/grpcio/) と[cryptography](https://pypi.org/project/cryptography/) だけインストールに失敗した。

# 原因

原因は、macOSのバージョンとその判別処理のせいらしい。
macOSのバージョンは長らく10系だったが、Big Surになりバージョンが11系になった。
grpcioやcryptographyは11系のサポートをしていないためにエラーが返り、インストールに失敗するのだ。

# 解決方法

今回はgrpcioが引っかかったが、実は他のパッケージやPython以外のソフトウェアでも似たような問題は起こりうる。
Appleもそれは見越していて、このような問題の対処のために `SYSTEM_VERSION_COMPAT` という環境変数を用意している。

`SYSTEM_VERSION_COMPAT=1` という環境変数を定義しておくと、Big Sur（11系）であってもOSのバージョンを10系列であるかのように反応してくれる。

今回の場合であれば、

```bash
SYSTEM_VERSION_COMPAT=1 pip install grpcio cryptography
```

として実行すればよい。

pipenvやpoetryを使っている場合も同様で、

* `SYSTEM_VERSION_COMPAT=1 pipenv install grpcio　cryptography`
* `SYSTEM_VERSION_COMPAT=1 pipenv install`
* `SYSTEM_VERSION_COMPAT=1 poetry add grpcio　cryptography`
* `SYSTEM_VERSION_COMPAT=1 poetry install`

などなど、とにかくコマンドの頭に `SYSTEM_VERSION_COMPAT=1` をつけておけばよい。

# 2021/09/17追記

cryptographyもだめだったので追加
【初心者向け】Eclipseのダウンロードと日本語化

日本では一般的に、日本語化プラグインとプログラミング言語別に便利なプラグインがまとめられたパッケージ「Pleadise All in One」がダウンロードされていると思います。
が、私のPCでは「Pleadise All in One」がうまくインストール出来なかったので、今回別の方法を試してみました。

###①ECLIPSE FOUNDATIONからダウンロードする
[こちらのページ](https://www.eclipse.org/downloads/)にアクセスします。
![スクリーンショット (11)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1280658/183d59e2-bc76-64ac-73ff-4af19b44796c.jpeg)
上図の左下にある「Download x86_64」というオレンジのボタンをクリックすると、ダウンロード専用ページに遷移します。
するとまた同じようなオレンジのダウンロードボタンが出てくるので、クリックしてダウンロードしてください。

###②インストーラを起動・インストール
ダウンロードしたexeファイルをクリックして、インストーラを起動させます。
![スクリーンショット (20).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1280658/ec8cac40-4abb-35e1-ecd5-9c7e9e2d8851.png)

「Eclipse IDE for Java Enterprise Java and Web Developers」をクリックします。
Webアプリ開発をしたい人に必要なJavaScriptやTypeScriptといったツールが用意されています。

![スクリーンショット (21)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1280658/124fd7fc-26a0-c7fb-6ca6-47daa461231c.jpeg)

このような画面になったら、以下のチェックポイントを参考にカスタマイズしてみてください。
**＜チェックポイント＞**
◆インストール先のフォルダを指定したい場合は、「Installation Folder」のパスを変更しましょう。
◆スタートメニューに設定する場合は「create start menu entry」をチェックします。
◆デスクトップにショートカットを設定する場合は「create desktop shortcut」をチェックします。

準備ができたら「INSTALL」ボタンをクリックして、インストール開始です！

###③Eclipseを起動
![スクリーンショット (22)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1280658/3b86dc4a-f50a-f686-c528-c1588d4378e4.jpeg)

この画面が出たら、インストール完了です。
「LAUNCH」ボタンをクリックして、いよいよEclipseを起動させます！
（起動には少し時間がかかりますが、焦らずゆっくり待ちましょう。）
![スクリーンショット (23)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1280658/b1e8bb7d-3626-ef8e-ae29-53a0f0024c0f.jpeg)

「Launch」をクリックするとEclipseの起動完了です。
しかしこのままでは英語だらけで使いづらいので、日本語化が必要です。
次のステップでは日本語化プラグインという拡張モジュールを追加していきます。

###④Eclipseを日本語化する
![スクリーンショット (24).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1280658/185950c3-8cda-b93c-9e14-d06777fc7695.png)

[Merge Doc Project](https://mergedoc.osdn.jp/)が配布しているPleadiseというプラグインを使用します。
↑のサイトにアクセスし、少し下にスクロールしたところにあるPleadiseプラグインを、お使いのPCに合わせて選びダウンロードします。
ダウンロードしたzipファイルを任意の場所で解凍していきます。

![スクリーンショット (27).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1280658/33e4a3dd-5da4-b03e-9392-6db28327794c.png)

解凍したファイルを開くと、上図のようになっていると思います。
「features」「plugins」の２つのフォルダをコピーし、①でダウンロードしたeclipseフォルダに上書き保存（貼り付け）します。
![スクリーンショット (28).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1280658/9b8cc60b-06fb-60da-32fc-0448ee0c9b0b.png)

このように「features」「plugins」が貼り付けられたらOKです。
貼り付けたら、このeclipseフォルダ内にある「eclipse.ini」というフォルダを開きます。見当たらない場合は、フォルダ内検索をしてください。
メモ帳などで開いたら、最後の行に以下の２行を足して保存します。

```
-Xverify:none
-javaagent:plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar
```

###⑤日本語化されているかの確認
Eclipseを開いたままの場合は一度閉じて、再度起動し直してみましょう。
日本語化されていたら成功です！
お疲れ様でした！
Vue Routerで固定のパスの一部をparamsにしたい

## 動機

下記のようなroutesがあったとして、"/xxx/yyy"にマッチしたときにも$route.params.aaaに"yyy"が入ってきてほしい。

```js
routes: [
  {path: '/xxx/yyy', ...},
  {path: '/xxx/:aaa', ...}
]
```

## 解決方法

```js
routes: [
  {path: '/xxx/:aaa(yyy)', ...},
  {path: '/xxx/:aaa', ...}
]
```

":aaa"のようなパラメータを設定すると、その部分を"[^/]+"という正規表現に置き換えてマッチングするんですが、パラメータの後ろにカッコ付きで正規表現を指定してやると、その正規表現が"[^/]+"の代わりに使用されます。

https://github.com/pillarjs/path-to-regexp/tree/v1.7.0
MicrosoftDocsに日英両方のテキストを表示する

# 概要

MicrosoftDocsはもうだいぶ前から`英語で読む`というトグルで気軽に日英切替できるようになりましたが、やはり日本語が怪しいと感じることがあります。
その度に英語に切替してもいいのですが、結構面倒ですしかといって最初から全部英語で読むのも語学力の関係で疲れます。

というわけで日英両方のテキストを両方表示するGreaseMonkey用スクリプトを作りました。

# userscript

```javascript:microsoftdocs-bilingual.user.js
// ==UserScript==
// @name         microsoftdocs-bilingual
// @namespace    https://gist.github.com/ryuix
// @version      0.1
// @description  dispalay ja-JP and en-US texts.
// @author       ryuix
// @match        https://docs.microsoft.com/ja-jp/*
// @require      http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
// @icon
// @grant        none
// @noframes
// ==/UserScript==

$(function () {
  "use strict";
  function addBlockQuoteStyle() {
    const head = document.getElementsByTagName("head")[0];
    if (!head) {
      return;
    }
    const style = document.createElement("style");
    style.type = "text/css";
    style.innerHTML = `.biLangBlockQuote {
      border-radius:6px;
      border:1px solid;
      background-color: #efefef;
      border-color:#efefef !important;
      margin:0.3em 0 !important;
      display:block;}`;
    head.appendChild(style);
  }
  function addVisilityButton() {
    if (document.getElementById("biLangToggle")) {
      return;
    }
    const button = document.createElement("input");
    button.id = "biLangToggle";
    button.type = "button";
    button.value = "switch";
    button.style.position = "fixed";
    button.style.top = "90%";
    button.style.right = "1em";
    const blocks = document.querySelectorAll("blockquote.biLangBlockQuote");
    button.addEventListener("click", () => {
      for (const element of blocks) {
        if (element.style.display === "none") {
          element.style.display = "block";
        } else {
          element.style.display = "none";
        }
      }
    });
    document.querySelector("body").append(button);
  }
  // https://qiita.com/nagtkk/items/e1cc3f929b61b1882bd1
  function groupBy(array, getKey) {
    return Array.from(
      array.reduce((map, current) => {
        const key = getKey(current);
        const list = map.get(key);
        if (list) list.push(current);
        else map.set(key, [current]);
        return map;
      }, new Map())
    );
  }
  const enDescriptions = document.querySelectorAll("span[data-stu-id]");
  if (enDescriptions.length === 0) {
    return;
  }
  addBlockQuoteStyle();
  const paragraphGroup = groupBy(
    Array.from(enDescriptions),
    (x) => x.parentNode.parentNode
  );
  for (const paragraph of paragraphGroup) {
    const fragment = document.createDocumentFragment();
    for (const span of paragraph[1]) {
      fragment.appendChild(span);
    }
    const quote = document.createElement("blockquote");
    quote.className = "biLangBlockQuote";
    quote.appendChild(fragment);
    paragraph[0].appendChild(quote);
  }
  addVisilityButton();
});
```

# 使い方

下図のように段落毎に英語テキストが表示されます。
邪魔な場合は右下のswitchボタン(ださい…)を押すと通常の一言語表示に戻ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1135585/8a847e96-88da-048f-079c-571589bcc8f2.png)


# Gist

https://gist.github.com/ryuix/7c4b1bf8273e29a56196115a9f8175cf
Office 365　で中小企業が構築したいセキュリティまとめ（MS-900のモジュール2にも出るよ）

# Office 365　で中小企業が構築したいセキュリティの情報をまとめる

（前置き） 今回は、中小企業がいいかんじにセキュリティを強くするためのOffice365の使い方の情報のメモをまとめてます。別記事にかいたけど、IPAとか世の中の良いとされているオーソリティの情報が、LANとかIPSとかFireWallとかのバウンダリの話題がでてくる（オンプレ前提な）ので全く参考にならないことが多いのでなるべく若い企業向けに情報をシェアします。

良さそうな企業・組織

* 設立5年以内
* チームのITの仕組みはこれから
* Google Drive, Slack, Chatworkなどのツールは一通り普通に触ったことがある
* 今後の社内統制に Microsoft のツールはありだと思っている。（監査対応や中途採用など色々な理由で）

主な出典はなるべく、Microsoftの公式を参照していますが、独断と偏見で間違っていることをいうかもしれませんが、結構加工しています。僕の印象で、Microsoftの公式ドキュメントは、用語がわかりにくく、ちょっとお堅い感じがしたのが理由です。（ただし MS-900のモジュール2にも出る最低限は抑える）


# セキュリティから考える、Office 365のライセンス


昔、Office の価格というと、ユーザー側のソフトウェアの機能の差異が、ごちゃごちゃしてましたが、いまはシンプルです。

判断ポイントは以下の通り。

* クライアント（PC）にOfficeのアプリを導入したいかどうか？
* 個人のファイルスペースとしてのクラウド（OneDrive）を使いたいかどうか？
* グループウェア系のサービス（Teams、Sharepointなど）を使いたいかどうか？
* 端末の管理、文書の管理をしっかり仕組みでやりたいかどうか？




![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1273644/9c389d2e-51f1-c9ab-a935-1795d4c1763a.png)

全部、Yesだと、Microsoft 365 Business Premium　になります。

よく忘れられるポイントなのですが、Office 365を購入する一番の理由、顧客メリットは、管理ポータルです。
これが使えること、管理者が、メンバー（社員、社長含む）に対してどのようなライセンスを付与するべきか、どのような利用状況をしているのか？ 万が一パスワードがわからなくなった場合のリセットは？ といった管理業務ができること、、ということなので、基本売り切りライセンスはあんまりお勧めできません。

一人の組織だったとしても、セキュリティとかITの仕組みを常識的に身につけた方がいいと思うので普段から、買い切りではなく、Office 365をお勧めします。

ちなみに、Premiumはセキュリティが向上するのですが、セキュリティ機能は単体でも購入が可能で、Azure AD P1として購入できます。


#### パターン1　個人事業主

個人事業主は、Microsoft 365 Apps　900円でよいかとおもいます。だれかとシェアするものが少ないので。。

#### パターン2 ハーフコミットの集まり

ハーフコミットといえど、情報のシェア、チャットの履歴の保管、セキュリティなどをきにするとおもうので、
基本は、Business Basic が安くていいとおもいます。
Office アプリはきっと本業？でもっているとおもうので省略。


#### パターン3 少人数の組織（👈大抵の場合はここ）

Standard が良いと思います。端末セキュリティや文書セキュリティの機能がついていないのですが、
数人の組織で、信頼関係でお互いのモバイル端末が、パスコードをかける、といったことを声がけで
なんとか運用できるなら、仕組みを無理やりはめなくても大丈夫。

#### パターン4 次のフェーズ（数十人への成長）を意識し始めたら

いわゆる情報システム部を置いて、社内のいろいろなことを管理しないといけないとなると、Microsoft 365 Businesses Premiumが一番コスパがいいです。ライセンスだけ考えるとちょっと高く見えるかもしれませんが、Azure AD P1の機能がフルで使えるなら、結果的に安くなります。

>  Microsoft 365 Business Premium
> = [ Microsoft 365 Business Standard ]  +
>   [ EMS {Intune + + AIP P1 } ] + 
>   [ Windows 10 Business ]

もし、Standardのまま、社員のセキュリティを担保しようとしたら・・・。たとえば、EMS（Enterprise Mobility+Security） を別に購入したり、シングルサインオン、HENGEといった高度な認証ソリューションを（別費用で）買うことになり、一つ一つを積み重ねていくとトータルでは、割と高くつきます。

#### パターン5 IPOとかMAの準備で特定の課題がでてきたとき

以下の機能表をみると、最後は、Microsoft 365 E5にしないと全ての機能が使えないようなのです
https://www.microsoft.com/en-us/download/details.aspx?id=103006

が基本的には Microsoft 365 Business Premium でできることが多いので、ほとんどの中小企業は、Business Premium のままでも大丈夫だと思います。ですので、基本的にPremium でもいいような気がしますが、もしかしたら、AIによる脅威検知をオプションで追加しても良いかもしれません。その場合は、Microsoft Defender for Office 365 というのがあります。さらに増えてもIDごとに、月額で六百円程度です。 https://docs.microsoft.com/ja-jp/microsoft-365/security/office-365-security/overview?view=o365-worldwide セキュリティは、Microsoft 365になるべく寄せた方がコストが安くなります。



# オプション系のサービスも一応みてみる（試験対策）

基本は、 Microsoft 365 Business Premium でセキュリティはいいかんじんなりますが、オプション系でどんなものがあるのかと言う情報は整理しておいた方が良いです。この知識は、MS-900にも出るかもしれないのでどちらかと言うと試験対策にまとめました。




# Microsoft 365 の ID およびアクセス管理機能について説明する

https://docs.microsoft.com/ja-jp/learn/modules/describe-identity-access-management-capabilities-of-microsoft-365/
WaniCTF'21-spring Easy Writeup

python力が問われる問題。
力づく（ 総当たり × 総当たり ）で解いた。

## 問題

Easy
166pt Easy
手始めに
encrypt.pyとoutput.txtが渡される

```python:encrypt.py
with open("flag.txt") as f:
    flag = f.read().strip()


A = REDACTED
B = REDACTED

plaintext_space = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}"

assert all(x in plaintext_space for x in flag)


def encrypt(plaintext: str, a: int, b: int) -> str:
    ciphertext = ""
    for x in plaintext:
        if "A" <= x <= "Z":
            x = ord(x) - ord("A")
            x = (a * x + b) % 26
            x = chr(x + ord("A"))
        ciphertext += x

    return ciphertext


if __name__ == "__main__":
    ciphertext = encrypt(flag, a=A, b=B)
    print(ciphertext)
```

文字の置換に使用されているパラメータ
変数A , 変数B が消されている。

```:output.txt
HLIM{OCLSAQCZASPYFZASRILLCVMC}
```

FLAG{ を暗号化した結果が
HLIM{ なので，
総当たりで A , B を求めた後，
ciphertextに一致するplaintextを総当たりで求めるソルバーを作成する


```python:solver.py
def encrypt(plaintext: str, a: int, b: int) -> str:
    ciphertext = ""
    for x in plaintext:
        if "A" <= x <= "Z":
            x = ord(x) - ord("A")
            x = (a * x + b) % 26
            x = chr(x + ord("A"))
        ciphertext += x

    return ciphertext

plaintext_space = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_{}"

flag = "FLAG"

A=0
B=0

i = 0
while i < 26:
    j = 0
    while j < 26:
        ciphertext = encrypt(flag, i, j)
        if ciphertext == "HLIM":
            print("bingo!")   
            print("A="+str(i))
            print("B="+str(j))
            A = i
            B = j
        j = j + 1
    i = i + 1

plaintext = ""
ciphertext = "HLIM{OCLSAQCZASPYFZASRILLCVMC}"

for i in ciphertext:
    for j in plaintext_space:
        c = encrypt(j, A, B)
        if c == i:
            plaintext += j

print(plaintext)
```

実行結果

```
>python sol_cry-easy.py
bingo!
A=5
B=8
FLAG{WELCOMETOCRYPTOCHALLENGE}
```

ビンゴ！

作問者writeupみたら数学的に解いてる。
　
　
　
作問者writeup

https://github.com/wani-hackase/wanictf21spring-writeup/tree/main/cry/easy
ディレクトリ構造を崩さずに、PDFのパスワードを一括解除する

# Summary
1箇所のフォルダにまとめたPDFファイルを、ディレクトリ構造はそのままに、パスワード保護を解除します。パスワードは既知です。

《追記》Pythonで再帰的プログラミングを使う方法を別の方に教えていただきました。

# いきさつ
全て同じパスワードでロックされた3階層にまたがる48ディレクトリ・806のPDFファイルのパスワード保護を解除したくなりました。大学で配布される資料が漏洩時に備えてすべてロックされているけれど、私的利用に限って解除したくなることありますよね...？

※配布者に無断でロック解除したファイルを再頒布する行為はお控え下さい。

# 環境
- Windows10 ビルド19042
- WSL2 Ubuntu 20.04.2 LTS
- Qpdf
- zsh

# ディレクトリ構成

```zsh
.
├── execute.sh
├── password.txt
└── pdf_input #このディレクトリ以下のPDFを全てロック解除したい
    ├── hoge1
        ├── fuga1
      　︙
        └── fugaN
    ├── hoge2
  　︙
    └── hoge6
```

今回作ったシェルスクリプト`execute.sh`と同じディレクトリに、パスワードだけを記述した`password.txt`と、変換対象のディレクトリ`pdf_input`を配置します。

# シェルスクリプトを使った書き方
ディレクトリ構造だけをコピーする部分が冗長になってしまいました。よりよい方法があれば是非ご一報下さい。

```zsh:Qpdfのインストール
> sudo apt update
> sudo apt install qpdf
```

Qpdfが未導入の場合は導入して下さい

```zsh:execute.sh
#! /bin/zsh

# パスワードの取得
PASSWORD=`find -iname password.txt | xargs cat | head -n 1`

# ディレクトリ構造のコピー
mkdir temp_dir
find pdf_input -type d | xargs -I DIR mkdir temp_dir/DIR
mv temp_dir/pdf_input pdf_output
rm -R temp_dir

# 進捗表示用にファイル数を取得
FILE_NUMBER=`find pdf_input -name "*.pdf" | wc -l`
COUNT=0

# ファイルの復号化
for FILE_IN in `find pdf_input -name "*.pdf"`; do
  (( COUNT += 1 ))
  FILE_OUT=`echo ${FILE_IN} |
  sed -e "s/pdf_input\(.*\)\.pdf/pdf_output\1_unlocked\.pdf/g"`
  qpdf --decrypt --password=${PASSWORD} ${FILE_IN} ${FILE_OUT}
  printf '\r Progress : [%d/%d]' ${COUNT} ${FILE_NUMBER}
done
```

また、パスワードを別途`password.txt`に保存します。パスワードがpiyopiyo123の場合は以下の通りです。

```text:password.txt
piyopiyo123
```

ここまで準備ができたら実行するだけです。

```zsh
> ./execute.sh
```

実行が完了すると、`pdf_output`という名前のディレクトリにパスワード解除済みのPDFファイルが生成されています。なお、解除後のファイル名には「_unlocked」を付しています。

# 再帰的プログラミングを使った書き方
※これは別のアプローチです。結果に変わりはありません。

やりたいことを整理しなおすと、それぞれのディレクトリ階層において、

「あるオブジェクトがpdfファイルであれば複合化し、ディレクトリであれば、そのディレクトリ下で**このかぎかっこ内**のプログラムと同様の処理をするプログラム」

が全ての階層で行われればよいことになります。このような同一処理が繰り返し出現する構造の課題に対しては、再帰的プログラミングを用いるとコードが簡潔になったりします。

参考までにpythonコードを載せておきます。

```Python
import os
import pikepdf

password = 'looool'
target_dir = '/pdf_input'
os.mkdir(os.getcwd() + '/pdf_output')

def recursive_decrypt(x, y):
    for f in os.listdir(x):
        ObjPath = x + '/' + f
        NewObjPath = y + '/' + f 
        if os.path.isfile(ObjPath) and ".pdf" in str(ObjPath):
            pdf = pikepdf.open(ObjPath, password)
            pdf.save(NewObjPath)
        elif os.path.isdir(ObjPath):
            os.mkdir(NewObjPath) 
            recursive_decrypt(ObjPath, NewObjPath)

recursive_decrypt(os.getcwd() + target_dir, os.getcwd() + '/pdf_output')
```

# トラブルシューティング
シェルスクリプトを実行できない場合は以下の@1915kekeさんの記事をどうぞ。
[シェルスクリプトを実行できるようになるまで(基本知識）](https://qiita.com/1915keke/items/c8f12b303cedf17c0e17)

また、`pdf_output`というディレクトリが既に存在しているとエラーになります。

# 参考記事
[QPDFを用いたPDF便利操作集](https://qiita.com/mst8000/items/0906d7e63f8a0e427b67)