OmniAICreator/Qiita-1.07M huggingface dataset:
【ruby on rails 】Google Natural Language APIを追加導入し感情スコアを表示。

文章を投稿できる既存アプリにGoogle Natural Language APIを追加導入し
感情スコアを画面上に表示させます。

#1.前提条件、前準備


APIを使用するための前準備(概要だけ記載)
 ①google側準備

  ・Google Cloud Platformに登録
  ・新しいプロジェクトを作成
  ・APIキーを生成
  ・Vision APIの有効化

  ②rails側準備

  ・gemfile に dotenv-rails 追加
  ・envファイルに生成したAPIキーを記載
  ・Google Cloud Vision APIを呼ぶためのライブラリを作成
　　※「lib/vision.rb」作成
  ・Google Natural Language APIを呼ぶためのライブラリを作成
　　※「lib/language.rb」作成
  ・自作したライブラリを読み込む
    ※「config/application.rb」に追記

#2.変更前状態

文章を新規投稿・更新できる、postモデルが存在。
以下はposts_controllerのファイル

```app/controllers/posts_controller.rb
class PostsController < ApplicationController
....

  def show
      @post = Post.find(params[:id])
  end

  def create
      @newpost = Post.new(post_params)
      @newpost.save
      redirect_to posts_path
  end

  def update
      @post = Post.find(params[:id])
      @post.update(post_params)
      redirect_to  posts_path
  end
....
end
```


投稿内容を表示させるviewファイル
次からは投稿内容を表すカラム「postbody」に対して感情スコアを出させるようにします

```app/views/posts/show.html.erb
 <div>
    <%= simple_format(@post.postbody) %>
 </div>
```

#3.Google Natural Language API導入

感情スコア用のカラム(score)をpostsモデルに追加します。

```
$ rails g migration AddScoreToPosts 'score:decimal{5,3}'
$ rails db:migrate
```

以下では、create,updateアクション内に以下の記述をすることで、
新規投稿時、投稿更新時に投稿内容本文(postbody)を 
「Language.get_data(post_params[:postbody])」 でAPI側に渡しています。
その後、API側から返ってきた値をもとに、スコアを作成しています。

```app/controllers/posts_controller.rb
class PostsController < ApplicationController
....

  def show
      @post = Post.find(params[:id])
  end

  def create
      @newpost = Post.new(post_params)
      @newpost.score = Language.get_data(post_params[:postbody])  #←追加箇所1 
      @newpost.save
      redirect_to  posts_path
  end

  def update
      @post = Post.find(params[:id])
      @post.update(post_params)
      @post.score = Language.get_data(post_params[:postbody]) #←追加箇所2
      @post.save                                              #←追加箇所3
      redirect_to  posts_path
  end
....
end
```

updateアクションの「@post.save 」は本筋とは関係ありませんが、
自分の場合、付けずにいたらscoreの更新がされなかったので追加致しました。
※Postsモデルにscoreカラムを追加した時に既存投稿のscoreはnilとなってます。


```app/views/posts/show.html.erb
<div>
  <%= simple_format(@post.postbody) %>
</div>
<p class="sentiment-score">感情分析スコア <%= @post.score %> / 1.0</p>　#←追加箇所
```

新規投稿・更新すると、その内容に応じて、-1.0〜1.0の値が入るようになります。
以下のようにすれば投稿内容のスコアに応じて投稿内容を非表示にすることもできます。

```app/views/posts/show.html.erb
<div>
 <% if @post.score >= -0.2 %>
  <%= simple_format(@post.postbody)%> #-0.2以上なら表示
 <% else %>
   感情スコアが低いため非表示にしてます。 #-0.2未満なら非表示に
 <% end %>
</div>
<p class="sentiment-score">感情分析スコア <%= @post.score %> / 1.0</p>
```
難しくないRustで画像表示そして画像認識(Rust+PyTorch)

Rust言語は難しいと思われているが、実際使ってみると実用的な優れた言語だと思う。
初心者殺しに会わないように、簡単にWindows表示ができて、描画、画像表示、画像認識ができるプログラムを紹介する。
RustでWindowを作成して、画像表示、描画をしたい、キーボードやマウスも操作したいとなると、標準のimage クレートではそこまでできない。
そこで適当なクレートを探そうとしても、多すぎて見つけるのが困難だ。そういう人にこの記事が参考になると嬉しい。
[show-imageクレート](https://github.com/robohouse-delft/show-image-rs)を使うと、Window作成とキーボード操作が簡単になる。
分かりやすいように、エラー処理は省いている。プログラムは以下の様な感じになる。

```Rust:show_image.rs
use show_image::{ImageView, ImageInfo, create_window};
#[show_image::main]
fn main() -> Result<(), Box<dyn std::error::Error>> {
　// 事前にpixel_dataに画像をロードする
  let image = ImageView::new(ImageInfo::rgb8(1920, 1080), pixel_data);
  // Windowを作成して画像を表示
  let window = create_window("image", Default::default());
  window.set_image("image-001", image);
// ESCキーが押されたら終了
for event in window.event_channel() {
  if let event::WindowEvent::KeyboardInput(event) = event {
        println!("{:#?}", event);
        if event.input.key_code == Some(event::VirtualKeyCode::Escape) && event.input.state.is_pressed() {
            break;
        }
    } 
}
Ok(()) 
}
```
このshow-imageを使って[マンデルブロー・ナビゲータ](https://jweb.asia/39-it/rust/66-rust-mandelbrot.html)を作るとこうなる。
今回はWebAssembly は使ってないので、より高速な処理が可能になった。
![mandel.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2255933/2c320f9e-7f3a-1441-9c55-a5f99ec542b1.png)
ROS初心者がUnity Robotics Hubでつまづいたところ

Unity-Robotics-Hub のチュートリアルはこちら

https://github.com/Unity-Technologies/Unity-Robotics-Hub

### チュートリアルが英語で難しい
とりあえずさらうためにも下記サイトを順に追っていくと特に問題なく進めることができました

https://note.com/npaka/n/n6cd4d79f22c2

### ソースを変更したい - Pythonソースコード
こちらで解決しました
・ソースコードを更新しても反映されない
・imageとcontainerの再作成が分からない

https://qiita.com/ninnegi/items/11846ea6f6d2a02289f0#%E3%82%BD%E3%83%BC%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B4%E6%96%B0%E3%81%97%E3%81%A6%E3%82%82%E5%8F%8D%E6%98%A0%E3%81%95%E3%82%8C%E3%81%AA%E3%81%84

### ソースを変更したい - Unity-ROS MessageGeneration ソースコード
『MoverServiceRequest』というスクリプトを変更したかったのですが、ソースコードの一番上に
`'Do not edit! This file was generated by Unity-ROS MessageGeneration'`
と記載されていて、自動生成されるため編集しない前提のソースコードでした。

でもrequest増やしたいんです・・・。

ということで自動生成部分のチュートリアルを読み直した結果、

https://github.com/Unity-Technologies/Unity-Robotics-Hub/blob/main/tutorials/pick_and_place/2_ros_tcp.md

下記画面の【build 1 srv】ボタン押下で自動生成されることが分かったため、『MoverService.srv』を見てみることに。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1548088/82220999-820f-a7d9-6a32-698e1bfbef21.png)
『MoverService.srv』に、requestで増やしたい内容を追記して保存。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1548088/8f3ed323-6104-091d-1c16-ed6454492d19.png)
もう一度先ほどの【build 1 srv】ボタンを押下して『MoverServiceRequest』を見に行くと、勝手に追加されてました（歓喜）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1548088/f5edc2df-3c5a-fca2-6fd7-1cedef530d0d.png)
LaravelでYouTube動画のIDが有効かどうかを検証する

# 問題

- YouTube動画のIDが有効かどうかを簡単に検証したい
- バリデーションメッセージを日本語化したい

# 環境

以下で検証

- Debian GNU Linux 10
- PHP 7.1
- Laravel 5.5

# 使用ライブラリ

こちらを使用

https://github.com/alaouy/Youtube
v 2.2.5を使用

# 方法

## Request

`app/Http/Requests/YouTubeVideoRequest.php`を作成して
`rules()`に以下を追加


```php:YouTubeVideoRequest.php

    public function rules()
    {
        return [
            'source_url' => [
                'bail',
                // これを追加
                new YoutubeVideoRule,
                Rule::unique('posts')->where( function($query){
                    return $query
                    ->where('user_id','=' , $this->user()->user_id)
                    ->where('deleted_at',null);
                }),
                'required', 
                'regex:/(http(s|):|)\/\/(www\.|)yout(.*?)\/(embed\/|watch.*?v=|)([a-z_A-Z0-9\-]{11})/i',
            ],

            //...省略...

        ];
    }
```

