OmniAICreator/Qiita-1.07M huggingface dataset:
【覚えておくと便利!!!】MySQLの文字コード変更

## 1.事前知識
- [【初心者でもできる‼】Windows10にMySQLをインストールする方法（MySQL Server 8.0.20）](https://qiita.com/ryome/items/68766f81f0668ff6876b)

事前知識として、上記リンクの内容が必要です。


## 2.MySQLの文字コード変更

![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/1e9bfa88-2b2e-5ac9-7982-30682da1c549.png)
1. `mysql -u ユーザ名 -p` でログインし、 `status` で `文字コード` を確認する。
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/d6c753e9-1caa-3ca0-1891-8c7fe34b44aa.png)
2. `フォルダ` を開き、 `[表示]→[隠しファイル]` にチェックを入れる。
![03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/edc5f36b-b48a-2bfe-831f-3bb7e7fb569e.png)
![04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/12ed091e-c822-ea4d-9d6d-9b10d48202b4.png)

```:my.ini
[client]
default-character-set=utf8
```

3.`エディタ` を開き、上記の文をコピーして、ファイル名を `my.ini` で `デスクトップ` に保存する。

![05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/9c07a8e9-3ba5-70bc-2a01-eaf45288d220.png)
4.`C:\Program Files\MySQL\MySQL Server 8.0` を開き、デスクトップから `my.ini` を移す。
![07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/d181df00-808a-1a22-e4e5-56a14c02b95f.png)
5.`C:\ProgramData\MySQL\MySQL Server 8.0` を開き、 `my.ini` を `エディタ` に表示する。
![08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/0598f6ff-1727-7a60-cddb-80216fe29564.png)
6.`100行目` に `character-set-server=utf8` を加える。
![09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/0111120d-d00a-ba88-4ff0-73d319a024d8.png)
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/419016bc-88c4-3bec-d989-59586f9e9eb9.png)
7. 検索ボックスに `サービス` と入力して起動する。
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/096b60fd-5c48-70c2-0a45-db09e54a7626.png)
8. `MySQL8.0` を選択し、 `サービスの再起動` をクリックする。
![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/73a0e22e-c500-a42a-f60f-82f6ddc13bb3.png)
![23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/351c92f9-efad-b067-c284-7f8cf28112bb.png)
9. 検索ボックスに `cmd` と入力し、 `コマンドプロンプト` を起動する。
![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449867/aba47769-e9c6-4b56-9fa6-96619546fa41.png)
10. `mysql -u ユーザ名 -p` でログインし、 `status` で `文字コード` を確認する。
11. 文字コードが `utf8` になっていれば成功。