このままではバリデーションメッセージが英語のままである。
原因は

https://github.com/alaouy/Youtube/blob/v2.2.5/src/Rules/ValidYoutubeVideo.php

ソースの最下部、`return`部分で

```php
return 'The supplied URL does not look like a Youtube URL.';
```
となっているため。


# Rules

今回はカスタムルールを作成するので、ついでに`Rules.php`に追加

```php:YouTubeVideoRule.php
// ...省略...
use Alaouy\Youtube\Facades\Youtube;
// ...省略...
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        try {
            // ...何らかのバリデーション
            $videoId = Youtube::parseVidFromURL($value);
            $video = Youtube::getVideoInfo($videoId, ['id']);

        } catch (\Exception $exception) {
            return false;
        }

        return $video != false;
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        //ここで __()をつけておく
        return __('The supplied URL does not look like a Youtube URL.');
    }
```


```json:lang/ja.json

{
  "The supplied URL does not look like a Youtube URL.": "URLが無効です"
}

```
brew upgrade / "Error: Permission denied @ apply2files..." の対処法

# 環境

- macOS Montery
- Homebrew 3.3.4


# 背景

久々に`brew upgrade`した際に、`Error: Permission denied @ apply2files...`が発生したので、その対処法を備忘録として記す。

```terminal:terminal
$ brew upgrade
==> Upgrading X outdated packages:
:
:
==> Upgrading node
:
:
Error: Permission denied @ apply2files - /usr/local/share/systemtap/tapset/node.stp
```


# 対処法

まずは問題のファイルの権限を確認。

```terminal:terminal
$ cd /usr/local/share/systemtap/tapset/node.stp
$ ls -l
total 0
drwxr-xr-x  X root       wheel  XX XX XX XX:XX .
drwxr-xr-x  X root       wheel  XX XX XX XX:XX ..
lrwxr-xr-x  X user_name  wheel  XX XX XX XX:XX node.stp -> ../../../Cellar/node/XX.X.X/share/systemtap/tapset/node.stp
```

問題のファイル`node.stp`の所有グループ`wheel`に属しているユーザーを確認。

```terminal:terminal
$ dscl . -read /Groups/wheel
AppleMetaNodeLocation: /Local/Default
GeneratedUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
GroupMembers: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
GroupMembership: root
Password: *
PrimaryGroupID: 0
RealName:
 System Group
RecordName: wheel
RecordType: dsRecTypeStandard:Groups
```

もしくは

```terminal:terminal
$ dscacheutil -q group -a name wheel
name: wheel
password: *
gid: 0
users: root
```

これで、`brew upgrade`を実行したユーザーが`wheel`に属していないことが今回のエラーの原因と判明。

ここで、`root`ユーザで`brew upgrade`を実行したくなるが、他のファイルも含めて権限がおかしなことになるため、Homebrewとしては推奨されていない。却下。

そして、`chown`で所有グループを変更することも、権限がおかしなことになるのでおすすめしない。

今回は、**`wheel`グループに`brew upgrade`を実行したいユーザーを追加する**ことで対応することとした。

※ 個人用PCなら自身を追加すればよいので気にすることはないが、共有PCなら誰を追加するか注意が必要。

```terminal:terminal
$ sudo dscl . -append /Groups/wheel GroupMembership user_name
```

`wheel`グループに`user_name`が追加されたことを確認。

```terminal:terminal
$ dscl . -read /Groups/wheel
AppleMetaNodeLocation: /Local/Default
GeneratedUID: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
GroupMembers: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
GroupMembership: root user_name
Password: *
PrimaryGroupID: 0
RealName:
 System Group
RecordName: wheel
RecordType: dsRecTypeStandard:Groups
```

もしくは

```terminal:terminal
$ dscacheutil -q group -a name wheel
name: wheel
password: *
gid: 0
users: root user_name
```

追加できたので、`brew upgrade`を実行。

```terminal:terminal
$ brew upgrade
```

エラーもなくなり、更新完了。めでたしめでたし。
GatsbyでsvgファイルのbackgroundImage(tailwind)が表示されない。

GatsbyでStaticImageの画像は表示されるが、Tailwindのtailwind.config.jsで設定したbackgroundImageが表示されない。

```
backgroundImage: () => ({
        image: "url('/image.svg')",
        }),
```

また、publicフォルダに画像を入れると表示されるが、Devtoolsの要素インスペクターでimgで読み込まれてしまっており、Static形式で読み込まれていないことがわかる。

##解決方法

gatsby-config.jsのgatsby-plugin-react-svgの設定を以下のように更新する。

```
 　　　　{
      resolve: 'gatsby-plugin-react-svg',
        options: {
          rule: {
            include: /\.inline\.svg$/,
          },
        },
    },
```

また、tailwind.config.jsも更新する。

```
backgroundImage: () => ({
        image: "url('./images/image.svg')",
        }),
```

この変更を行うことで、以前は表示されていたsvgファイルが表示されなくなったので、以下のように更新すると表示されるようになった。

`import Logo from '../../images/logo.inline.svg'`
Go言語でKubernetes関連開発時に unknown revision v0.0.0 エラーが発生した時の対処

# コンパイルエラー全体

```
go: finding module for package github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned
go: found github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned in github.com/argoproj/argo-cd/v2 v2.1.6
go: github.com/argoproj/argo-cd/v2@v2.1.6 requires
	github.com/argoproj/gitops-engine@v0.4.1 requires
	k8s.io/kubernetes@v1.21.0 requires
	k8s.io/api@v0.0.0: reading k8s.io/api/go.mod at revision v0.0.0: unknown revision v0.0.0
```

# 対処方法


`go.mod` にk8s.io関連のバージョンを固定で指定する。KubernetesのAPIが1.21系であれば下記のように指定する。マイナーバージョンは時期によって都度調整すること。k8s.ioパッケージのバージョン一覧は、https://pkg.go.dev/k8s.io/api?tab=versions ここに記載されている。


```golang:go.mod
replace (
	k8s.io/api => k8s.io/api v0.21.6
	k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.21.6
	k8s.io/apimachinery => k8s.io/apimachinery v0.21.6
	k8s.io/apiserver => k8s.io/apiserver v0.21.6
	k8s.io/cli-runtime => k8s.io/cli-runtime v0.21.6
	k8s.io/client-go => k8s.io/client-go v0.21.6
	k8s.io/cloud-provider => k8s.io/cloud-provider v0.21.6
	k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.21.6
	k8s.io/code-generator => k8s.io/code-generator v0.21.6
	k8s.io/component-base => k8s.io/component-base v0.21.6
	k8s.io/component-helpers => k8s.io/component-helpers v0.21.6
	k8s.io/cri-api => k8s.io/cri-api v0.21.6
	k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.21.6
	k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.21.6
	k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.21.6
	k8s.io/controller-manager => k8s.io/controller-manager v0.21.6
	k8s.io/mount-utils => k8s.io/mount-utils v0.21.6
	k8s.io/kube-proxy => k8s.io/kube-proxy v0.21.6
	k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.21.6
	k8s.io/kubectl => k8s.io/kubectl v0.21.6
	k8s.io/kubelet => k8s.io/kubelet v0.21.6
	k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.21.6
	k8s.io/metrics => k8s.io/metrics v0.21.6
	k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.21.6
)
```

# 関連issue