## 3.関連
- [【最新】UbuntuでJavaの環境を構築する方法](https://qiita.com/ryome/items/37c53e9638a9c6ea146a)
- [【初心者でもできる‼】Windows10にJava環境を作る方法（JDK14.0.1）](https://qiita.com/ryome/items/30135570954e36196821)
- [【初心者でもできる‼】Windows10にEclipseをインストールする方法（Java環境構築）](https://qiita.com/ryome/items/b47c5acdaa52cffbec58)
- [【超簡単!!】CentOSにLAMP環境を構築する方法](https://qiita.com/ryome/items/931abd54c9b8a46c9722)
- [Ubuntu 18.04にLAMP環境を構築する手順](https://qiita.com/ryome/items/be8a811a5a7c175d7c7b)
- [【初心者でも簡単!!】WordPressをダウンロード及びインストールする方法](https://qiita.com/ryome/items/ca73897b67dc9c4a3df0)
サーバー認証まわりの基礎（独自SSL証明書、DNSなど。Amazon CloudFrontでの例。）


いつもいつも忘れてしまうので、備忘のため書いておくことにしました。本当に忘れっぽくて困る・・。

# サーバー認証とは

サーバー認証とは、クライアントが、これから接続する相手であるサーバーの正当性を確認することです。

正確には、「サーバーの公開鍵」が正当なものかを確認することです。

# サーバー認証の流れ

クライアントは、サーバーからサーバー証明書を受け取ります。

このサーバー証明書には、以下が記載されています。

1. サーバーの公開鍵（これがメインの情報）
* その他属性
* 上記をまとめてハッシュ化し、更にそれを認証局の秘密鍵で暗号化したもの（＝ 認証局の署名）
* その時に利用されたハッシュ関数の名前、暗号化方式

クライアントは、サーバーの公開鍵が信頼できるものか確認するために、サーバーの公開鍵に認証局のお墨付きが付いているかを確認します。

具体的には、クライアントは以下を行います。

* **認証局の公開鍵**と、4に書かれてある暗号化方式をもとに、3を復号します。（もともと**認証局の秘密鍵**で暗号化されているわけですから、認証局の公開鍵で復号できるわけです。）
* 4に書かれてあるハッシュ関数で、1と2をまとめてハッシュ化します。
* 両者が一致しているか確認します。

一致していれば、サーバーの公開鍵は認証局が署名した（信頼した）ものである、ということをクライアントは確認できます。

サーバーには、こういったサーバー証明書がインストールされていないと、クライアントからまともなサーバーだと信じてもらえず、TLS通信を開始することができません。

# サーバー証明書とドメインの関係

サーバー証明書は、「サブドメイン名」と「ホスト名」を含むFQDNに対して発行されます。

例えば

* example.com
* sub1.example.com
* sub2.example.com
* userCompany1.example.com（マルチテナントサービス）

といった感じです。

また、このように個別で指定するのではなく、

* *.example.com

といったように、ワイルドカードを指定することもできます。この場合、「*.example.com」に対してサーバ証明書が発行されます。

ただ、ワイルドカード指定の証明書では、example.comの事業者の実在性は証明できるものの、

userCompany1.example.com

のuserCompany1の実在性までは証明できません。userCompany1の実在性を証明する必要があるならば、ワイルドカードで表現されたドメイン名の証明書ではなく、

userCompany1.example.com

のサーバー証明書を取得する必要があります。

こういった理由から、独自ドメインの独自SSL証明書（サーバー証明書）というものが必要となります。


### 具体例（Amazon CloudFront）

例えば、AWSのCloudFrontでは

*.cloudfront.net

のサーバー証明書はAWSが取得済みですので、CloudFrontが払い出してくれる

xxxx.cloudfront.net　（xxxxはCloudFrontディストリビューションごとに異なる）

を呼び出し元に公開するのであれば、サーバ証明書についての手続きは不要です。

しかし、これではxxxxのコンテンツオーナーの実在性は証明されません。xxxxのコンテンツオーナーが自社の実在性を証明したければ、独自のドメインを取得し、このドメインのサーバ証明書を取得する必要があります。

例えば

userCompany1.com

のドメインを取得して、このドメインのサーバ証明書を取得するのです。

そして、このドメインをDNSで名前解決できるように、

userCompany1.com 　→　 xxxx.cloudfront.net

という紐付けを表現するCNAMEレコードを登録します。

（なお、xxxx.cloudfront.netとIPアドレスを紐づけるAレコードについては、当然ながらAWSが作成してくれます。）

# 参考
https://xtech.nikkei.com/it/article/COLUMN/20060725/244233/
https://www.digicert.co.jp/welcome/pdf/wp_ssl_domain.pdf
IQ Bot：チェックボックスの抽出　基本編

[IQ Bot](https://qiita.com/IQBotter/items/fab5f59230ff38a98bbd)では、チェックボックスの抽出ができます。

この記事では、基本編のやりかたを紹介します。

#IQ Botによるチェックボックスの抽出　基本編

IQ Botによるチェックボックスの抽出の基本のやりかたは、こちら↓の動画のとおりです。
![チェックボックス（４倍速）.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/0a563e6f-796b-7e81-214d-0d4f04b5966d.gif)

##基本編のポイント

- 「データタイプ」で「checkbox」を選択
- 「フィールドラベル」はチェックボックスの選択肢（男性、女性など）の文字列を選択
- 「フィールド値」でチェックボックスの範囲を選択（ラジオボタンでもOK）

#基本編で抽出できるチェックボックスの種類

以下のタイプのチェックボックスに対応できます。

- レ点チェック
- ラジオボタン
- 四角の中に×でチェックがしてあるタイプのチェックボックス
- 黒四角（■）でチェックがしてあるタイプのチェックボックス

##基本編のデメリット

基本のやりかたでは、手軽な操作で様々なタイプのチェックボックスに対応できる一方、以下のようなデメリットもあります。

- 画像処理を使うため処理が重くなる
- チェックボックスの場所をかなり精緻に指定しないと結果を誤る場合がある

また、出力結果のCSVは以下のような形式になります。

＜イメージ1＞
![チェックボックス_抽出結果.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/936c26bb-2378-24ed-00fe-9e05b54b7ddc.jpeg)

##応用編への入り口

出力結果のCSVを、例えば以下のような形式にしたい場合はどうすればよいのでしょうか？

＜イメージ2＞
![チェックボックス_抽出結果_応用.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/9c65383f-7105-ad85-5e75-6e383fd03f62.jpeg)

ひとつのやりかたは、RPA側でCSVを加工することです。
ですが、ちょっと回りくどい。

[カスタムロジック](https://qiita.com/IQBotter/items/1d8e7c77ad47e838476c)を使えば、IQBot側で一発で上記の形式のCSVを出力することができます。

そんなわけで、応用編では上記のやりかたをご紹介したいと思います。
Qiita APIを使って特定タグが付いた記事一覧をLINE通知する


# つくったもの
GASでAPIを使う練習として、
「Qiita APIを使って特定のタグ（今回はGoogleappsscript）が付いた記事の最新上位5記事を特定のLINE通知する。」
スクリプトを作りました。

# Qiita API ドキュメント
使用したQiita APIはこちら
> [Qiita APIドキュメント]指定されたタグが付けられた記事一覧を、タグを付けた日時の降順で返します。
> https://qiita.com/api/v2/docs#get-apiv2tagstag_iditems


#つくってみる

## １．LINE Notifyトークンの取得
LINE Notifyトークンの取得方法は下記に記載しています。

[GAS]GASでスクレイピングしてLINEグループとChatworkグループへ通知する
https://qiita.com/jooji/items/a779e5b98f0186458093

## ２．コード作成

```javascript:GAS
function getQiitaArticle() {
  var tag = "GoogleAppsScript" //取得したい記事のタグを指定する
  var endpoint = "https://qiita.com/api/v2/tags/" + tag + "/items"
  var paramStr = "?per_page=5&page=1"; //1ページにつき5記事を1ページ分。つまり5記事。
  var res = UrlFetchApp.fetch(endpoint + paramStr)  
  var json = JSON.parse(res.getContentText());
  
  var message
  json.forEach(function(item){
     message += item["title"] + "\n" + item["url"] + "\n" 
  });
  //プロパティストアに格納したLINEトークンを取得する
  LINE_TOKEN = PropertiesService.getScriptProperties().getProperty('LINE_TOKEN');
  var options =
   {
     "method"  : "post",
     "payload" : "message=" + message,
     "headers" : {"Authorization" : "Bearer "+ LINE_TOKEN}
   };
   UrlFetchApp.fetch("https://notify-api.line.me/api/notify",options);//LINE通知する
}
```

## ３．トリガーを作成する。
編集→現在のトリガーをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/3028b87a-92f7-a4d4-26ab-eaff5212f88e.png)

トリガー管理画面になります。右下のトリガーを追加をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/39bc36a9-62ac-4064-048f-95784a24c73f.png)

毎日一回通知を受け取りたいので、イベントのソースを”時間主導型”、時間ベースのトリガータイプを”日付”、時刻は好きな時間を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/2b808d8d-139e-4247-4c21-8c096b7ce545.png)

# 補足
- LINEトークンはべた打ちせずにプロパティストアを使ってみました。
- Qiitaトークンはこのページの内容だけでしたら不要です。操作するAPIによっては必要です。今回5記事だけですが、取得ページが多い場合トークンによる認証が必要かもしれません。詳しくはドキュメントをご確認ください。

# 結果
LINEで記事一覧を受け取れました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342467/88905391-1366-294f-a672-166a585873b5.png)

# 次回
つぎは、LineBotと対話して記事一覧を受けれるようにしたいと思っています。


# 参考にしたサイト
プロパティストアについては下記Webサイトを参考にしました。

【初心者向けGAS】プロパティストアの概要とスクリプトプロパティの編集方法
https://tonari-it.com/gas-property-store/
【初心者向けGAS】スクリプトプロパティを操作してそのデータを取り出す方法
https://tonari-it.com/gas-properties-script-property/
zshのprofile，ついでにSAC(Seismic Analysis Code)のMACでの設定の仕方

最近MacBookAir2020を買って，ログインシェルがzshでびっくりした．
MacBookAirの最新モデル．

元々は，岡山大学での研究室ではLinux Vine，自分の所有端末はLinux Mintを使ってて，両方でbashを基本的に使っていた．
そのためか，zshでのprofile設定がいまいちわからず，bashじゃないのに，.bash_profileと.bashrcで設定しようとしたりして，四苦八苦の上，やり方わかったけど，忘れそうなので，それの備忘録．

きっかけは，SAC(Seismic Analysis Code)というIRISの地震波解析用のパッケージを使おうと思ってたが，コマンドパスがなぜか古いバージョンに繋がってしまって，修正しようとも方法を思いつかず，やむなく，ログイン時に実行されるprofile(結局はrc)設定で，無理やりエイリアスを設定しようということだ．

SACは古いバージョンが，/usr/local/bin/sacに繋がってて(なんで？)，これではうまく動かなかった．
新しいバージョンでは，/usr/local/sac/bin/sacがコマンドパスだが，どうしても先に古いバージョンの方が先に引かれてしまう(今となっては，サーチ順を環境変数で先にすれば良かった．)．．．

そこで，先にログインの時点で，

「新しいバージョンの方にコマンドパスが繋がるように，作ってしまえばいい」

と考えて，いつものノリで，.bash_profileと，.bashrcで設定しようとした．

だが，ログインシェルが違うと，profileも異なるのは当たり前で，うまくいかなかった．
そこで，調べてみると，zshでは，

.zprofile
.zshrc

が基本的なzshのprofileのようだった．


ps.
以下は，SACも含めた解説．
詳細は，SAC for MACOS XのREADMEを参照．
SACを動かすには，Xquartzを入れておくと良い(波形などの出力はデフォルトでXquartz)．
その上でSAC for Mac OS Xの中身をルートディレクトリのどこかにコピーしておく．
大体は，/usr/local/sac/というディレクトリを作っておいて，そこにコピーする．
必ず，このSACディレクトリにPATHを繋げておく．

```quite.rb
$cp (SAC for MacOS X) /usr/local/sac/ 
#SAC for MacOS XはIRISからもらえるファイル
```

その後，bashの環境ではbashrc，zsh環境ではzshrcに以下を記述．

```quite.rb
export SACHOME=/usr/local/sac/
. ${SACHOME}bin/sacinit.sh
export SACAUX=${SACHOME}/aux/
```

この後，

```quite.rb
$. ~/.zshrc
```

を実行して，

```quite.rb
$${SACHOME}/bin/sac/ #SACの実行ファイル
```

とやると，とりあえずうまくいく．
毎回zshrcを実行して，これするのは面倒なので，再びzshrcを開いて，

alias sac="${SACHOME}bin/sac"

と記述し，~/.zprofileに以下を記述．

```quite.rb
if [ -fi ~/.zshrc ]; then
  . ~/.zshrc
fi
```

これで，

```quite.rb
$. ~/.zprofile
```

を実行すると，以降

```quite.rb
$sac
```
だけで，SACが使える．
Javascriptを使って文字が大きくなるアニメーションを作ろう

#Javascriptを使って文字が大きくなるアニメーションを作ります。

完成見本
[リンク]https://i.gyazo.com/5f09aecd2bc47701dd396d3d515e703d.gif


```index.html
<body>
    <h1 id="text">この文字が大きくなります</h1>
    
    <script>
        //JavaScriptを記述する場所
    </script>
</body>
```

h1の文字にアニメーションを加えるので、getElementById()を用いてID属性の要素を取得します。

```index.html
<body>
    <h1 id="text">この文字が大きくなります</h1>
    
    <script>
        var h1 = document.getElementById("text");
    </script>
</body>
```

CSSのmargin、fontSizeなどの値を変化させていけば動いているように見えます。




```index.html
<script>
    var h1 = document.getElementById("text");
    let count = 0;

    var timer = setInterval(() => {
        h1.style.fontSize = count + `px`;
        count++;

        if (count >= 100) clearInterval(timer);
    }, 10);
</script>
```

この記述により、fontSizeが0から100になるまで1ずつ加算されます。
setInterval()を用いて処理が見えるようにします。最後に記述している10が繰り返し処理の速度です。
claerInterval()メソッドにより繰り返し処理を終了させます。

これでアニメーションをつけることができました。
5 月 LeetCode 挑戰, W1D1, First Bad Version, in Swift

五月 LeetCode 也有挑戰活動，應該是跟疫情多少有關係。
由於回到第一週變得簡單許多所以有鬆了一口氣。

演算法與資料結構： `Binary Search` `Array`

## 題意和解法關鍵

- 挑戰頁面： <https://leetcode.com/explore/challenge/card/may-leetcoding-challenge/534/week-1-may-1st-may-7th/3316/>
- 原題目： [278. First Bad Version](https://leetcode.com/problems/first-bad-version/)

給出一個最大數，找出傳入 isBadVersion 方法會是 false 的第一個數值並回傳。

### 關鍵句子

> You should minimize the number of calls to the API.

根據這句話就可以確定這是一題二元搜尋法的題目。

為什麼這麼說呢？

從題意來看最簡單的走法方式會是依序走訪 O(n) ，而有了這句話就會知道 LeetCode 的編譯器十之八九會讓 O(n) 超時或是超過存取次數。

而降這種解法的時間複雜度的方式就只剩下二元搜尋法 (binary search) 。在用了二元搜尋法之後的時間複雜度，就會降到 O(logn)。

### 相關題目

相關題目可以看四月的這題： [Leetcode 30 天挑戰, Week 3 Day 7, Leftmost Column with at Least a One, in Swift](https://qiita.com/vc7/items/7022af54c5e7b0703366)

而這題的題目也有出現類似的關鍵句子阻止我們用逐項走訪：

> Submissions making more than 1000 calls to BinaryMatrix.get will be judged Wrong Answer.

這一類句子就是「快用用看 binary search 吧！」的強烈訊號。

## 二元搜尋法的走法

是基本二元搜尋的變形。

- 如果該步驟的正中間為 false ，代表 false 的開頭會在 **左** 半邊，因此把 middle 指定給右指標，接著下個步驟就可以搜尋左半邊。
- 反之，若該步驟的正中間為 true ，代表 false 的開頭會在 **右** 半邊，因此把左指標改成 middle + 1，接著下個步驟就可以搜尋左半邊。
  - +1 是因為 middle 已經不符合需求，所以找 middle 的下一個位置
- 全部走訪完成之後，left 就會是結果

## 程式碼

``` swift
class Solution : VersionControl {
    func firstBadVersion(_ n: Int) -> Int {
        var head = 1 // 因為是從 1 開始，所以要注意不要從 0 開始
        var tail = n // 因為是到 n 為止，所以要注意上限不是 n - 1
        var middle = 0
        while head < tail {
            middle = head + (tail - head) / 2
            if isBadVersion(middle) {
                tail = middle
            } else {
                head = middle + 1
            }
        }
        return head
    }
}
```

## 複雜度分析

n 為傳入的 n 的值。

- 時間複雜度： O(logn)，因為是對一維陣列進行二元搜尋就是 O(logn)
- 空間複雜度： O(1)，只有用常數個變數

## 執行結果

``` text
Runtime: 20 ms (beats 95.63%)
Memory Usage: 20.9 MB
```

middle 的宣告放在 while 裡面的話 runtime 會慢！
GithubFlowにまつわる専門用語

###Github Flow
Github Flowとは、Githubを使用した開発フロー（複数人での開発手順）であり、複数人で開発するときに、Github上でのやり方をまとめたもの。

###issue
issueとは、Githubで開発すべき機能の開発用件をまとめたもの。
要は、複数人でも開発できるように、１個のサービスを機能ごとに細分化したタスクである。

###アサイン
アサインとは、タスクを振り分けること。Github上で自分にアサインすることができる。

###プルリクエスト
プルリクエストとは、ローカルから送られてきたデータを、Githubのmasterブランチにマージするためのリクエストのこと。

####今日の一言（2020/05/01）
　今日はスクールの同期と初めての顔合わせだった。いままで、１人でやってきたので仲間が増えたのでとても嬉しい。毎日、更新していきたい。
【正規表現】メタキャラクタ

①単一文字メタキャラクタ

|記法|意味|
|:-----------------|:-----------------|
|.|任意の一文字|
|\w|英単語を構成する文字|
|\W|英単語を構成する文字以外|
|\s|空白文字|
|\S|空白文字以外|
|\d|数字|
|\D|数字以外|

②量指定子メタキャラクタ
※〇は任意の文字列。nとmは任意の数字。

|記法|意味|
|:-----------------|:-----------------|
|〇*|0回以上の繰り返し|
|〇+|1回以上の繰り返し|
|〇{n}|n回の繰り返し|
|〇{n,}|n回以上の繰り返し|
|〇{n,m}|n回以上m回以下の繰り返し|
|〇?|0回または1回|

③集合メタキャラクタ

|記法|意味|
|:-----------------|:-----------------|
|[〇〇〇]|いずれかの文字|
|<notextile>〇〇〇|〇〇〇</notextile>|いずれかのパターン（OR）|

④アンカー（位置を表すメタキャラクタ）

|記法|意味|
|:-----------------|:-----------------|
|^|行の先頭|
|$|行の終端|
|\b|単語境界|
|\B|単語境界以外|

⑤その他メタキャラクタ

|記法|意味|
|:-----------------|:-----------------|
|\1 \2 \3|すでにマッチしたテキスト|
|\〇|エスケープ（メタキャラクタでマッチさせたい場合に利用）|
ZoomをAzure ADでSSOする構成にしてみた（前編）

[Salesforce](https://qiita.com/hisssa0102/items/0d7f55e97faf850d387b)や[Tableau](https://qiita.com/hisssa0102/items/9e06f75a343761828280)に続く第3弾。今回はZoomをOffice365にSSOする設定をしてみた。

今回のゴールは、組織のドメインのユーザーは、強制でSSOさせること。利便性とセキュリティの両立、ってことでやってみた。

前提条件
バニティURLの取得
SSOの構成(Azure AD上の設定）
SSOの構成(Zoom上の設定）
参考資料

#前提条件
前提条件として「SSOするならビジネス版を10アカウント」というのが最低条件らしい。
SSOさせたければ、一定以上のライセンスが必要な、というSaaSは多く、Zoomもその例に漏れない。しかし、どうもビジネス版を10ライセンスあればよいみたい。

くわしくは、ここ。
https://zoom.us/pricing

#バニティURLの取得

バニティURLとは、カスタムのZoom用ドメイン。yourcompany-jp.zoom.usみたいな感じ。これを取らないとSSOできないので、前提条件その2ともいえる。

管理者アカウントで管理画面に入って、管理者 - アカウント管理 - アカウントプロフィールを開く。すると、下のほうにバニティURL欄が出てくるので、申請。

メールのドメインに準じて設定しないといけない。
メールドメインがyourcompnay.jpなら、yourcompany-jp.zoom.usみたいな感じで、"."を"-"で置き換える。yourcompany.zoom.usではリジェクトされました。

申請してたら数日待つ。のだけど数時間で回答きた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237698/a412264f-d9dd-f99a-345c-8978c5805b7d.png)


#SSOの構成(AzureAD上の設定）

3回目ともなれば慣れたもので「公式ドキュメントは正しいとは限らない」「最新だとは思わない」を前提に進められるようになってきた。毎度おなじみ、Azure Portalと、Zoomの管理画面を行ったり来たり。

1.Azure Portalでの作業
Azure Portal - Azure ActiveDirectory -エンタープライズアプリーケーション を開いて、ギャラリーでZoomを検索
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237698/762cb0f3-d775-0e3d-3063-7f01683333c7.png)

画面が切り替わったら、シングルサインオンの設定 - SAML と進めて、入力。

|欄|値||
|:--|:--|:--|
|Identifier (Entity ID)|yourcompany-jp.zoom.us||
|Reply URL (Assertion Consumer Service URL)|https://yourcompany-jp.zoom.us/SAML/SSO/||
|Sign on URL|https://yourcompany-jp.zoom.us||

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237698/af31abb8-baf8-2f9f-6c94-2cd71f8c8dae.png)
※バニティドメインはそれぞれ置き換えてください。

それから、Base64の証明書をダウンロードして、Login URL、Azure AD Identifier、Logout URLをこぴっておく。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237698/0c1a361b-9cba-a114-7b95-bd8421dce7ce.png)

#SSOの構成(Zoom上の設定）

ここまでやったら、次はZoomの管理画面。管理者 - 詳細 - シングルサインオンを開いて、以下入力


|欄|AzureADの値||
|:--|:--|:--|
|サインインページ|AADのLogin URLの値||
|サインアウトページ|AADのLogout URLの値||
|プロバイダの証明書を特定|さっきダウンロードした証明書をメモ帳で開いてそのままコピペ||
|発行者（IDPエンティティID）|AADのAzure AD Identifierの値||

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237698/9a1aef34-593c-87f7-7fb3-ade770d1548e.png)


#テストする

あとはAzure AD - エンタープライズアプリケーション -　Zoom - ユーザーとグループでユーザーを割り当てて、実際にテスト。

バニティURLを開いて、サインインを選んだら、見慣れたOffice365のログイン画面が表示されるはずなので、そのままサインインできればOK。

・・・なのだが。


https://yourcompany-jp.zoom.us
からであればSSOになるのだけど、

https://zoom.us
からなら、素のパスワードでログインできてしまう。これではSSOの利便性は担保できるけど、認証制御の意味ないやん！


うちのドメインのユーザーは、強制でSSOしかできなくしないと意味がないんよ！

というわけで、[後編に続く](https://qiita.com/hisssa0102/items/449c6b87fd81c3421fdb)

#参考資料
チュートリアル:Azure Active Directory シングル サインオン (SSO) と Zoom の統合
https://docs.microsoft.com/ja-jp/azure/active-directory/saas-apps/zoom-tutorial

SSO入門（Zoom)
https://support.zoom.us/hc/ja/articles/201363003-SSO%E5%85%A5%E9%96%80


ほかのSaaSもいくつかまとめてみました。[各種SaaSとAzure Active Directoryのシングルサインオン対応状況を調べてみた](https://qiita.com/hisssa0102/items/77f94823f6c708623694)を参照してください。
【MySQL】テーブル名・カラム変更

##テーブル名の変更

```
ALTER TABLE tbl_name RENAME TO new_tbl_name;
```

##インデックス名の変更

```
ALTER TABLE tbl_name RENAME INDEX old_index_name TO new_index_name;
```

##カラム名の変更

```
ALTER TABLE tbl_name RENAME COLUMN old_col_name TO new_col_name;
```

##カラム定義の変更

```
ALTER TABLE tbl_name MODIFY col_name column_definition;
```

##カラムを追加する

```
ALTER TABLE tbl_name ADD col_name column_definition;
```

##カラムを削除する

```
ALTER TABLE tbl_name DROP col_name;
```
【Ruby on Rails】パーシャル (partial)について

#パーシャル (partial)とは

ビュー画面を共通化するために使用します。
ビュー内のコードをいくつものファイルに分割して書き出し、他のテンプレートでも使いまわすことができます。

##パーシャル (partial)の使用方法

「_header.html.erb」 といった「_（ファイル名）.html.slim」
の先頭に_(アンダースコア)を付けたファイルを作成します。
これは、パーシャルで使う普遍的な命名規則となります。

##パーシャル (partial)の呼び出し方

```
<%= render 'layouts/header' %>
```

上記のように、「render　ファイル名（ディレクトリを指定）」で呼び出すことが可能です。


##おわりに
パーシャルはビューファイル内で使われている部分を一つのファイルにまとめて使いまわすことができる便利な機能となります。
【MySQL】データベースのコピー

###コピー元データベースをファイルに出力

```
mysqldump -u user_name -p from_db > from_db.dump.sql
```

###データベースの作成

```
mysqladmin -u user_name -p create dest_db
```

###コピー先へデータを投入する

```
mysql -u user_name -p dest_db < from_db.dump.sql
```
Qiitaのこんな使い方

いつもお世話になっております。

# Qiita様。

そして、

# 有益な記事を書いてくださってる著者の皆様。

本当に何度も救われています。感謝してもしきれません。

私もへたっぴなりに、自分にも発信できることをみなさんにお届けしようと奮闘中です。

そんな私は日頃、発信力を身に付けるために、偉大なる先輩方の記事を参考にしています。

具体的に何をしているかというと、

### LGTMがたくさん付いている記事をチェックする

これだけです。

そうするとですね、元々は記事をうまく書きたい目的で拝見していたのですが、```プログラミングそのものの理解が深まっている```ことに気付きました。

そうですよね。高評価の記事をずーっと見ていたわけですから。概念理解がしっかり固まってきます。

なので、少し学習が行き詰まってしまったときに、偉人が残してくれたモノをぼんやり眺めるのも良いですよ。

<img width="1414" alt="qiita_1_1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406347/49c71cba-ee82-a725-5c73-eae1d5cb7059.png">

<img width="1415" alt="qiita_1_2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406347/b2af6d33-f76b-a552-d83a-de46ba97e3c6.png">

<img width="1416" alt="qiita_1_3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406347/420ed99c-a9b7-4578-e51d-aa182bce75db.png">



プログラミング学習をしていると、毎日新たな発見がありますよね。

また面白い勉強法などあれば随時更新します。

みなさんの日々の学習のスパイスになれば何よりです😊

最後まで読んでくださりありがとうございました！！
Railsでモーダルの出し分けを管理する

Railsでモーダルの表示処理をまとめて衝突しないように管理する方法を紹介します。

# 背景

[ツクリンク](https://tsukulink.net/)を運営する中でモーダルが少しづつ増え、衝突することがあったためモーダルの優先順位を付け、衝突しないよう実装をしました。

# 実装

## 前提

以下の３つのモーダルがあるとします。

- A: 初回ログインで出すモーダル（全ページ）
- B: 特定のユーザーにだけお知らせを出すモーダル（全ページ）
- C: 特定のページで出すモーダル（Posts#show）

## コードサンプル

全ページに出すモーダルはApplicationControllerで該当ユーザーか判断しモーダルに必要な情報をセットします。

```ruby
# controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :set_modal_A
  before_action :set_modal_B

  def set_modal_A
    return if cookies['modal_A'].present? # 表示済みなら何もしない
    if is_first_signed_in? # 初回ログインか？
      @modal_A = { title: '表示に必要な情報など' }
    end
  end

  def set_modal_B
    return if cookies['modal_B'].present? # 表示済みなら何もしない
    if show_notice? # お知らせを出すユーザーか？
      @modal_B = true
    end
  end
end
```

Viewごとに出るモーダルは `provide` でモーダルをセット

```erb
# views/posts/show.html.erb
<% provide :modal, render('modal/C') %>
```

レイアウトのView（もしくはそれに準ずるパーシャル）でモーダルの出し分けを行います。
ifの上位にあるものが優先され、モーダルが複数renderされるのを防いでいます。

```erb
# views/layouts/application.html.erb
<% if yield(:modal).present? %>
  <%= yield(:modal) %>

<% elsif @modal_B.present? %>
  <%= render 'modal/B' %>
<% elsif @modal_A.present? %>
  <%= render 'modal/A' %>
<% end %>
```

各モーダルのViewでは表示を管理するCookieを保存するなどの処理をしています。

```erb
# views/modal/A.html.erb
<% cookies.permanent['modal_A'] = { value: true, expires: 1.day } %>
<div class="modal">お知らせだよ！</div>
```


※こちらの記事は自ブログからの転載です
https://akinov.hatenablog.com/entry/2020/05/01/222151
【MySQL】DB作成・ユーザ作成・権限設定

##DB作成

```
CREATE DATABASE db_name;
```
##DB削除

```
DROP DATABASE db_name;
```
##全ユーザ確認

```
SELECT user, host FROM user;
```
##ユーザ作成

```
CREATE USER user_name IDENTIFIED BY 'password';
```
##ユーザ削除

```
DROP USER user_name;
```
##権限を表示

```
SHOW GRANTS for 'user_name'@'%';
```
##権限付与

・特定DBの全テーブルに対する権限

```
GRANT ALL PRIVILEGES ON `db_name`.* TO `user_name`@`%`;
```

##権限削除
・特定DBの全テーブルに対する権限

```
REVOKE ALL PRIVILEGES ON `db_name`.* FROM `user_name`@`%`;
```
micro:bitのコンパイル環境をArduino IDEで構築する

#■micro:bit購入のきっかけ
2020年のお正月、実家のある大阪に帰省した際、日本橋の共立電子で「micro:bit」を購入したことがきっかけです。

何年か前から名前だけは聞いたことがあって、『おもちゃ』と思って購入することはなかったのですが、子供が5歳になり『こういうおもちゃに興味をもってくれたらな～』と思うようになり、micro:bitを購入しました。

#■Arduino IDEを選択したわけ
じゃあ、なぜ開発環境はArduinoなの？
micro:bitの開発環境なら、下記のようなものがあるじゃないか？
オフラインで開発できる[Muエディター](https://codewith.mu/)というのもあるのですが、それは少し置いておいて...

|開発環境|URL|
|------------------|-----|
|Microsoft MakeCode|https://makecode.microbit.org/#|
|Pythonエディター|https://python.microbit.org/v/2.0|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223945/b244313a-7278-42dd-6004-0294d8ce68a0.png)
その通りなのですが、Web経由で開発する必要があるのが面倒くさい。
正直、仕事でArduinoを使う機会が多いのと、自宅にArduinoUnoがあるのに未だ開封すらしていないので、この機会にArduinoも弄ろうと思ったのがArduino IDEを選択した一番の動機です。

#■手順
<b>1.Arduino IDEを以下の場所からDLする。</b>
https://www.arduino.cc/

<b>2.ボードマネージャーの追加</b>
Arduino IDEでMicro:bitを使えるようにするため、ボードマネージャにMicro:bitのURLを追加する。
[ファイル]→[開発環境]を選択し、下記URLを設定する。
https://sandeepmistry.github.io/arduino-nRF5/package_nRF5_boards_index.json
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223945/59de54f2-d585-1e56-0575-72e0debec666.png)
※上記URLは、[ここ](https://learn.adafruit.com/use-micro-bit-with-arduino/install-board-and-blink)に記載されています。

<b>3.ボードマネージャーからnFR5をインストール </b>
nRF5をインストールした後、「BBC micro:bit」を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223945/ba2ba546-dd59-ce81-44bb-899885ee6a61.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223945/4e7f7e58-19cd-6dcf-4617-c84ba1e445e3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223945/a7248de9-4318-83fd-4847-121cae4ec3d4.png)
S110を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223945/a020267b-3cf2-71cb-6332-701b20384481.png)

<b>4.サンプルプログラムの書き込み</b>

```cpp
const int buttonA = 5; // the number of the pushbutton pin
const int buttonB = 11; // the number of the pushbutton pin

void setup() {
  Serial.begin(9600);
  Serial.println("microbit is ready!");
  
  pinMode(buttonA, INPUT);
  pinMode(buttonB, INPUT);
}

void loop(){
  if (! digitalRead(buttonA)) {
    Serial.println("Button A pressed");
  }
  if (! digitalRead(buttonB)) {
    Serial.println("Button B pressed");
  }
  delay(500);
}
```

AボタンまたはBボタンを押下したら、シリアルに押された事をプリントアウトするプログラム。
[ツール]→[シリアルモニタ]で、シリアル出力を確認するウィンドウを表示し、以下の通り表示されていればOK。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223945/732d3a23-85b2-e177-e3ac-cff8b5f4dc6b.png)
【MySQL】別スキーマ(データベース)のテーブルを利用する

##目的
Oracleの場合、別スキーマのテーブルを利用する目的で、
パブリックシノニムを利用できる。
MySQLは、シノニムが存在しないため、別機能を利用する必要がある。

##対策
ビューで代替する。

##方法

1. 対象表を全件検索するSQLに対してビュー名user_pubを定義する。

	```
	CREATE VIEW user_pub AS SELECT * FROM user;
	```

1. ビューuser_pubに対するSELECT権限をユーザーに付与する。

	```
	GRANT SELECT ON user_pub TO ユーザー名;
	```

##補足
 - MySQLでは、スキーマはデータベースと同義である。
railsで.xlsxを生成する

## 概要
xlsxを出力する必要があったため、 [caxlsx_rails](https://github.com/caxlsx/caxlsx_rails) 使用して.xlsxファイルを生成していく
> **axlsx_rails renamed to caxlsx_rails**

もともと`axlsx_rails`という名称のgemだったっぽい

## 準備
```ruby:Gemfile
gem 'caxlsx'
gem 'caxlsx_rails'
```

## コントローラーに追加
```ruby:hoges_controller.rb
...
  def show
    @hoges = Hoge.all
    respond_to do |format|
      format.html
      format.xlsx
    end
  end
...
```


## viewに拡張子が.xlsx.axlsxのテンプレートを作成
下記のようなファイルを作成するだけでxlsm形式のファイルを生成する事が可能。 

```ruby
wb = xlsx_package.workbook
wb.add_worksheet(name: "hoge") do |sheet|
  @hoges.each do |hoge|
    sheet.add_row ["hoge", hoge.hoge]
  end
end
```


### ファイル名を変更したい場合
controllerの`format.xlsx`内を変えるだけでいけるっぽい

```ruby
...
format.xlsx {
  response.headers['Content-Disposition'] = 'attachment; filename="hoge.xlsx"'
}
...
```

### 複数シートを作りたい
こんな感じで出来るっぽい

```ruby
@hoges.each.with_index(1) do |hoge, i|
  wb.add_worksheet(name: "#{i}") do |sheet|
    sheet.add_row ["hoge", hoge.hoge]
  end
end
```
[^1]:[Ruby Axlsx: How to get values from another worksheet - Stack Overflow](https://stackoverflow.com/questions/44287291/ruby-axlsx-how-to-get-values-from-another-worksheet)

`add_worksheet`の引数に渡しているnameはそれぞれ別の名前を指定しないと下記のエラーが出るのでファイル毎にそれぞれ違う名前を設定する必要がありそう
`There is already a worksheet in this workbook named 'hoge’. Please use a unique name`

### セル内改行したい
下記設定を追加すればいけそう

```ruby
...
wb.use_shared_strings = true
...
wrap = wb.styles.add_style alignment: { wrap_text: true }
...
sheet.add_row ["hoge", hoge.hoge], style: wrap
...
```

実際のコードにするとこんな感じ

```ruby
wb = xlsx_package.workbook
wb.use_shared_strings = true
@hoges.each.with_index(1) do |hoge, i|
	wrap = wb.styles.add_style alignment: { wrap_text: true }
  wb.add_worksheet(name: "#{i}") do |sheet|
    sheet.add_row ["hoge", hoge.hoge], style: wrap
  end
end
```
[^2]: https://github.com/randym/axlsx/issues/252#issuecomment-225868798
TerraformでGCPのService Account Keyを作成し、ローカルにファイルとして保存する

# 動作確認環境

- terraform v0.12.24
- terraform-provider-google v3.19.0


# TL;DR

下のようなコードで実現できます。

```HCL:main.tf
resource "google_service_account" "my-account" {
  account_id = "my-account"
}

resource "google_service_account_key" "my-account-key" {
  service_account_id = google_service_account.my-account.name
}

resource "local_file" "my-account-key" {
  filename             = "./output/secrets/my-account-key.json"
  content              = base64decode(google_service_account_key.my-account-key.private_key)
  file_permission      = "0600"
  directory_permission = "0755"
}
```

# local_fileとは

その名の通りで、Terraformでローカルにファイルを作成できるリソースです。

https://www.terraform.io/docs/providers/local/r/file.html

# 参考

- [google cloud platform - Dumping Terraform output to a local file - Stack Overflow](https://stackoverflow.com/questions/59736621/dumping-terraform-output-to-a-local-file)
Reactメモ

Reactの個人的なメモです。

## Reactとは？
・Facebookが開発したJSライブラリ（≠フレームワーク）

・WebのUI（ユーザーインターフェイス）
　１.見た目（View）
　２.機能（Controller）

・仮想DOMを操作できる
 
通常DOM

```
 document.getElementById('hoge').innerText = 'fuga' 
```
仮想DOM

```
 render(
<div id = 'hoge'>fuga</div>
)
```
## JSX
React内で使うHTMLの様なJSの拡張言語(仮想DOMで使う言語)

通常DOM

```
const fuga = "<h1>Hello,World!</h1>"
const foo = "<h2>React Commentary</h2>"
const bar = "Hi, I'm Taro"
document.getElementById('hoge').innerHTML = fuga
document.getElementById('foo').innerHTML = foo
document.getElementById('bar').innerText = bar
```
JSX

```
return(
  <React.fragment>
   <div id = "hoge">
    <h1>Hello,World!</h1>
   </div>
   <div id = "bar">
    <h2>React Commentary</h2>
   </div>
   <p id = "foo">Hi,I'm Taro</p>
  </React.fragment>
);

```

## JSXの基礎文法
１
Reactパッケージのインストール
.JSXファイルの先頭で宣言

```
import React from "react"
```

２
変数名はキャメルケースで書く
classNameなど文字を繋ぐときは大文字で

３
classはclassNameに変える

```
const App = () =>{
  return(
    <div id = "hoge" className = "fuga">
      <h1>Hello, World!</h1>
    </div>
  );
};
```

４
{}内に変数や関数を埋め込める

```
const foo = "<h1>Hello, World!</h1>"
const App = () =>{
  return(
    <div id = "hoge" className = "fuga">
      {foo} 
    </div>
  );
};
```


5
空要素は必ず閉じる

```
const App = () =>{
  return(
    <div id = "hoge" className = "fuga">
      <input type="text" id = "blankElement"/> #閉じる
      <img src="/assets/icon/foo.png"/> #閉じる
    </div> 
  );
};
```
#MaixHub Online CompileでMaixPyをカスタマイズする

# 概要
SiPEEDのMaixPy用のクラウドサービスMaixHubの中で、
MaixPyファームウェアをカスタマイズしてダウンロードできる、
MaixHub Online Compileが公開されました。

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">MaixHub now support compile your own MaixPy firmware, with your customized spiffs content~<br>Check it out:<a href="https://t.co/45spDu8k4F">https://t.co/45spDu8k4F</a></p>&mdash; Sipeed (@SipeedIO) <a href="https://twitter.com/SipeedIO/status/1255798974662897664?ref_src=twsrc%5Etfw">April 30, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

Maixボードのメモリ容量はシビアで、大きい学習モデルを読み込みたいときには、
MaixPyファームウェアをカスタマイズする必要がありました。
MaixPyファームウェアのカスタマイズには、これまでは、ローカルのLinux(Ubuntu)でのビルド環境を準備する必要がありました。

### Ubuntuで#MaixPy をカスタマイズする

https://qiita.com/nnn112358/items/fb6ebc8a7868d249f05d

MaixHub Online Compileは、
MaixPyファームウェアのカスタマイズをWEB上で行い、
クラウド上でビルドしてダウンロードすることができる画期的なサービスです。

# MaixHub
MaixHubは、MaixボードをリリースするSiPEEDが運営するクラウドサービスです。
Maixボードのための機械学習の学習モデルの生成を行う「Model Training」や、
学習モデルの共有を行う「Share Model」などのページが用意されています。

https://www.maixhub.com/

## ユーザ登録

![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/57ed1030-7004-0a70-25fa-3de0f294e188.png)

MaixHubのユーザ登録をします。

![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/855f08c0-5f43-76bf-3896-dfe4f80fbcac.png)
メールアドレスとユーザ名を登録します

![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/5a39c23a-5113-baa0-47e0-d514d9c5fef3.png)
ユーザ登録がすんだら、MaixHubにログインします

![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/feb87b5b-3fb5-d7ff-2eb2-5fffebe0d574.png)
MaixHub Online Compileに入ります
最初に、メールアドレスに認証コードを送り、認証をします。

## MaixPy Firmware online generation 

![005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/62f472b2-76b6-7325-0c4b-384bea52c2d8.png)
LCDのサイズ、Maixボードか、M5StickVか、等を選択します


![006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/ed87da7b-d7c5-8905-e2d8-71bacceb0aa9.png)

モジュールの機能を選択します。

|	Module	|	機能		|
|	--------------	|	--------------	|
|IDE|MaixPy IDEとの接続プロトコルモジュール|
|_thread |マルチスレッドモジュール|	
|NES |ファミ〇ンエミュレータモジュール|	
|video |動画モジュール|	
|Minimum OMV |OpenMVの機能を削減する|	
|YUV convert table	|YUVの変換テーブル追加する|	
|dual sensor buff	|カメラバッファを追加する|	
|MIC_ARRAY |MICARRAYモジュール|	
|lodepng module	|PNGモジュール|
|ulab module	|行列演算モジュール|
|kmodel V4	|kmodel V4対応|
|touch screen module	|タッチパネルモジュール|	
|Ws2812	|Ws2812モジュール|
|HTPA thermal driver|thermalモジュール|
|htpa|module	|thermalモジュール|

![007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/9b6bd95f-ce52-31c1-6ad7-6e524faa7a02.png)
SPIFFSのサイズと格納するファイルを選択します。

## MaixPy Firmware のダウンロードリンクを送信

![008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/0dc4820e-5e9f-e9b3-fcae-be2ca5e7b853.png)


SUBMITを押すと、MaixPyのファームウェアをクラウド上でビルドし、
自分のメールアドレスへ、ダウンロードのリンクを送信します。


![_009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73020/f50656d7-195d-d175-9fd0-b5cb1e73c706.png)

KFlashでMaixボードにファームウェアを書き込んで終了です。
【SpringBoot】スケジューリング

## `@EnableScheduling`
Spring Bootにてスケジューリング機能を有効にするには、EnableSchedulingアノテーションを利用する。

```java
@SpringBootApplication
@EnableScheduling
public class SampleApplication extends SpringBootServletInitializer {

	public static void main(String[] args) throws Exception {
		SpringApplication.run(SampleApplication.class, args);
	}
}

```

## `@Scheduled`
定期的に実行したいメソッドにScheduledアノテーションをつけ、実行するタイミングを定義する。

```java
	@Scheduled(cron = "0 0,15,30,45 * * * *", zone = "Asia/Tokyo")
	public void task() {
		//処理
	}
```
上記の例では、cron式で実行タイミングを定義している。

## 参考
・cronの起動時間設定（Spring Bootでは秒まで指定する。）

```
* * * * * * （起動したい処理）
[秒] [分] [時] [日] [月] [曜日] 
```
terraformで、terraformを介さずにインスタンスを削除してしまった場合の対処法

# 概要

Terraformで、Terraform用の権限には削除権限を付けないでやりたい時があります。
つまり、Terraformで削除することができないので、手動で削除することになります。

この時に、Terraformの状態を保持するtfstateで状態を保持し続けてしまい、
手動で削除したことを検知できず、terraform applyができないことがあります。

こうした時、terraform importの逆をしたいと思いますが、
それがなんなのかわからないということがありました。
今回それの方法を知ったので共有したいです。

# terraform state rm

これを使用することで、terraform importの逆、tfstateからDropすることができます。

使い方は

```
terraform state rm [resource type].[resource name]
```
と書きます。これで削除できます。

これで、引っかかってしまうエラーも回避することができます！
Spockで「where-blocks may only contain parameterizations」のエラー

## Spockで発生したエラー

Spockでテストを実行します。

```groovy
import spock.lang.*

class DataDrivenSpec extends Specification {

    def "a + b == sum"() {
        expect:
        a + b == sum

        where:
        a | b || sum
        1 | 2 || 3
        3 | 1 || 4
    }
}
```

ちなみに、`build.gradle`の設定は次のようにしました。

<details><summary>build.gradle</summary><div>

```groovy:build.gradle
plugins {
    id 'groovy'
}

group 'old-spock-sample'
version '1.0-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    // https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-all
    compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '1.7.5'
    // https://mvnrepository.com/artifact/org.spockframework/spock-core
    testCompile group: 'org.spockframework', name: 'spock-core', version: '0.5-groovy-1.7'
}
```
</div></details>

すると、次のようなエラーが発生しました。

```
where-blocks may only contain parameterizations (e.g. 'salary << [1000, 5000, 9000]; salaryk = salary / 1000')
```

このエラーが発生する理由は複数あるようです。珍しいパターンだと思いますが、自分の場合はSpockのバージョンが古かったためでした。

## 理由

Spockの[リリースノート](http://spockframework.org/spock/docs/1.2/release_notes.html#_0_6_released_2012_05_02)に次のように記載されています。

>## 0.6 (released 2012-05-02)
>（中略）
>### Improved Data Table Syntax
>
>Table cells can now be separated with double pipes. This can be used to visually set apart expected outputs from provided inputs:

>```
>...
>where:
>a | b || sum
>1 | 2 || 3
>3 | 1 || 4
>```

Spockではパラメータ化テストの期待値を`||`の後ろに書くのはSpockの0.6で追加された機能なのでバージョンによっては使えないようです。（現在（2020年5月）の最新バージョンは1.3です）  
解決としては単純に`||`を`|`にすれば実行できます。  
もしくは、Spockのバージョンを上げても当然解決するはずです。  
当たり前のように使われている機能なのでバージョンによって使えないことがあるという発想がなかったのと個人的に原因がエラーメッセージからすぐにはわからず苦戦しました。  
とても古いバージョンのSpockを使う際はご注意ください。
【CentOS】Jenkinsのインストール

OpenJDKのインストール

```
yum -y install java-1.8.0-openjdk
```

wgetモジュールのインストール

```
yum -y install wget
```

yumリポジトリに追加（LTSバージョン）

```
wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat-stable/jenkins.repo
```

RPMパッケージの公開鍵をインポート

```
rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
```

Jenkinsインストール

```
yum -y install jenkins
```

Jenkinsの起動

```
systemctl start jenkins
```

Jenkinsの自動起動設定

```
chkconfig jenkins on
```

ファイアウォール設定

```
firewall-cmd --permanent --new-service=jenkins
firewall-cmd --permanent --service=jenkins --set-short="Jenkins Service Ports"
firewall-cmd --permanent --service=jenkins --set-description="Jenkins service firewalld port exceptions"
firewall-cmd --permanent --service=jenkins --add-port=8080/tcp
firewall-cmd --permanent --add-service=jenkins
firewall-cmd --zone=public --add-service=http --permanent
firewall-cmd --reload
```

ファイアウォール確認

```
firewall-cmd --list-all
```

初期パスワードの確認

```
cat /var/lib/jenkins/secrets/initialAdminPassword
```

■ホーム画面
初期パスワードを入力、管理者ユーザを設定する。
その後、以下画面が表示される。
![Jenkinsダッシュボード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/488082/26d84150-b3c6-ca29-da06-a183ad5b1fe5.png)
filter vs 内包表記

# `filter`と内包表記どっちが速いの？

内包表記で条件つけた場合と `filter` に `lambda` で条件渡した場合、どっちが速いの？

```python
iter = 10000

# 内包表記
for _ in tqdm(range(iter)):
    [i for i in range(iter) if i % 2 == 0]

# filterのみ
for _ in tqdm(range(iter)):
    filter(lambda i: i % 2 == 0, range(iter))

# filterからのlistにキャスト
for _ in tqdm(range(iter)):
    list(filter(lambda i: i % 2 == 0, range(iter)))
```
それぞれ比べてみました。

|条件| `iter/s` |
| ---- | ---- |
|内包表記|1531.51|
|`filter`のみ|1169176.56|
|`filter`からの`list`にキャスト|880.30|

~~`filter`が圧勝でした。~~
~~しかし、その後の処理で `list` にしなければならない場合は内包表記に軍配が上がるようです。~~

と、上記のように比較して `filter` が圧勝するのも当たり前
https://qiita.com/licht-e-jima/items/0ef1e630c9afb771c040#comment-0882f662a1eb61d3e602
この通りですね。ご指摘ありがとうございます。
再起動ループテスト

起動時にFANの警告が出る事があるのでFAN自体の問題なのか
BMCセンサーの問題なのか切り分けたい。
起動時にBMCがうまく情報を取れていない可能性を疑い、ひたすら再起動してセンサーデータを取ってみよう。
/etc/rc.localに書いてもいいけど、他の用途でも使いまわせそうなのでスクリプト化しておこう。

１、Crontabの設定
２、スクリプト実行
３、確認

#Crontabに再起動スクリプトを登録

下記で3分おきに/home/test.shを実行するように設定

```
#crontab -l 
(何も設定していないことを確認）
#crontab -e
*/3 * * * * /home/test.sh
```
止めるときは行ごと削除
#再起動スクリプトの中身

```
#/bin/sh
sleep 60　 #止めるときに編集する時間的余裕が必要なので入れてあります。
echo `date` >>/home/log.txt　 #実行時間を追記
sh ipmitool -H 192.168.1.x -U ユーザー名 -P パスワード sdr list >> /home/log.txt　#センサー情報を追記
/sbin/reboot　# フルパスで/sbinを付けないとrebootコマンドを受け付けない。
```




#結果の確認

70回くらい再起動をしてみました。
日付だけに番号を振るのはnlコマンドの後に-b オプションを付けてp文字列。
手癖的にpの後にスペース入れたくなるけどpの後にスペースを入れると通らない謎のSyntax。

```
cat /home/log.txt | grep -e Apr -e CPU -e FAN | nl -b pApr
 
    66 Thu Apr 30 23:50:01 JST 2020
       CPU Temp | 40 degrees C | ok
       FAN1 | 3400 RPM | ok
       FAN2 | 3800 RPM | ok
       FAN3 | 3500 RPM | ok
       FAN4 | 3800 RPM | ok
       FANA | 3400 RPM | ok
       FANB | 3700 RPM | ok
    67 Thu Apr 30 23:53:01 JST 2020
       CPU Temp | 40 degrees C | ok
       FAN1 | 3400 RPM | ok
       FAN2 | 3800 RPM | ok
       FAN3 | 3500 RPM | ok
       FAN4 | 3800 RPM | ok
       FANA | 3400 RPM | ok
       FANB | 3700 RPM | ok
    68 Thu Apr 30 23:56:02 JST 2020
       CPU Temp | 40 degrees C | ok
       FAN1 | 3400 RPM | ok
       FAN2 | 3800 RPM | ok
       FAN3 | 3500 RPM | ok
       FAN4 | 3800 RPM | ok
       FANA | 3400 RPM | ok
       FANB | 3700 RPM | ok
    69 Thu Apr 30 23:59:01 JST 2020
       CPU Temp | 40 degrees C | ok
       FAN1 | 3400 RPM | ok
       FAN2 | 3800 RPM | ok
       FAN3 | 3500 RPM | ok
       FAN4 | 3800 RPM | ok
       FANA | 3400 RPM | ok
       FANB | 3700 RPM | ok
```

めっちゃ安定してる。
という事で本件は経過観察。（なかった事。）
【JavaScript】forEach,mapメソッドの違い、使いどころ

#【JavaScript】forEach,mapメソッドの違い、使いどころ
forEach(),map()の違い、使いどころを整理しました。

##forEach
引数それぞれ(=each)に同じ処理を実行する。
使いどころ：for文よりも短く書きたい場合。

```js

    const a=[0,1,2,3];

    a.forEach((tempA)=>{　　　　 //aの要素を受けとるtempAという入れ物を作る
        return tempA += 1;　　　 //tempAに対して1ずつ加算
        })

    console.log(a);             //[1,2,3,4]
```

##map
配列に同じ処理を関連付けして（=map）、新しい配列を作る。
使いどころ：元の配列を残したまま、新しい配列を作るとき（比較、行列的な処理？）

```js
    const a = [0,1,2,3];
    const b = a.map((tempA)=>{　//配列aを基に配列bを作成
        return tempA + 1;       //tempAに対して1ずつ加算
    })

    console.log(b);　　　　　　　//[1,2,3,4]
```

##短く書くために
forEach()もmap()もfor文に比べると短く書けるのが利点。
アロー関数の場合、次の条件を満たせば更に短く書くことができる。

・引数が1つの場合()を省略可能
・return行が1行の場合{return}を省略可能

私はグルーピングする要素()とか{}が1つの場合＝省略できると覚えました。

forEach文を省略しない場合

```js

    a.forEach((tempA)=>{
        return tempA += 1;
        })
```

forEach文を省略した場合

```js
    a.forEach(tempA=> tempA += 1)
```

map文を省略しない場合

```js
    const b = a.map((tempA)=>{
        return tempA + 1;
    })
```
map文を省略した場合

```js

    const b = a.map(tempA=> tempA + 1);
```
Kinx アルゴリズム - マージソート

# アルゴリズム - マージソート

## はじめに

**「見た目は JavaScript、頭脳(中身)は Ruby、（安定感は AC/DC）」** でお届けしているスクリプト言語 [Kinx](https://github.com/Kray-G/kinx)。「プログラム＝アルゴリズム＋データ構造」。アルゴリズムの実装例をご紹介。

元ネタは「C言語による（30年経っても）最新アルゴリズム事典」。今回はソート（マージソート）です。

* 参考
    * 最初の動機 ... [スクリプト言語 KINX（ご紹介）](https://qiita.com/Kray-G/items/ca08b6fb40d15dd0ec76)
        * 個別記事へのリンクは全てここに集約してあります。
    * リポジトリ ... [https://github.com/Kray-G/kinx](https://github.com/Kray-G/kinx)
        * Pull Request 等お待ちしております。  

アルゴリズムと言えばソート。今回はマージソートです。

## マージソート

* https://ja.wikipedia.org/wiki/マージソート
    * クイックソートより最悪計算量は少ない
    * 平均 $O(n \log n)$
    * 安定

### テスト・ドライバ

テスト・ドライバコードは [前回](https://qiita.com/Kray-G/items/cc6b523ec8471041be92) と一緒です。

```javascript
function display(name, a) {
    System.println(name, a.map(&(e) => "%2d" % e).join(' '));
}

function demo(N, func) {
    var a = N.times(&() => Integer.parseInt(Math.random() * 100));
    display("Before: ", a);
    a = func(a);
    display("After:  ", a);
}
```

### サンプル・コード

```javascript
function mergesort(a, first, last) {
    var middle, work = [];
    var i, j, k, p;

    if (first < last) {
        middle = Integer.parseInt((first + last) / 2);
        mergesort(a, first, middle);
        mergesort(a, middle + 1, last);
        p = 0;
        for (i = first; i <= middle; i++) {
            work[p++] = a[i];
        }
        i = middle + 1;
        j = 0;
        k = first;
        while (i <= last && j < p) {
            if (work[j] <= a[i]) {
                a[k++] = work[j++];
            } else {
                a[k++] = a[i++];
            }
        }
        while (j < p) {
            a[k++] = work[j++];
        }
    }
}

demo(20, &(a) => {
    mergesort(a, 0, a.length() - 1);
    return a;
});
```

### 結果

```
Before:  4 84 51 71 19 44  6 90  4 22 13  0 41 15 36 73 79 50 97 64
After:   0  4  4  6 13 15 19 22 36 41 44 50 51 64 71 73 79 84 90 97
```

## おわりに

マージソートは安定です。安定で平均して速いので魅力的ですが、ランダムなデータに対しては（不安定で良ければ） [クイックソート](https://qiita.com/Kray-G/items/d2305cd5e97e803af25c) のほうが速い。また外部にワーク領域が必要なので外部ソートです。

ではまた、次回。
【PowerShell】ダウンロードファイルの破損、改ざんがないか検証する

①ダウンロードサイトから対象ファイルのチェックサムを取得する。
※CentOSの場合は、以下のページ。
対象ファイルのchecksumsのSHA256を取得する。
https://wiki.centos.org/Download

②ダウンロードしたファイルから作成したチェックサムを作成する。
7-Zipをインストールしている場合、
対象ファイルで右クリック「CRC SHA」>「SHA-256」で作成できる。

また、powershellコマンドでも取得できる。
・コマンド

```powershell
Get-FileHash -Algorithm SHA256 C:\CentOS-7-x86_64-DVD-1810.iso
```
・実行結果

```
Algorithm       Hash                                                                   Path
---------       ----                                                                   ----
SHA256          6D44331CC4F6C506C7BBE9FEB8468FAD6C51A88CA1393CA6B8B486EA04BEC3C1       C:\CentOS-7-x86_64-DVD-1810.iso
```

③文字列を比較し、一致しているか（大文字・小文字の区別はしない）、確認する。
・比較検証を実施。例として、以下のpowershellコマンドを実行。

```powershell
# ダウンロードサイトから取得したチェックサム（SHA256）
$a = "6d44331cc4f6c506c7bbe9feb8468fad6c51a88ca1393ca6b8b486ea04bec3c1"
# ローカルファイルから作成したチェックサム（SHA256）
$b = "6D44331CC4F6C506C7BBE9FEB8468FAD6C51A88CA1393CA6B8B486EA04BEC3C1"

# 比較処理(大文字・小文字を区別しない)
if($a -ieq $b){
  Write-Output '正常'
}else{
  Write-Output '異常'
}
```

・実行結果

```
正常
```
【初心者向け】10行以内のスクリプト(3.datareaderでデータ取得・csv化)

### 【初心者向け】10行以内のスクリプト(3.datareaderでデータ取得・csvへ書き込み)
pythonで色々なライブラリを使えば、「少ないコードでもちょっとした事が出来て、少なければ5行程でもちょっとしたスクリプトが作れ、便利だな。」と思ったので、pythonやその他、コマンドを羅列しただけのものも出てくるかも知れませんが、10行程のスクリプトを不定期で投稿してみようと思います。

**第3回**として、<font color=blue>datareaderでデータを取得、csvへの書き込みを投稿したいと思います。</font>

社内でcsvやexcelのデータを渡されたり、ネット上で公開されているでもjson等に並び、csvが公開されている事例が多いと思います。

取得するデータは、fredの日経225の2016年1月1日からとしました。
このデータを、pandasのデータフレームに格納、csvに書き込みます。

最後、補足として、plot関数でプロット。

【環境】
Linux: debian10
python: 3.7.3
pandas: 1.0.3
pandas-datareader: 0.8.1

***
### 1.データの取得・データフレームへの格納
日経225データ取得のdatareaderの取得は、以下の様な構文です。
pdr.DataReader('NIKKEI225' ,'fred' ,start) 

csvへの書き込みは、pandasで書き込みます。
構文は、データフレーム.to_csv('書き込むファイル名.csv')

コードはjupyterで実行しました。

```python:datareader

#datareaderでfredからNikkei225のデータを取得して、csvに書き込み
#outfile = ('./nikkei225_20200428.csv')
import pandas as pd
from pandas_datareader import data, wb
import datetime
import matplotlib.pyplot as plt

#データ取得・データフレームに格納
start = datetime.datetime(2016, 1 ,1)
df_nikkei225 = pdr.DataReader('NIKKEI225' ,'fred' ,start)

# csvに書き込み
df_nikkei225.to_csv('./nikkei225_20200428.csv')
```

上記、スクリプトのデータフレームへの格納で、1行目で取得する開始日を指定、2行目でfredから日経225を1行目で指定した日から取得、データフレームにする記述をしました。

start = datetime.datetime(2016, 1 ,1)
df_nikkei225 = pdr.DataReader('NIKKEI225' ,'fred' ,start) 

データフレームの名前は、「df_nikkei225」としましたが、実際は何でも良いです。

csvへの出力はカレントディレクトリに、「nikkei225_20200428.csv」として、出力しました。

### 2.取得したデータの内容
取得したデータを見てみようと思います。
「df_nikkei225」というのは、先程のコードで使ったデータフレームです。

```python:データフレーム

df_nikkei225
# 格納されたデータフレームの内容
 	NIKKEI225
DATE 	
2016-01-01 	NaN
2016-01-04 	18450.98
2016-01-05 	18374.00
2016-01-06 	18191.32
2016-01-07 	17767.34
... 	...
2020-04-22 	19137.95
2020-04-23 	19429.44
2020-04-24 	19262.00
2020-04-27 	19783.22
2020-04-28 	19771.19

1128 rows × 1 columns
```

【補足】として、グラフを描画してjpgで出力してみようと思います。
以上のデータをプロットする最も簡単な方法は、2行のコードで出来ます。
最初に「import matplotlib.pyplot as plt」で、matplotlibをインポートしました。

その状態で「データフレーム名.plot()」、<font color=blue>データフレームが「df」の場合は、df.plot()でプロット出来ます。</font>
例として、グラフをjpgで出力する場合、<font color=blue>plt.savefig("出力するファイル名.jpg")</font>

```python:plot

# グラフ描画・出力
df_nikkei225.plot()
plt.savefig("nikkei225_20200428.jpg")
```
![nikkei225_20200428.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624077/c011e277-36e7-088a-b620-d9f9967a890f.jpeg)

```python:データの情報

df_nikkei225.info()

<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 1128 entries, 2016-01-01 to 2020-04-28
Data columns (total 1 columns):
 #   Column     Non-Null Count  Dtype  
---  ------     --------------  -----  
 0   NIKKEI225  1056 non-null   float64
dtypes: float64(1)
memory usage: 17.6 KB

df_nikkei225.isnull().sum()

NIKKEI225    72
dtype: int64

df_nikkei225.head()

DATE 	
2016-01-01 	NaN
2016-01-04 	18450.98
2016-01-05 	18374.00
2016-01-06 	18191.32
2016-01-07 	17767.34

```
取引が無い日はデータはありませんが、取得したデータをそのままプロットしましたので、実際のグラフとしては、ひとまず欠損値の処理をする事になると思います。
それについては、別記事で触れたいと思います。

**以上、datareaderでデータの取得・csv化でした。**
【PowerShell】特定ファイル内の特定行をコメントアウトする

##コマンド

```powershell
$path = "C:\Windows\System32\drivers\etc\hosts"
$old = "(.*hoge`.test2.*)"
$new = "#`$1"
$file_contents = $(Get-Content $path) -replace $old , $new
$file_contents > $path
```

##実行前

```
192.168.10.10 hoge.test1
192.168.10.10 hoge.test2
192.168.10.10 hoge.test3
```

##実行後

```
192.168.10.10 hoge.test1
#192.168.10.10 hoge.test2
192.168.10.10 hoge.test3
```

##補足
- powershellでのエスケープは、`（バッククォート）。
Gitリモートブランチでローカルを上書きする

## 目的
忘れがちなgitコマンドについて、ググってすぐに出てきてほしいですね。本記事が検索可能となるか、試してみます。

## リモートブランチでローカルを上書きする
```
1. リモートから最新の全ブランチを取得
git fetch --all

2. masterブランチで上書きする場合
git reset --hard origin/master

3. その他のブランチで上書きする場合
git reset --hard origin/<branch_name>
```

### 注意点
上記の方法は、**更新中のローカルファイルや履歴も上書きされてしまいます。** 
次の点に注意して実行前に準備しておきましょう。

 - `.env`等の環境依存の設定ファイルは、`.gitignore`に登録して管理対象外としておく
 - その他、ローカルで保管しておきたいファイルがあれば`git stash`して、退避しておく

### その他の方法
ローカルの変更・履歴をブランチとして残しつつ、リモートの`master`を元に強制的に変更するには、ローカルの`master`を新しいブランチに対象に上書きすることで、解決できます。

```
git checkout master
git branch new-branch
git fetch --all
git reset --hard origin/master
```

`new-branch`で、ローカル変更の保管先のブランチを指定することになります。


### 参考資料
https://stackoverflow.com/questions/1125968/how-do-i-force-git-pull-to-overwrite-local-files
Autotools３種の神器をインストールしよう

# Autotoolsとは
`Autotools`とは多様なUNIX互換環境への移植性の高いビルドを行うソースコードビルドシステム！

## Autotoolsの３種の神器とは
`Autotools`は主に`Autoconf`、 `Automake`、`Libtool`の３つで構成されている。

# インストール

```bash
$ brew install autoconf automake libtool
```

----
よかったよかった
PHP 学習リファレンス - Cookie

コード
---
ref: https://blog.csdn.net/u011781769/article/details/48470697

```php
<?php
    if ($_COOKIE ['uname'] != '') {
        $CKUNAME = $_COOKIE ['uname'];
    }
    if ($_COOKIE ['pwd'] != '') {
        $CKPWD = $_COOKIE ['pwd'];
    }
    echo "\$CKUNAME:" . $CKUNAME;
    echo '<br>';
    echo "\$CKPWD:" . $CKPWD;
?>

<!DOCTYPE html>

<head>
    <title>Cookie</title>
    <h1> Cookie</h1>
</head>
<hr />
<body>

<form action = "<?php echo htmlspecialchars($_SERVER["PHP_SELF"])?>" method = "POST">
Name:   <input type="text" name="uname" id="uname" value="<?php echo $CKUNAME;?>"><br>
PW:    <input type="password" name="pwd" id="pwd" value="<?php echo $CKPWD;?>"><br>
    <input name="remember" type="checkbox" value="1" checked = <?php if($CKUNAME!=''){echo "checked";}?>> 记住我! 
    Debug : <?php echo "\$CKUNAME:" .  $CKUNAME?>
    <br>
    <input type="submit" name="button" id="button" value="登录" /> <br>
</form>
<hr />

<?php
    // 登录，将用户名和密码存入到COOKIE
    if ($_POST ['button'] != '') 
    {
        $uname = $_POST ['uname'];
        $pwd = $_POST ['pwd'];
         
        // 如果输入的加密密码和COOKIE中不一样，那么就加密
        if ($pwd != $CKPWD) {
            $pwd = md5 ( $pwd );
        }
        $remember = $_POST ['remember'];
        if ($remember == 1) {
            setcookie ( "uname", $uname, time () + 3600 * 24 * 30 );
            setcookie ( "pwd", $pwd, time () + 3600 * 24 * 30 );
        }
    }
?>
</body>

</html>
```