- ['unknown revision v0.0.0' errors, seemingly due to 'require k8s.io/foo v0.0.0' · Issue #79384 · kubernetes/kubernetes](https://github.com/kubernetes/kubernetes/issues/79384)
- [k8s.io/api@v0.0.0: reading k8s.io/api/go.mod at revision v0.0.0: unknown revision v0.0.0 · Issue #90358 · kubernetes/kubernetes](https://github.com/kubernetes/kubernetes/issues/90358)
Airtable 日付から週の開始日を計算

ExcelやSpreadsheetでも同様のテクニックが使われていると思うが、
Airtableでも週単位の集計をしたい場合、「〜の週」という感じで週の開始日をグルーピングしたい場合がある。それを実現するための計算式について。

# 計算式

週を日曜始まりとした場合の計算式。

```text:計算式
DATETIME_FORMAT(IF(DATETIME_FORMAT({日付カラム},"e")=1,DATEADD({日付カラム},-1,"days"),IF(DATETIME_FORMAT({日付カラム},"e")=2,DATEADD({日付カラム},-2,"days"),IF(DATETIME_FORMAT({日付カラム日},"e")=3,DATEADD({日付カラム},-3,"days"),IF(DATETIME_FORMAT({日付カラム},"e")=4,DATEADD({日付カラム},-4,"days"),IF(DATETIME_FORMAT({日付カラム},"e")=5,DATEADD({日付カラム},-5,"days"),IF(DATETIME_FORMAT({日付カラム},"e")=6,DATEADD({日付カラム},-6,"days"),{日付カラム})))))),"YYYY/MM/DD")
```

長いので整形したものも掲載。

```text:整形した計算式
DATETIME_FORMAT(
  IF(
    DATETIME_FORMAT({日付カラム},"e")=1,
    DATEADD({日付カラム},-1,"days")
    ,IF(
      DATETIME_FORMAT({日付カラム},"e")=2,
      DATEADD({日付カラム},-2,"days"),
      IF(
        DATETIME_FORMAT({日付カラム},"e")=3,
        DATEADD({日付カラム},-3,"days"),
        IF(
          DATETIME_FORMAT({日付カラム},"e")=4,
          DATEADD({日付カラム},-4,"days"),
          IF(
            DATETIME_FORMAT({日付カラム},"e")=5,
            DATEADD({日付カラム},-5,"days"),
            IF(
              DATETIME_FORMAT({日付カラム},"e")=6,
              DATEADD({日付カラム},-6,"days"),
              {日付カラム}
            )
          )
        )
      )
    )
  )
,"YYYY/MM/DD")
```

# 解説

DATETIME_FORMAT({日付カラム},"e")で週の番号が0(日曜日)〜6(土曜日)で取得できる。
DATEADDで対象の日付から週の番号を引いてあげると、0(日曜日)の日付が取得できる。
IFをネストして1〜6の式を組めば良い。

なお、月曜始まりの場合は、曜日に対して加減算する値を変えてやれば良い。

```text:計算式(月曜始まり)
DATETIME_FORMAT(IF(DATETIME_FORMAT({売上日},"e")=0,DATEADD({売上日},-6,"days"),IF(DATETIME_FORMAT({売上日},"e")=2,DATEADD({売上日},-1,"days"),IF(DATETIME_FORMAT({売上日},"e")=3,DATEADD({売上日},-2,"days"),IF(DATETIME_FORMAT({売上日},"e")=4,DATEADD({売上日},-3,"days"),IF(DATETIME_FORMAT({売上日},"e")=5,DATEADD({売上日},-4,"days"),IF(DATETIME_FORMAT({売上日},"e")=6,DATEADD({売上日},-5,"days"),{売上日})))))),"YYYY/MM/DD")
```

```text:整形した計算式(月曜始まり)
DATETIME_FORMAT(
  IF(
    DATETIME_FORMAT({売上日},"e")=0,
    DATEADD({売上日},-6,"days"),
    IF(
      DATETIME_FORMAT({売上日},"e")=2,
      DATEADD({売上日},-1,"days"),
      IF(
        DATETIME_FORMAT({売上日},"e")=3,
        DATEADD({売上日},-2,"days"),
        IF(
          DATETIME_FORMAT({売上日},"e")=4,
          DATEADD({売上日},-3,"days"),
          IF(
            DATETIME_FORMAT({売上日},"e")=5,
            DATEADD({売上日},-4,"days"),
            IF(
              DATETIME_FORMAT({売上日},"e")=6,
              DATEADD({売上日},-5,"days"),
              {売上日}
            )
          )
        )
      )
    )
  )
,"YYYY/MM/DD")
```
クロージャについてのメモ

## クロージャ
クロージャを宣言しても呼び出しがあるまでは中身のコードは処理されない。

●基本記法

```swift
let closure = { (x: Int) -> String in
            String(x) + "あいうえお"
}
let str = closure(6)
// print: 6あいうえお

複数行の場合
let closure2 = { (x: String) -> NSDecimalNumber in
            let deciX = NSDecimalNumber.init(string: x)
            let deciY: NSDecimalNumber = 123
            return deciX.multiplying(by: deciY)
}
let decimal = closure2("10")
// print: 1230
```

他の記法

```swift
/* 引数と戻り値の省略 */
// 先に宣言する
let closure3: (Int) -> Double

// aaa はクロージャ内で使う引数的な存在(?)。今回ではIntにあたる
closure3 = { aaa in
            Double(aaa)
}
let double = closure3(22)
// print: 22.0
```
funcの引数にも使えるそうだけれどまだ試してないのでサンプルコードない。
使い方がわからないものは書けない。
UIGestureRecognizerについてのメモ

##画面操作系（単タップ、長タップ、ダブルタップなど）
``UIGestureRecognizer``配下にある指による画面操作系の制御。全部で8個。
・UITapGestureRecognizer
・UIPinch
・UIRotation
・UISwipe
・UIPan
・UIScreenEdgePan
・UILongPress
・UIHover

●基本記法（例：UILongPress）

```swift
class GestureViewController: UIViewController, UIGestureRecognizerDelegate {

 // view系のUI
 let tableView: UITableView!

 // viewDidLoad内で宣言する
 override func viewDidLoad() {
      let longTap = UILongPressGestureRecognizer(target: self
                                                 action: #selector(tappedButton))

      // delegateは細かい設定をするときには必要なのかもしれない[要調査]
      longTap.delegate = self

      // view全体に適応させたいとき
      self.view.addGestureRecognizer(longTap)
      // 特定のviewに適応させたいとき
      self.tabelView.addGestureRecognizer(longTap)
 }

 @objc func tappedButton(_ gesture: UILongPressGestureRecognizer) {
      if gesture.state == .Began {
          print("タップしてます")
       } else if gesture.state == .Ended {
          print("指離しました")
       }
 }

}
```

storyboard上でも紐付けできるらしい。
UIはStoryboardで作ってジェスチャーはコードで紐付けとかできるんだろうか。
NCMBのFlutter SDKを使ってTodoアプリを作る（その3：タスクの編集と削除）

NCMBでは公式SDKとしてSwift/Objective-C/Java/Unity/JavaScript SDKを用意しています。また、それ以外にもコミュニティSDKとして、非公式ながらFlutter/React Native/Google Apps Script/C#/Ruby/Python/PHPなど幅広い言語向けにSDKが開発されています。

今回はコミュニティSDKの一つ、Flutter SDKを使ってTodoアプリを作ってみます。[前回はタスクの追加](https://qiita.com/goofmint/items/17f65b64774106bcb5e2)を行いました。今回はタスクの取得と編集、そして削除を解説します。

## 完成版のコード

作成したデモアプリのコードは[NCMBMania/Flutter_Todo_App: Flutter SDKを使ったTodoアプリのデモです](https://github.com/NCMBMania/Flutter_Todo_App)にアップロードしてあります。

## タスクの取得

NCMBに保存されたタスクを取得する際にはNCMBQueryを利用します。これは `_TodoListPageState` の `initState` にて呼び出します。

```dart
// タスク一覧画面用ステート
class _TodoListPageState extends State<TodoListPage> {
  // Todoリストのデータ（初期値は空）
  List<NCMBObject> todoList = [];

  // 初期データをセットアップする処理
  @override
  void initState() {
    super.initState();
    getAllTask();
  }
}
```

`getAllTask` ではデータの取得と、結果をtodoListへ適用する処理を行っています。

```dart
// NCMBから既存のTodoリストを取得する処理
void getAllTask() async {
  // Todoクラス（DBで言うテーブル相当）を検索するクエリーオブジェクト
  var query = NCMBQuery('Todo');
  // データを取得
  var items = await query.fetchAll();
  // データを適用
  setState(() {
    todoList = items;
  });
}
```

### タスクの一覧表示

todoListをListViewで一覧表示しますが、スワイプ処理で削除ラベルを表示するために `Dismissible` を使っています。

```dart
ListView.builder(
  itemCount: todoList.length,
  itemBuilder: (context, index) {
    final item = todoList[index];
    // スワイプで削除する機能
    return Dismissible(
      key: Key(item.get('objectId') as String),
      child: Card(
        child: ListTile(
          title: Text(item.get('body') as String),
          onTap: () async {
            // 後述
          }
        ),
      ),
      direction: DismissDirection.endToStart,
      // スワイプした際に表示する削除ラベル
      background: new Container(
          padding: EdgeInsets.only(right: 20.0),
          color: Colors.red,
          child: new Align(
            alignment: Alignment.centerRight,
            child: new Text('削除',
                textAlign: TextAlign.right,
                style: new TextStyle(color: Colors.white)),
          )),
      // スワイプした際に処理
      onDismissed: (direction) {
        // 後述
      },
    );
  },
),
```

## タスクの編集処理

タスクを編集する際には、タスクの追加する時と同じくTodoPageを使います。新規作成時と同じくNCMBObjectを渡しますが、今回は一覧で利用している既存のNCMBObjectを渡すだけです。

また、更新された場合にはtodoListへの追加ではなく、既存データの上書きになります。

```dart
final NCMBObject obj = await Navigator.of(context).push(
  MaterialPageRoute(builder: (context) {
    return TodoPage(
      todo: item,
    );
  }),
);
// 編集後のデータがあれば、リストを更新する
if (obj != null) {
  setState(() {
    todoList[index] = obj;
  });
}
```

## タスクの削除処理

タスクをスワイプしきった際には削除を行います。削除は該当するタスクのdeleteメソッドで削除できます。

```dart
onDismissed: (direction) {
    // スワイプされた要素をデータから削除する
    setState(() {
      item.delete();
      todoList.removeAt(index);
    });
    // Snackbarを表示する
    ScaffoldMessenger.of(context)
        .showSnackBar(SnackBar(content: Text('削除しました')));
  },
);
```

## まとめ

NCMBObjectとNCMBQueryを使うことで、NCMBにおけるデータのCRUD操作が実現できます。初期化して利用する前の手順はとても簡単なので、ぜひFlutterアプリ開発に利用してください。

他にも写真などを保存するファイルストア、認証、プッシュ通知なども機能も用意されています。皆さんのアプリ開発に役立ててください。

[ncmb | Dart Package](https://pub.dev/packages/ncmb)
NCMBのFlutter SDKを使ってTodoアプリを作る（その1：画面の仕様とSDKの初期化）

NCMBでは公式SDKとしてSwift/Objective-C/Java/Unity/JavaScript SDKを用意しています。また、それ以外にもコミュニティSDKとして、非公式ながらFlutter/React Native/Google Apps Script/C#/Ruby/Python/PHPなど幅広い言語向けにSDKが開発されています。

今回はコミュニティSDKの一つ、Flutter SDKを使ってTodoアプリを作ってみます。まず画面の仕様とSDKの初期化について解説します。

## 完成版のコード

作成したデモアプリのコードは[NCMBMania/Flutter_Todo_App: Flutter SDKを使ったTodoアプリのデモです](https://github.com/NCMBMania/Flutter_Todo_App)にアップロードしてあります。

## ベースを作成する

`flutter create` コマンドでベースを生成します。

```
flutter create todo_app
```

## lib/main.dartについて

今回のコードはすべて main.dart に記述しています。クラスは次の5つです。

- MyTodoApp
- TodoListPage
- _TodoListPageState
- TodoPage
- _TodoPageState

### MyTodoApp

一番ベースになるStatelessWidgetです。MaterialAppの定義、TodoListPageの呼び出しを行います。

### TodoListPage

StatefulWidgetです。_TodoListPageStateを呼び出しています。

### _TodoListPageState

![FireShot Capture 230 - タスクアプリ - localhost 20211117111623 - 230.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/411f169e-cd15-50d8-497f-3e907b94c364.jpeg)


タスク一覧画面用です。NCMBにあるタスク一覧を取得して表示したり、タスクの追加や編集画面への遷移、タスクの削除を行います。

### TodoPage

StatefulWidgetです。_TodoPageStateを呼び出しています。

### _TodoPageState

タスクの作成と、既存タスクの編集を行います。

![FireShot Capture 230 - タスクアプリ - localhost 20211117111624 - 230.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/ce532d12-0516-d308-8c18-720488dcbe97.jpeg)


## NCMB SDKのインストール

NCMB SDKは `flutter pub add ncmb` でインストールできます。

```
flutter pub add ncmb
```

記事執筆時点での最新版は `2.0.3` です。

## NCMB SDKの初期化

まず main.dart にてNCMB SDKをインポートします。

```dart
import 'package:ncmb/ncmb.dart';
```

main関数にて初期化します。 `YOUR_APPLICATION_KEY` と `YOUR_CLIENT_KEY` はそれぞれ自分のものと置き換えてください。

```dart
void main() {
  // NCMBを初期化
  NCMB('YOUR_APPLICATION_KEY', 'YOUR_CLIENT_KEY');
  // 最初に表示するWidget
  runApp(MyTodoApp());
}
```

これでNCMB SDKの利用準備が整います。

## まとめ

今回は各画面の説明とNCMB SDKの初期化までを行いました。[次回はタスクの追加処理を実装](https://qiita.com/goofmint/items/17f65b64774106bcb5e2)します。
Python環境の構築

# はじめに
Macbook Air（macOS:High Sierra 10.13.6）にPythonの実行環境を構築しました。

### 手順1 Command Line Toolsをインストール
```
$ gcc
```
Command Line Toolsがインストールされていない場合は、インストールが開始されます。

### 手順2 Homebrewをインストール
```
$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
```
```homebrew-core is a shallow clane.```というErrorが出たので、
```$ git -C/usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow```
を実行しました。

### 手順3 Pythonをインストール
```
$ brew install python3
```
### 完了
Python 3.10.0がインストールされました。

```
$ python3
Python 3.10.0 (v3.10.0:b494f5935c, Oct  4 2021, 14:59:20) [Clang 12.0.5 (clang-1205.0.22.11)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
 ```

# 参考にしたページ
https://www.python.jp/install/macos/install_python.html
iPhoneで写真を撮ったらDropboxに自動保存されるようにする

IFTTTを使って、iPhoneで撮った写真が自動的にDropboxフォルダに保存される仕組みを作りました。

IFTTTのExploreの中にも「iPhoneのスクリーンショットをGoogle Driveに自動保存する」というものがあり、簡単に実装できるかと思ったら一手間必要だったので記事にしています。

#手順

https://ifttt.com/

こちら、IFTTTのサイトから新たなAppletを作ります。

上の「If This」で複数あるサービスから「iOS Photos」を選び、今回は背面カメラで撮った写真を自動保存するようにしたかったので「New photo with the rear camera」を選びました。

次に、「Then that」では、複数サービスから「Dropbox」を選び、「add file from URL」を選択。
ここで「Creat action」を選びAppletを作れば、iPhoneの背面カメラで撮った写真が自動的にDropboxに保存されるようになります。
![スクリーンショット 2021-11-17 12.41.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2051906/5f485c08-0912-4040-c04b-743262ce5faa.png)


#写真を開くには一手間が必要
しかし、実際にやってみると、Dropboxに新しくできたIFTTTというフォルダに写真は保存されるものの開くことができません。
ここに一手間が必要でした。

iPhoneで開こうとしても何も表示されないのですが、PCで開こうとすると「拡張子がないファイルは開けません」との記載が。
保存する写真ファイルの名前の後に「.jpeg」を付け加える必要があるのです。

具体的な手順は、
今回のappletを選んで右上の「Settings」から「Then」にすすみ、「File name」の部分で名前の後に「.jpeg」を付け加えればOKです。

#複数iPhoneからの写真を同じDropboxフォルダに送るには
当院の場合、毎日複数の医師が訪問診療にでていて、それぞれ担当の事務さんが記事の代行入力を行なっています。
どのiPhoneから送られてきた写真かを区別するには「File name」の下にある「Dropbox folder path」の部分で保存されるフォルダ名を書き換えるとできそうです。

#フィードバック
[クリニック](https://taro-cl.com/)のスタッフに仕組みの構想を伝えたところ記事入力する側の事務スタッフさんからは好評でした。
ただ、写真を撮る側の看護師さんからは、クリニックから支給された携帯とはいえ、ランチの写真をったりすることもあるということで「自動で写真あげるのを一時的に切ったりはできますか」との質問もありました。
これは、appletの画面で「connected」を切って「disconnected」とすれば良いので問題ないです。

プログラミング初心者でコードを書くことに自信がない身としてはIFTTTはとっても便利ですね。
医療現場でも活用できる場面は多いように思いますので、今後も様々な場面の実装にチャレンジしたいと思います。
異なる uvm_analysis_imp への write() を区別する方法

# 複数のクラスからトランザクションを受け取るときの問題
uvm_analysis_port.connect() により接続された uvm_analysis_port_imp へ write() が発行されると，uvm_analysis_imp インスタンスを持つクラスの write() が呼び出される仕組みになっている． 

ということは，複数の uvm_analysis_imp を一つのクラスで持っていたとしたらどうなるのだろうか． 

試してみたところ，どの uvm_analysis_imp に対して通信しても，同じ write() が呼び出された．どちらに向けてから発行された write() なのかを区別できないのだ．

#解決策
google で 「 UVM multiple analysis ports 」などと検索すると解決策が見つかった．

https://verificationguide.com/uvm/tlm-analysis-port-multi-analysis-imp-port/

このサイトによれば，imp 側を持つクラスの宣言の前に `uvm_analysis_imp_decl(_2) などと記述すると，新しいクラス uvm_analysis_imp_2 が使えるようになるので，これをメンバに持たせればよい．uvm_analysis_port 側では特に意識せずに write() すれば，uvm_analysis_imp_2 を持つクラスの write_2 が呼び出されるようになる．
jupyter notebookでデータフレームの全部を表示する

n行目まで表示したい時
```pd.set_option('display.max_rows', n)```
n列目まで表示したい時
```pd.set_option('display.max_columns', n)```
列ごとの最大表示幅に制限を設けない
```pd.set_option('display.max_colwidth', None)```
エンジニアの種類編（忘備録）

#そもそもエンジニアは多様な世界

#１．アプリケーション系エンジニア

未だ需要が高く、引く手あまたな現状ではある。
比較的学習サービスも豊富であり、特化型のエンジニアと比べてとっつきやすい部分ではある。

そのため、迷っている人はアプリケーションエンジニアから挑戦してみると面白いプログラミングライフを過ごせるかもしれない。

##フロントエンド(FE)
主にUIをコードに落とし込んでいるエンジニア。

【必須スキル】
HTML
CSS
JavaScript
モダンな開発ツール(Vue等)

AIが発達しても置き換わるのはもう少し先。

##バックエンド
やることが多くて語り尽くせないエンジニア。

【必須スキル】
主要なプログラミング言語（1つを深く）
データベース
AWS等のIaaS。主要なものをいくつか。

##iOS
iPhoneアプリやMAC OSで使用するものを開発。
割とAppleの盛衰に左右される部分は多分に含む。

【必須スキル】
Xcode(開発環境。多機能なので使いこなすのは難しい。)
Swift

##Android
Androidアプリを開発するエンジニア。

【必須スキル】
Android Studio(XcodeのAndroid版)
Java
Kotlin

JavaとKotlinは互換性があるので、どっちから学んでも大きな問題なし。

しかしながらPWAによって置き換わる可能性もあるので、iOSエンジニアに比べて少し逆風か。

#２．インフラ周辺

##SRE (Site Reliabilty Engineer)
サイトの障害の際にアラートを提示したり、運用業務周辺を担うエンジニア。大企業を中心に採用が広まりつつある。しかしベンチャー系企業はバックエンドとして採用される可能性もある。

「バックエンドもできるが、SREが特に得意」のようなイメージ。

【必須スキル】
バックエンドの知識
インフラの知識

##セキュリティ
サイバー攻撃の防御等、セキュリティに纏わる業務を遂行する。
しかしこちらもLINEレベルでセキュリティエンジニアという職種があるレベルで採用自体は少ない。狭い門ではある。

【必須スキル】
Linux/Unix等のOS周囲の知識
セキュリティの知識

##ネットワーク
資格が活きるらしい。あまり追えてません…。
ただネットワークもIaaS等で置き換えられるため、あまり伸びる部分ではないのかもしれない。

【必須スキル】
ネットワークの知識

#３．専門系

##機械学習エンジニア
数学的素養が必要になるエンジニア。修士・博士課程で終えている人が多い印象。求められる範囲が広く、様々なライブラリを動かすPythonを使えるかどうかは必須条件である。ただ将来性は間違いなくある。

【必須スキル】
統計解析
機械学習
データベース
Python

##ブロックチェーン技術
Dappを作るのにJavaScriptやSolidtyが必要。間違いなく高難易度職種の一つであり、常に最新の情報をキャッチアップすることが何より重要。

【必須スキル】
ブロックチェーン技術
→コンセンサスアルゴリズム等々
JavaScript
Solidity

##VUI(Voice User Interface)
画面を通してのUIから、声を通してのサービス開発に関わる。音声大和に関する知識は必要。
マネタイズモデルがそこまで確立されていない点が厄介。発展途上のフェーズである。

【必須スキル】
音声対話の知識
各種SDK(Alexaスキル等)

##Game開発
Gameを好む人が多い印象。しかし開発に当たり物理演算等の知識が必要になるため、数学的な素養も一定求められるかもしれない。

【必須スキル】
Unity（メインのゲームエンジン）
プラットフォームに応じた知識
→家庭用ゲーム、アプリケーション、ブラウザ等
数学的知識

#４．特殊系

##QA（テスト）エンジニア
品質保証の動作テスト等を行ったりするエンジニア。

【必須スキル】
テストに関する知識
各種テスティングツール

##セールスエンジニア
営業×エンジニアという非常に面白い職種。
toB向け商品の導入提案、導入後の運用保守を担当したりする。

【必須スキル】
取り扱うシステムに関する周辺知識
営業スキル
javascriptのメモ


####オブジェクト内の個数

```
Object.keys(this.$store.getters['topPage/newItemList']).length
```

####オブジェクトマージ
```
Object.assign(params, optionParams);
```

####オブジェクトのプロパティ更新(Vue)

```
this.$set(itemsObj, 'flg', true)
```

####オブジェクトのフィルター
```
this.objectFilter(response.data.items, (item) => item.cd != this.item.cd)
```
Pythonで特定のソフトをアクティブにする方法

Pythonで自動入力等をしたいときに、目的のソフトをアクティブにする方法の覚書き
#その１　自動クリックでアクティブにする
[【Python】Windows10：アプリケーションを最前面に表示・アクティブ化させる方法](https://office54.net/python/app/windows-foreground-active)が分かりやすい。

```active.py
import win32gui
import win32con
import pyautogui
import ctypes

def foreground():
    hwnd = ctypes.windll.user32.FindWindowW("目的のWindowタイトル", 0)
    win32gui.SetWindowPos(hwnd,win32con.HWND_TOPMOST,0,0,0,0,win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
    #2つ目の要素の内容
    #HWND_TOPMOST:ウィンドウを常に最前面にする。
    #HWND_BOTTOM:ウィンドウを最後に置く。
    #HWND_NOTOPMOST:ウィンドウの最前面解除。
    #HWND_TOP:ウィンドウを先頭に置く。
    
    left, top, right, bottom = win32gui.GetWindowRect(hwnd)
    win32gui.SetForegroundWindow(hwnd)
    pyautogui.moveTo(left+60, top + 10)
    pyautogui.click()

```
PyAutoGUIについては、[Pythonでマウスやキーボードを操作できるPyAutoGUIによる自動操作マニュアル](https://hashikake.com/PyAutoGUI)がとても参考になった。

#その２　特定のタイトルを持つウィンドウを最前面にする(常にではない)
[任意の文字の含まれたウインドウを最前列に持って行く関数を作りたい。その文字列を引数として渡す場合は？](https://teratail.com/questions/188610)を参照。

```Active2.py
import win32gui
import ctypes
def forground( hwnd, title):
    name = win32gui.GetWindowText(hwnd)
    if name.find(title) >= 0:
        if win32gui.IsIconic(hwnd):
            win32gui.ShowWindow(hwnd,1) # SW_SHOWNORMAL
        ctypes.windll.user32.SetForegroundWindow(hwnd)
        return False

win32gui.EnumWindows( forground, 'タイトルの一部')
```

最後に、ウィンドウクラスやタイトルをすべて取得するコード
調べてくれた先人に感謝です。

```titleGet.py
import win32gui

win32gui.EnumWindows(lambda x, _: print(str(x)+' : '+win32gui.GetClassName(x)+' : '+win32gui.GetWindowText(x)), None)
```

ウィンドウタイトルだけならこっちもあり

```get_window_title.py
import ctypes
def get_window_title():
    
    EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
 
    EnumWindows = ctypes.windll.user32.EnumWindows
    # タイトル格納用変数
    title = []
    def foreach_window(hwnd, lparam):
        if ctypes.windll.user32.IsWindowVisible(hwnd):
            length = ctypes.windll.user32.GetWindowTextLengthW(hwnd)
            buff = ctypes.create_unicode_buffer(length +1)
            ctypes.windll.user32.GetWindowTextW(hwnd, buff, length + 1)
            title.append(buff.value)
            return True
    EnumWindows(EnumWindowsProc(foreach_window), 0)
    print(title)

if __name__ == '__main__':
    get_window_title()
```
Open CV エラー error: (-215:Assertion failed) !_src.empty() in function 'cv::cvtColor' -> Numpy.fromfileを使用する

以下の様に<ダウンロード1.JPG>を表示させるプログラムを実行した。

```python
import cv2 as cv

image_picture = 'Resources/ダウンロード1.JPG'
img = cv.imread(image_picture)
cv.imshow('IMAGE',img)
cv.waitKey(0)
```

結果、エラーを検知

```
error: (-215:Assertion failed) !_src.empty() in function 'cv::cvtColor'
```

調査したところ、今回の場合はファイル名にカタカナが使用されていることが原因という仮説が立った。そのため、こちらの記事を参考にしてNumpy.fromfileを使用して修正を試みた。
http://solasyndrome.blog.fc2.com/blog-entry-16.html

```python
import cv2 as cv
import numpy as np

image_picture = np.fromfile("Resources/ダウンロード1.JPG", dtype=np.uint8)
img = cv.imdecode(image_picture, cv.IMREAD_COLOR)
cv.imshow('IMAGE', img)
cv.waitKey(0)
```

無事、表示に成功。
VirtualBoxでインポート時のエラー対応

# VirtualBoxでのインポートエラー(NS_ERROR_INVALID_ARG)
## 始めに
今回は、VirtualBoxでインポートエラー(NS_ERROR_INVALID_ARG(0x80070057))が発生したので、大した内容ではないですが備忘録としてやったことを書いておきます。
## 動作環境
* macOS Monterey 12.0.1
* VirtualBox 6.1.28
* CentOS Stream release 8

## 起こったこと
VirtualBoxでCentOSのovaファイルをインポートしようとしたところ、NS_ERROR_INVALID_ARG(0x80070057)というエラーが出ました。

##試したこと
* 友人が同じことをしてできていたので、同じ環境で実行するためにVirtualBoxのバージョンを6.1.26にダウングレードしました。
    * 特に変化なし
* エラーコード（0x80070057）で検索したところ、[VirtualBox結果コード：E_Invalidarg（0x80070057）の対処法](https://www.partitionwizard.jp/resizepartition/result-code-e-invalidarg-0x80070057.html)というサイトを見つけたので、ここにあるストレージの確保などを行いました。
    * 特に変化なし
* VirtualBoxについて調べていくと、ターミナルからCLIで操作できることがわかったので、[VirtualBoxをCUIで操作する](https://bablovia.hatenablog.com/entry/2019/10/01/223852)を参考にインポートを行いました。
    * インポートに成功しました。このあと、GUIでもインポートを行いましたが、問題なくインポートが行えました。

## まとめ
結果として、CLIでインポートを行うことで解決できました。
原因は今のところわかってないです。
以前にも起こった記憶があるので、また起こったら調査してみたいと思います。
to_i(num)がつまづいた件

RubySilverの勉強中疑問に思ったことがあったので備忘録として記していきます。

##to_s と to_i 使い方
to_s => 数値を文字列に変換
to_i => 文字列を数値に変換

```ruby:例
n = 8　
puts n.to_s #文字列に変換
# => 8

n = "8"
puts "8".to_i　# 数値に変換
# => 8
```

##to_s と to_i に引数を設定
to_s と to_i に引数を設定することで基数を変えることができます。
to_s にも使えることは少し驚きでした。
下記の1つ目の例を解説すると
**10進数**の8を**2進数**で表すという処理を行っています。

2つ目は**2進数**の8を**10進数**で表すという処理を行っています。
2進数は0と1で表すので無視されます。よって、0が出力されます。

3つ目は**2進数**の100を**10進数**で表すという処理を行っています。
ここで100は百ではなく100という文字列であることに注意してください

```ruby:例
n = 8
puts n.to_s(2)
# => 1000

n = "8"
puts n.to_i(2)
# => 0

n = "100"
puts n.to_i(2)
# => 4
```

##参考
[Ruby 3.0.0 リファレンスマニュアル](https://docs.ruby-lang.org/ja/latest/method/String/i/to_i.html)
teraterm menuのリストから登録した項目を削除する方法

わかりづらかったので備忘録として残します。

## 手順

リストから消したい項目をクリックして「▶」ボタンを押すと消せます。

例の場合ですと、リストの中にある「aaa_test」をクリックしてから「▶」ボタンを押すとリストから項目が削除されます。

<img width="500" alt="89db8d62-9fd2-a187-51cb-d1ace9423953.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308656/89db8d62-9fd2-a187-51cb-d1ace9423953.png">

以上となります。
gitでファイル一覧を取得する

Gitでソースコード管理しているが、
本番のアプリケーションサーバがgitに対応しておらず、FTPを使って手動でファイルアップしなければならないプロジェクトをやっています。

デプロイ漏れがないように簡単なチェックリストを作ろうと思ったら使っているGUIソフトにファイル一覧をコピーする機能がありませんでした。
そこでコマンドを検索したら以下のQ&Aで答え書いてくれていました。

```
git show commit_id --name-only
```

下の記事そのものです。

https://teratail.com/questions/189250
<Ruby>rationalizeメソッドとto_rメソッドの違い

# はじめに
rationalizeメソッドとto_rメソッド、どちらもレシーバーを有理数に変換したものを返してくれる「同じ」メソッドだと勘違いしてました。(sizeメソッドとlengthメソッドみたいな)
そんな私が、rubyコミュニティーの強い人たちに質問して得た知識をまとめます。
## to_rメソッドとrationalizeメソッドとは
レシーバーを有理数に変換します。同じ結果が取得できる時もあります

```
a = 0.25

a.to_r          ==>(1/4)
a.rationalize   ==>(1/4)
```

しかし

```
b = 0.1

b.to_r          ==>(3602879701896397/36028797018963968)
b.rationalize   ==>(1/10)
```
というふうに結果が異なる時もあります。

- to_rメソッド
https://docs.ruby-lang.org/ja/latest/method/Float/i/to_r.html
- rationalizeメソッド
https://docs.ruby-lang.org/ja/latest/method/Float/i/rationalize.html

## 両者の決定的な違い
**Float#to_r は Float の値と等しい有理数を返します。**
**一方、Float#rationalize は Float の値を近似する有理数を返します。**

では実例を以下で考えていきます。

```
b = 0.1

b.to_r          ==>(3602879701896397/36028797018963968)
b.rationalize   ==>(1/10)
```

そもそも、なぜ```b.to_r ```の場合、(3602879701896397/36028797018963968)という複雑な数字になってしまうのでしょうか。

それには、**"0.1"という文字列をFloatオブジェクトに変換する過程で丸め誤差が起きてしまっている**ことが関係しています。**プログラムのテキストは見た目が数値でも文字列扱い**なので、"0.1"という文字列をFloatオブジェクトに変換するというのが、プログラム実行開始より前に発生します。

その際、コンピュータは内部的に2進数を使って計算を行なっているのですが、0.1は2進数では（有限桁数で）表現しきれないため丸め誤差が起こるようです。

そして、丸め誤差を含んだFloatオブジェクトをto_rメソッドで有理数に変換しているので(3602879701896397/36028797018963968)という複雑な数字になっています。

ではなぜrationalizeメソッドの場合は(1/10)というスッキリとした数字なのかというと、rationalizeメソッドはFloatの値に近い有理数を探索してしてくれるためです。(3602879701896397/36028797018963968)に近い(1/10)を探し出して返してくれます。

## その他の違い
### rationalizeはStringクラスのオブジェクトに対して使用できない

String#to_rは存在しますが、String#rationalizeは存在しません。

```
"0.25".to_r        ==>(1/4)

"0.25".rationalize  ==>undefined method `rationalize' for "0.25":String 
```

rationalizeメソッドが使えるオブジェクトはIntegerクラス、Floatクラス、NilClassクラスのオブジェクトのみです。

https://docs.ruby-lang.org/ja/search/query:rationalize/

ですので、例えば競プロで入力を受け取る時

```
arr = gets.chomp.split(" ").map(&:rationalize)
```
とはできないので注意です。
BotUIでチャットボット開発（ツール編）

## はじめに

以下2つの記事の続きです。

* [BotUIを使ってチャットボットを開発してみた](https://qiita.com/t_power/items/e966b4a5f35340ed9a41)
* [BotUIでチャットボット開発（DB編）](https://qiita.com/t_power/items/aac7cc0e34736e557a0a)

本記事では、DBのデータをメンテナンスするためのツールを開発した話を扱います。

## ツールの概要

![ツールの概要.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1998725/643ab6c7-c56d-51b0-7883-150ef313da63.png)

上記の手順で管理することにより、プログラミングの知識が全くない方でも、シナリオを変更できるようになりました。
また、担当者が改行を含むデータを扱いやすくするために、CSVファイルはExcelで編集する運用にしました。

**実際のシナリオ管理ページのキャプチャ**
![シナリオ管理ページ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1998725/0799ae68-d0ae-e58d-ebec-c06cbe778d2c.png)

**実際のCSVファイルをExcelで開いたキャプチャ**
![スクリーンショット (203).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1998725/8c016b7f-043e-69a0-17fe-e1499167f4d0.png)

## 作業環境

OS: Windows10
エディタ: Visual Studio Code、サクラエディタ、Excel（Microsoft365版）
ブラウザ: Microsoft Edge
DB: MariaDB 10.5.12
サーバOS: CentOS 7
Webサーバ: Apache 2.4
言語: PHP 7.4、JavaScript、HTML、CSS

## 工夫した点

### セル内改行

チャットボット画面上で読みやすくなるよう、CSVファイル内の回答文には、改行を多く含めています。
Excelで編集する際には、セル内改行を使っています。

![セル内改行.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1998725/b97b7603-19d8-bc2a-e46b-362841f13723.png)

今回、この「改行をデータとして含むCSVレコード」の扱いにとても悩まされました。
CSVファイルを1レコードずつ読み込んで処理するようにしました。
しかし、セル内改行がレコードの終わりだと判定されてしまったため、うまくDBにINSERTできませんでした。

CSVファイルをサクラエディタで開くと、
![スクリーンショット (192).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1998725/164b50fa-d81a-d4fd-3542-354b873b8e0c.png)
このように、セル内改行はLF、次のレコードに飛ぶ際はCRLFを使っていることが分かりました。

そこで、

```index.html
<?php
//■レコードの配列（改行区切り）
$rowArray = explode("\r\n", $csvData);
?>
```
このようにして、PHPの**explode**関数を使用してCRLF区切りで分割することで、解決できました。

参考記事
[【初心者向け】PHPで文字列を分割するexplode関数の使い方](https://blog.codecamp.jp/php-explode)

### CSVファイルに出力

```outputcsv.html
<?php
//■ループしながら連想配列に代入
while ($dbData = mysqli_fetch_array($result, MYSQLI_ASSOC)) {

    //■アンシリアライズした結果を取得
    $unserialize1 = unserialize($dbData['category'])[0];
    if(count(unserialize($dbData['category'])) > 1) {
        $unserialize2 = unserialize($dbData['category'])[1];
    }

    //■データ生成
    $csvData .= ",{$dbData['question']},{$dbData['answer']},$unserialize1,$unserialize2\n";

    //■アンシリアライズした結果をリセット（第2カテゴリがない項目があるため、unserialize2のみ）
    $unserialize2 = '';
}
?>
```
上記コードはCSVファイルに出力させるためのスクリプトの一部になります。

DBの設計では、**category**カラムを1つしか用意していません。
そのため、1つの質問に対して、カテゴリが2つある（第一カテゴリと第二カテゴリ両方を持つ）CSVレコードをDBにINSERTする際には、PHPの関数、**serialize**を使用しています。

逆に、DBからCSVファイルへ出力する際には、シリアライズしたデータを元に戻す必要があるため、**unserialize**関数と**count**関数を使用しています。

```outputcsv.html
<?php
//■SQL文
$sql = "SELECT * FROM `{$TableName}` ORDER BY `value` ASC";
?>
```

**value**カラムは、DBにINSERTされた順で連番が振られるようにしています。
そのため、SELECT文のソート条件に指定することで、前回DBにINSERTした順番通りにCSVファイルへ出力されるようにしました。

```index.html
<?php
//■SQL文（テーブル初期化）
$sql = "TRUNCATE TABLE `{$TableName}`";
?>
```

また、DBにデータをINSERTする前に、TRUNCATE文を使用することで、全てのレコードを高速に削除し、書き換えています。

## おわりに

最初に投稿した記事に載せたチャットボットと比べると、とても使い勝手のいいものが出来たと思いました。
しかし問題点はまだあり、レコード数が増えた場合など、管理をどのようにしていくかがこれからの課題になってくると思います。

最後まで読んでいただき、ありがとうございました:bow_tone1:
python の zip 関数の動き

# zipが何をしているか？

zipがどのように動くか気になったのでpythonチュートリアルで調べた。

[pythonチュートリアル 5. データ構造より抜粋](https://docs.python.org/ja/3/tutorial/datastructures.html#nested-list-comprehensions)

```python
matrix = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
]
print(list(zip(*matrix)))
```
>[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

ネストしたfor

```
vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
list = [num for elem in vec for num in elem]
list = []
for elem in vec:
     print(elem)
     for num in elem:
         print(num)
         list.append(num)
print(list)
```

>
[1, 2, 3]
1
2
3
[4, 5, 6]
4
5
6
[7, 8, 9]
7
8
9
[1, 2, 3, 4, 5, 6, 7, 8, 9]

```
matrix = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
]

list_first = [[row[i] for row in matrix] for i in range(4)]
```
```
list_first = []
for i in range(4):
     print(f'first:{list_first}')
     list_second = []
     for row in matrix:
         list_second.append(row[i])
         print(f'matrix → second:{list_second}')
     list_first.append(list_second)
     print(f'first → second:{list_first}')
print(list_first)
```

>
first:[]
matrix → second:[1]
matrix → second:[1, 5]
matrix → second:[1, 5, 9]
first → second:[[1, 5, 9]]
first:[[1, 5, 9]]
matrix → second:[2]
matrix → second:[2, 6]
matrix → second:[2, 6, 10]
first → second:[[1, 5, 9], [2, 6, 10]]
first:[[1, 5, 9], [2, 6, 10]]
matrix → second:[3]
matrix → second:[3, 7]
matrix → second:[3, 7, 11]
first → second:[[1, 5, 9], [2, 6, 10], [3, 7, 11]]
first:[[1, 5, 9], [2, 6, 10], [3, 7, 11]]
matrix → second:[4]
matrix → second:[4, 8]
matrix → second:[4, 8, 12]
first → second:[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
[[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]

# 組み込み関数のzipと同じ動きをする

```
matrix = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
]
print(list(zip(*matrix)))
```

>[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]

# unpackについて

アンパックも曖昧だったので記載した。

```
a = list(range(3, 6))
print(a)
```

>[3, 4, 5]

```
args = [3, 6]
b = list(range(*args))
print(b)
```

>[3, 4, 5]
PowerShellで64ビット開発環境

# はじめに

Qt6.2がLTSになったのと Windows11が出たので開発環境を64ビットにシフトしようと考えたのが発端
今迄は32ビットのビルド環境だったので Windows Terminal の PowerShell から次のスクリプトを実行する事で開発環境の設定が可能だった

``` powershell
${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\Launch-VsDevShell.ps1
```

がこれだと32ビットのビルド環境なのでQt6.2がビルドできない
これのパラメータでアーキテクチャが指定できるか読んでみても分からなかった
ググった結果 設定できたので記します

# 前提

次の開発環境がターゲット

- Windows 10 x64
- PowerShell
- Build Tools for Visual Studio 2019

# 準備

この方法ではインスタンスIDが必要なので インスタンスID 取ります

1. [スタート]→[Visual Studio 2019]→[Developer PowerShell for VS2019] のコンテキストメニューを表示
1. [その他]→[ファイルの場所を開く]を選択<br/>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570185/ec0f3181-aaa7-660d-cb72-87e609b88482.png)<br/>
1. ショートカットの格納先フォルダが表示されるので 該当ショートカットのプロパティを表示<br/>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570185/a3cb2559-7a47-3d5a-8571-18ed5a4cf1a1.png)
<br/>
1. [ショートカット]ページの[リンク先]の値にインスタンスIDが含まれていて xxxxxxxx がそれ

# 実行

次のスクリプトをPowerShellのプロンプトに流してあげればok

``` powershell
& {Import-Module "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"; Enter-VsDevShell インスタンスID -DevCmdArguments -arch=x64}
```

:::note info
インスタンスIDは書き換えて書き換えて
:::

以下 実行例

``` powershell
PS C:\Users\username> & {Import-Module "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\Microsoft.VisualStudio.DevShell.dll"; Enter-VsDevShell インスタンスID -DevCmdArguments -arch=x64}
**********************************************************************
** Visual Studio 2019 Developer PowerShell v16.11.6
** Copyright (c) 2021 Microsoft Corporation
**********************************************************************
PS C:\Users\username> cmd /c where cl
C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\VC\Tools\MSVC\14.29.30133\bin\Hostx64\x64\cl.exe
```

# 所感

これで漸くQt6.2での開発が出来そうです♪

# 参考・感謝
次のページがを参考にさせて頂きました！感謝！

http://mklearning.blogspot.com/2019/12/developer-powershell-for-vs-2019x64.html
pd.date_rangeより特定日時のインデックスを取得する方法

追記
`pandas.get_loc()`や`pandas.searchsorted()`というメソッドがあることを教えていただきました．<b>コメント欄をご覧ください．</b>

---------------------------------


暗算でも良いですが，パソコンに考えてもらった方が安心ですよね．

numpy.whereを使います．
 numpyは不要です．コメント欄をご覧ください．

```python3
import pandas as pd
import numpy as np

# 時系列の作成
times = pd.date_range('2015-04-01 00', '2015-04-30 18', freq='6H')

# 特定日時
t = '2015-04-13 12'

# 特定日時のインデックスの取得
i = np.where(times==t)[0][0]
```


最初の[0]はタプルのアンパック，次の[0]はnumpy.ndarrayのアンパック，最終的にnumpy.int64を返します．

numpy.whereはTrueのインデックスを返します．一方，pandas.whereはFalseとなるインデックスの値をNaNに置換します．
pandasだけでできると良いなと思いましたが，自分の知る限りnumpy使うのが楽でした．


参考
[pandasで条件に応じて値を代入（where, mask） - note.nkmk.me](https://note.nkmk.me/python-pandas-where-mask/)
[numpy.where – インデックスの検索 - TauStation](http://taustation.com/numpy-where/)
UbuntuからWindows共有フォルダにアクセス

あるWindowsマシンに共有フォルダがある。
同じネットワーク上にあるUbuntuマシンから、この共有フォルダにアクセスする方法。


```shell
$ dpkg -l | grep cifs
# No output

$ sudo apt install cifs-utils

```

```
sudo mkdir -p $HOME/mnt/windows

sudo mount -t cifs -o username=MyUsername,password=MyPassWord //xxx.xx.xxx.x/shared_storage_name $HOME/mnt/windows

```


references

https://bsj-k.com/ubuntu-access-win-common/

https://qiita.com/mdstoy/items/54925cdcbca6d558b666
RedBearLab nRF51822 mbed 起動について

#はじめに

BLE 通信を行ってみようと思い、RedBearLab nRF51822 を入手しました。
既に販売終了のようで、開発元の商品ページもアクセスできなくなっていますが、ライブラリ等は GitHub にあるので何とかなるだろうと思っていました。
電源を入れてみると「mbed」ドライブが表示されたので、さっそく Arduino bootloader を格納しようとしましたが、リセットがかかり、それ以降は 「BOOTLOADER」ドライブしか見えなくなりました。
色々と検索した結果、文鎮化を解除するために DaplinkBootloader をアップデートすればよいみたいです。
そこで、DaplinkBootloader（0244_k20dx_bootloader_update_0x5000.bin） と Interface Firmware（0254_k20dx_rbl_0x5000.bin） をアップデートしました。
しかし、状況はかわらず、BOOTLOADER の代わりに「MAINTENACE」ドライブしか見えません。
たまたま、1回だけ「DAPLINK」ドライブが見えたことがありますが、しばらくするとリセットがかかり「MAINTENANCE」ドライブになってしまいます。
英語のページも含めて、似たような現象は記載されていません。

#対策

どうも RESET Key が押されたままの状況ではないかと思い、回路図を見てみると RESET から MK20D の PTB1 への経路に 抵抗 R3 が入っているだけです。
このような場合は、内部でプルアップしているはずですが、もしかしたらやっていないのではないかと思い、J9 の RESET と 3.3V を 100KΩ の抵抗で繋いでみました。
結果、無事「DAPLINK」ドライブが見え、Arduino bootloader を保存して再起動すると Arduino IDE からスケッチをアップロードすることができました。
もちろん、RESET Key を押したまま USB ケーブルを繋ぐと「MAINTENANCE」ドライブが見えます。
ピンヘッダに抵抗を挿したままというわけにはいかないので、基板の裏の当該箇所にハンダ付けしました。

![nRF51822.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245347/f7b5caed-0a93-b04c-5b5d-261fa7984382.png)
Googleカレンダーの予定に登録された目的地までの経路・所要時間をLINEにお知らせする #ノーコード #GoogleMaps

##地理に疎い人！必見！
出かけるとき、道順・路線・所要時間を調べないとたどり着けない人。いると思います。私のことです。
更には調べるのを後回しにしがちなので、調べてみたら既に間に合わない時間だった。なんてこともよくあります。
そんな人のために、その日の予定の目的地までの経路と所要時間を大体教えてくれる品を作りました。
これさえあれば**もう遅刻する必要なんてない**


##システム構成

https://www.integromat.com/en

Integromatで作成
１．Googleカレンダーから当日の予定を取得する。（目的地をしっかり入れてください）
２．Google Maps で自宅から予定の目的地までの距離・経路・時間を取得する。
３．LINE Notify で結果を通知する。
４．エラー発生時（予定に目的地設定がされていない等）はIgnoreで終了させる。
![img1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/84ea971b-b05f-e527-9a28-076f868489f1.jpeg)


##完成デモ
例えば、このような予定だった場合、
![img5.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/b2a90c5c-84fe-c9fe-e67f-722e874fa232.jpeg)


朝起きると、その日（11/17）の予定の目的地までの所要時間と道のりがLINEに通知されてます。
「Google Maps Direction API」は**日本の路線に対応していない**ので、メッセージに表示される所要時間は車での移動時間になります。（設定で徒歩にも変更可能）
ただし、URLを押すとブラウザで経路が表示され、**そちらではタブ切り替えで路線経路を見ることができます**。
通知は朝まとめて届くので、
・まずは朝にざっと見て大体どれくらいかかるのかを把握する
・近くなってきたらURLをたたいて最新情報（路線もチェック）を見て出発
という流れです。**遅刻に対する不安感が払拭されました。無遅刻王になれそうです。**
![img6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/1936b227-d520-bac4-77cf-749c8731d348.jpeg)


##実装

####Googleカレンダー
Search Event で指定の期間のイベントを取得します。

![img2.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/602a6b97-2d73-553a-9577-c62111041cac.jpeg)

####Google Maps
Originは現在地、緯度経度を直接指定しております。
Destinationは行き先、Googleカレンダーから取得した目的地を設定しています。
Trabel Modeは車・徒歩・電車などの交通手段が選べます。
![img3.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/f1da6be6-9ba1-b4c8-1c92-b24ae496e47d.jpeg)
**＊＊重要＊＊**
Google Maps Direction API は日本の路線に対応していないみたいです。
詳しくはこちら。（感謝:pray:）

https://qiita.com/seigo-pon/items/bff784040e9e46dc8a56


####LINE Notify
Googleカレンダー、Google Mapsより取得した情報をメッセージに編集して通知します。
赤い部分がカレンダー、青い部分がGoogle Mapsからです。

![img4.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/3040f393-02b1-abd2-5561-8d75eadf6c35.jpeg)

##使用後の評価
:sparkles:**入力は簡単、日常使いできそう**:sparkles:
今までカレンダーに場所まで細かに入れない人だった私です。
この機能は**場所の入力が必須**になるので、実際に入力が面倒じゃないか、懸念していました。
が、結果としては負担なく入力することができました。
場所の名前を入れれば、Googleカレンダーが候補地を出してくれる点が大きい。
さすがGoogleです。

:sparkles:**何となく所要時間が把握できるだけでよい**:sparkles:
解説では朝に届くとしていましたが、前日の夜に明日の予定は～と連絡が来るようにしました。
メッセージ上に「○○まで30minsです」と書かれているのを「ふーん、30分か」とちょっとだけ頭に入るだけで結構違いました。

:question:**遅刻は減ったのか**:question:
今のところ遅刻はしていない。そもそも時間指定の約束がほとんどなかった。。。
所要時間が車になる点が懸念されます。近場であれば車も電車も大ブレしない何となく把握するのにはよいですが、遠くになるのブレは発生して、電車で行く場合は毎回URLをたたいてブラウザで確認しなければいけなくなる。と少し面倒になるかもしれないです。
引き続き使用して確かめていきたいと思います。



##感想
ノーコードで作成することができました。今回、１行もコードを書いていません。
これがIntegromatさんの力です。コード書くの苦手勢の私には最高の味方です。使い倒していきたい。そのためにアイデア力を鍛えていきたいと思います。
また、今回 Google Map を使ったのですが、APIの使用には Google Cloud Platform の無料トライアルに登録する必要がありました。90日間、$300のようです。
期限内に使い倒さなければ！！！
![キャプチャ.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2143015/34832cae-d58a-9db3-64d2-0b58eb678108.jpeg)



##参考記事（感謝:pray:）

https://qiita.com/nendocode/items/b3f9f9810a0dfd744dcf

https://www.zenrin-datacom.net/business/media/g001/