OmniAICreator/Qiita-1.07M huggingface dataset:
SEO基本

# SEOの基本

## title , description

```hoge.html
<title>Qiita</title>
<meta name="description" content="Qiitaは、プログラマのための技術情報共有サービスです。 プログラミングに関するTips、ノウハウ、メモを簡単に記録 &amp;amp; 公開することができます。" />
```

- 何に使われるの？  
 - ブラウザのタブに表示される(title)
 - Googleなどの検索結果一覧に表示される

![スクリーンショット 2021-02-03 10.30.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213395/a8be9fc6-774c-70b5-c7bc-806b4972fc01.png)


参照：https://wacul-ai.com/blog/seo/internal-seo/title-tag/  
SEOとしては、
>titleタグはGoogleが検索順位を決定するためにサイトを評価する際に用いられます。
>そのため、titleタグを最適化することで掲載順位をあげることができる可能性があります。
>逆にtitleを設定しなかったり、不適切なtitleになっている場合には、SEO的に非常に大きな損をしているといえます。

ユーザからすると、
検索一覧からそのサイトの内容を判断する情報なので、
クリックしてもらうために重要な記述となります。

## keywords　(結論：重要ではない）

```hoge.html
<meta name="keywords" content="知識共有,投稿,技術,IT" />
```

参照：https://www.seohacks.net/basic/terms/metakeywords/
>メタキーワードは、HTMLファイルのタグ内に記述するメタ情報の1つ
>以前は、ページ情報がどのようなキーワードと関係しているのかをクローラーに対し伝える役割
>ブラックハット的な使用などにより、現在は非サポートのメタタグでSEO的な効果はない

現在は、Googleのクローラーから無視される。
設定しても良いが最小限にする。

## nofollow 
```hoge.html
<a href="https://qiita.com/documents/faq.html" rel="nofollow">FAQ</a>
```

参照：https://www.seohacks.net/basic/terms/nofollow/
>「このリンクは無効化してください」と検索エンジンに対し伝えることができます。

- どういったケースで使うのか。  

 - 信頼できないサイト（コンテンツ）へのリンク  
リンク先が信頼出来ないサイトやリンク先にリンクジュースを受け渡したくないと思うサイトのリンクを仕方なく設定する場合

 - 有料リンク  
有料リンクとは、ページランクや検索順位を意図的に操作することを目的としたリンクを金銭で獲得することでございます。有料リンクはGoogleから禁止されており、SEO目的の相互リンクや低品質のディレクトリ登録等があげられます。もし、SEO目的ではなく宣伝・広告用に有料リンクを設定する際、もしくは既に設定している際はnofollowに指定することが推奨されます。

 - クロールの優先順位  
検索エンジンは、IDやパスワードを用いて登録やログインをすることができません。その為、ログイン画面や注文フォームなどをnofollow指定しておくことで、クローラーはリンクを辿りません。その代わりに、インデックス登録したいと思っているページや重要な下層ページに対して優先的にクロールしてもらいやすくすることができます。:eyes:

## noindex 

```hoge.html
<meta name="robots" content="noindex,nofollow">
```

参照：https://www.seohacks.net/basic/terms/noindex/
>noindexとは、特定のURLを検索エンジンにインデックスさせないようにするための構文です。
>noindexの記述されているURLは、原則としてどのようなキーワードで検索しても検索結果に表示されることがなくなります。

- どういったケースで使うのか  
 - ページ自体は必要
 - 検索結果には表示させたくない
 - robot.txtやベーシック認証で制御するまでのものではない

- 例えば？
　検索画面で、詳細な検索条件を指定しているリンクにはnoindexをつけたりします。
これは、主要な検索ページと情報が重複しているとクローラーに認識させないためです。

## canonical　と　alternate　

PCとスマホで URLが異なる場合、適切に設定する必要がある。
参照：https://marketingnative.jp/how-to-implement-alternate-tag/

- スマホ版でアクセスしたときに、正式なURLは、https://www.hogehoge.com/だよ、と伝える。

```(スマホ版に記述する)hoge.html
<link rel="canonical" href="https://www.hogehoge.com/" />
```

- PC版URLでアクセスしたときに、ちゃんとこちらにスマホ版を用意しているよ、と伝える。

```(PC版に記述する)hoge.html
<link rel="alternate" media="only screen and (max-width: 640px)" href="https://s.hogehoge.com/">
```


モバイルフレンドリーインデックス(MFI)の影響としては、
異なるURLでデスクトップ用とモバイル用のページを作成していて、
alternateタグとcanonicalタグが実装されていない場合、
サイト全体が「モバイル対応がされていない」と判断され、
検索順位に影響が出る可能性があるようです。


以上。
マークダウン記法とは

#　はじめに
2021年2月3日(水)、自身のアウトプット兼備忘録としてQiitaに記事を残すことを同僚と誓う。どの程度の拘束力があるかは不明だが、日付を書くことで自身はもちろん同僚にも更新のプレッシャーをかけていく。とはいえ、ほとんどなにも知識がない状態からなので、手始めにQiita記事自体を書く上で必要となる「マークダウン記法」について書く。筆者の現在のレベル感を記録するため、感想も逐次書いていく。

#　マークダウンとは
そもそもマークダウンとは、という観点から話を始める。自身に問うても答えは返ってこないので、さっそくWikipediaに問う。

>Markdown（マークダウン）は、文書を記述するための軽量マークアップ言語のひとつである。本来はプレーンテキスト形式で手軽に書いた文書からHTMLを生成するために開発されたものである。しかし、現在ではHTMLのほかパワーポイント形式やLATEX形式のファイルへ変換するソフトウェア（コンバータ）も開発されている。各コンバータの開発者によって多様な拡張が施されるため、各種の方言が存在する。

なるほど。プレーンテキスト形式からHTMLを生成できるわけか。学生時代にLATEXで淡白な文章や数式ばかり書いていた筆者はHTMLに対する理解がないが、いわゆるWebの記事もきっとマークダウンで書かれているのだろう。読みやすい記事を書く上では必要な知識ということのはず。HTMLもよくわかっていないがなにごとも勉強。

#　マークダウン記法
そんなことを書いているうちにもいくつかマークダウン記法を使っている。マークダウン記法では以下のような表記が扱えるらしい。（もちろんWikipediaさんに教えていただいている。）

　1. 段落
　2. 改行
　3. テキストの強調
　4. コード
　5. リスト
　6. 見出し
　7. 引用
　8. リンク
　9. 水平線

その他にも画像の挿入などたくさんあるが、とりあえず少しずつ学ぶということでこの9つをまずは書く。初投稿で気合を入れすぎて更新頻度があがらないと意味がないので、とりあえずここまで。次の記事で詳しい内容を書く。
Pythonで外部ライブラリが標準出力に吐くメッセージを抑制する

# What's this?
pythonでimportしたライブラリがデバッグメッセージを標準出力に撒き散らして困ったときのad hocな対処方法です。

# コード
```python
import os

# 前処理
fd_null=os.open('/dev/null',os.O_WRONLY) # /dev/nullをwrite onlyでopenし，ファイルディスクリプタを取得
fd_stdout_bak=os.dup(1) # stdout(fd=1)を複製して退避
os.dup2(fd_null,1) # /dev/nullのファイルディスクリプタをstdoutに複製 (stdoutを/dev/nullへリダイレクト)
os.close(fd_null) # 元の/dev/nullのファイルディスクリプタは使わないので閉じる

# ここで標準出力を汚す困ったちゃんを呼び出す
# pcd=open3d.io.read_point_cloud(filename)

# 後始末
os.dup2(fd_stdout_bak,1) # 退避しておいたstdoutを元に戻す
os.close(fd_stdout_bak)  # stdoutの複製を閉じる
```

# 何やってるの？
標準出力を一時的に/dev/nullにリダイレクトして出力を捨てているだけです。
/dev/nullが存在しないOSでは動きません。未確認ですがWindowsでは代わりにNULデバイスを開けばいいような気がします。

他の言語でもファイルディスクリプタをいじることができれば，ほぼ同じ手順で困ったちゃんを黙らせることが可能です。

# ちゃんとした対処方法は?

なにかの拍子に後始末がすっぽ抜けると，意図した`print()`の出力まで捨てられて悲しい思いをします。`try: ... finally:`でくくってやるか，`with`構文を使えるようにクラス化するのが良さそうです。

しかしながら，そもそもライブラリが標準出力にデバッグメッセージを吐いていることが問題なので，issueを上げ，可能ならパッチなりPRなりを送りつけるべきでしょう。
パンチングボード用のフックが一個足りなかったので3Dプリンタを買ってBlenderの勉強をして1日かけてフックを出力した話

# 事の発端
タイトルの通り
パンチングボード用のフックが一個足りなかったから、3Dプリンタを買おうと思った

# 買った3Dプリンタ
ANYCUBIC MEGA-S
https://amzn.to/39G7kvD
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023362/2fa15163-5852-58ae-d872-4e2e5c16b8f3.png)

# なぜblenderなのか
3Dプリンタ買った人はFusion360を使う事が多いらしい
ただ、無料版は1年間の期限付きという部分でなんとなく使う気が起きなかった

blenderなら名前は聞いたことがあるし、簡単そうだからblenderにした

**ただし、今となってはやはりFusion360の方が良いなと思っている**
理由としては精密性
blenderは1辺の長さとか分かりにくいし、mm単位の設定が難しい
Fusion360はそこら辺しっかりしてる
(ただ、自分がblenderを使いこなせていないだけかもしれないが)

# 結局どうなったのか
試作一号はサイズが大きすぎてパンチングボードの穴に入らなかった(一番上)
試作二号は形が歪すぎてパンチングボードに上手い事入らなかった(真ん中)
試作3号は良い感じ(一番下)
![IMG_0812.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023362/aba29239-bd3a-a505-433c-2957696aeb38.jpeg)

そして、
![IMG_0813.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1023362/37220191-bc5e-6b21-a10d-9d46de401f79.jpeg)
無事、試作3号はフックとして機能してくれた(ヘッドホンをかけているフックがそれ)
めでたしめでたし

# これから3Dプリンタを買おうとしている人に言いたい事
### 1. 騒音・振動対策は必須
3Dプリント中は騒音も振動も結構すごい
アパート暮らしとかだと割と怒られるレベル
特に、振動は対策必須
自分は、硬めのニトリのマットを6枚くらい重ねた上に3Dプリンタを置いて対処した

### 2. 温度設定、気温に注意
フィラメントを溶かして、重ねて造形していくタイプのプリンタは、フィラメントの冷えが天敵
朝、夜は3Dプリントが結構失敗する(複雑な形のゴミが出来る)
昼は割と上手くいった

### 3. ビルドプレート密着性は「ブリム」
ANYCUBIC MEGA-Sはスライサーにcuraを推奨している
curaのビルドプレート密着性という項目にはいくつか設定できる値がある
デフォルトはスカート

ビルドプレート密着性は、3Dプリント中にフィラメントが剥がれにくくなる様に作る土台の設定のこと
スカートはないも同然
ブリムは一層土台を作る
もう一個はよく知らん(試してない)

スカートで失敗続きだったところ、ブリムにしたら上手くいった
【2021年版】OPEN-MMLAB/MMDETECTIONのインストール

# 0. 概要
2019年頃にOPEN-MMLABをインストールして、MMDETECTIONを動作させた。
https://qiita.com/harmegiddo/items/55da6020314a12ba5479
あれから早いもので2年が立ち、TorchもCudaも様々なバージョンが出てきており、それらに合わせてインストール方法も少しだけ変化していたので、追記したいと思う。

なお記事を分けた理由としては、前回のパターンでも動作する場合は動作するので、前回記事で動作しない人にはこちらの記事でインストールを進めてほしい

参考元は以下のopen-mmlabの本家のサイトである。
https://github.com/open-mmlab/mmdetection/blob/master/docs/get_started.md
因みにこのままやっても上手くは動かない。

# 1. インストール

## 環境

以下の環境からOPEN-MMLAB/MMDETECTIONを構築していく。

```
CUDA=11.0
CUDNN=8.0
python=3.7
pytorch=1.4
```

なお物理的なGPUにはRTX3090を用いている。


## インストール
まずは何も考えずに以下で仮想環境を構築する。


``` bash
$ conda create -n open-mmlab python=3.7 -y
$ conda activate open-mmlab
```

次に以下でTorch環境を整えていく。

``` bash
$ conda install pytorch cudatoolkit torchvision -c pytorch
```

※しかし、問題が発生する。最新のGPUやドライバーではTorchやCudatoolkitも最新のものをビルドして入れなければならないことがある。ここで、自身の環境にあった最新のTorchやCudatoolkitをインストールしたものとする。

Torch周りのインストールでエラーが出るときは以下を参考。
https://qiita.com/harmegiddo/items/c1b781c82ab9c1c811bd


一先ず、Torch、Cuda環境等が整ったら、
以下のウェブサイトから自身の環境と合う`mmcv`を探してこよう。
https://download.openmmlab.com/mmcv/dist/index.html

自身の環境にあったmmcvのWHLをダウンロードして以下を実行

``` bash
$ pip install mmcv_full-1.2.2+torch1.7.0+cu110-cp37-cp37m-manylinux1_x86_64.whl
```
この場合だと、Torch 1.7、Cuda 11.0、Python 3.7という意味。

後は以下でMMDETECTIONをインストール

``` bash
$ git clone https://github.com/open-mmlab/mmdetection.git
$ cd mmdetection
$ pip install -r requirements/build.txt
$ pip install -v -e .  # or "python setup.py develop"
```

後の使い方は前回記事通り！
https://qiita.com/harmegiddo/items/55da6020314a12ba5479
Ubuntu20.04でpyenvでPython3.4.xを使う


## 記録
Ubuntu20.04にはOpenSSL1.1が入っていて、

```bash
$ openssl version
OpenSSL 1.1.1f  31 Mar 2020
```

OpenSSL1.0ベースで書かれているPython3.4.xを、pyenvでインストールしようとすると、OpenSSLの1.0と1.1でAPIが大きく変わっているため、関数が見つからないなど、OpenSSL周りでエラーが大量に出てビルドに失敗する。

```bash
ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?
```

libssl1.0-devというUbuntuのパッケージがある記事があったので、入れようとするが見つからない。

```bash
$ sudo apt install libssl1.0-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
Package libssl1.0-dev is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source

E: Package 'libssl1.0-dev' has no installation candidate
```

OpenSSL1.0をローカルでビルドして、それを使ってPythonをビルドしても失敗。

```bash
$ git clone https://github.com/openssl/openssl.git
$ cd openssl
$ git checkout refs/tags/OpenSSL_1_0_2
$ ./config
$ make
$ LDFLAGS="-L/.../openssl" CFLAGS="-I/.../openssl/include" pyenv install -v 3.4.3
...
BUILD FAILED (Ubuntu 20.04 using python-build 1.2.22-47-g4c302a02)
...
```

ログを見てみると、

```bash
building '_ssl' extension
...
/usr/bin/ld: /.../openssl/libssl.a(s2_srvr.o): relocation R_X86_64_PC32 against symbol `ssl2_accept' can not be used when making a shared object; recompile with -fPIC
....
```

共有ライブラリとしてOpenSSLをコンパイルしろとあったので、共有ライブラリを作る。

```bash
$ CFLAGS=-fPIC ./config shared
$ make
```

OpenSSL1.0の共有ライブラリの置き場所を、探索パスに追加して、

```bash
$ echo /.../openssl | sudo tee /etc/ld.so.conf.d/openssl-1.0.2.conf
$ sudo ldconfig
```

もう一度トライするとうまくいった。

```
$ LDFLAGS="-L/.../openssl" CFLAGS="-I/.../openssl/include" pyenv install -v 3.4.3
...
Installed Python-3.4.3 to /.../.pyenv/versions/3.4.3
```
初心者がdockerでphpの開発環境を整える#1 [dockerとは？]

#dockerでphpの開発環境を整える
phpの練習をするためには、開発環境を整える必要があるみたいなので、
dockerを利用して整えていきたいと思います。
その前に、'docker' って何？
##dockerとは
docker: ![docker](http://cdn-ak.f.st-hatena.com/images/fotolife/p/portaltan/20150528/20150528170949.jpg "docker")

>Docker（ドッカー[2]）は、コンテナ仮想化を用いてアプリケーションを開発・配置・実行するためのオープンソースソフトウェアあるいはオープンプラットフォームである[3]。
Dockerはコンテナ仮想化を用いたOSレベルの仮想化（英語版）によりアプリケーションを開発・実行環境から隔離し、アプリケーションの素早い提供を可能にする。かつその環境自体をアプリケーションと同じようにコード（イメージ）として管理可能にする[4]。Dockerを開発・テスト・デプロイに用いることで「コードを書く」と「コードが製品として実行される」間の時間的ギャップを大きく短縮できる[5]。
Markdown: [Docker - Wikipedia](http://qiita.com "Docker - Wikipedia")

docker公式サイト About Us : [About Docker - Management &amp; History | Docker](https://www.docker.com/company "About Docker - Management &amp; History | Docker")

僕の理解力では分からなかったので、他の記事を読んで何となく僕のイメージをまとめてみました。
読んだ記事は**参考にしたサイト**から確認してください。

##dockerのイメージ
![docker-1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1077136/61073992-a539-954d-8af9-e6733573ccb8.jpeg)
たくさん用語が出てきたので順番に説明していきたいと思います。
感覚的に表現しているところも多々あるのでご了承ください。

###dockerレジストリ (docker registry)
dockerのデータを保存するクラウドストレージのこと
公式で用意されてるものが**docker Hub**になります。

###dockerイメージ (docker image)
dockerの基本設定が書かれたファイル
イメージとしては、土台や家を建てるときの基礎のような感じです。
あとは、塗り絵の、色を塗る前の下絵も近いと思います。
dockerコンテナまで読んでみて想像しやすい方で考えていきましょう。

OSやソフトウェア、プログラミングコードのバージョンなど開発環境の基本設定をdockerイメージに書き、それごとデータのやり取りをすることで、サーバーや開発メンバーごとに違うOSやバージョン違いを無くしてくれます。

###dockerコンテナ (docker container)
開発内容、成果物を入れる箱
dockerイメージを基礎だとすると建物、
dockerイメージを下絵だとすると色塗り部分。

コマンド操作で制作過程も保存できるので、保存した場面であれば前の状態にリセットすることもできるらしい。便利！！
~~まだ使ったことないからよく分からんけど…~~

##まとめ
dockerは制作データだけではなく、制作環境の基礎設定まで共有することができる。
次回はdockerをインストールしてみたいと思います。

##参考にしたサイト

[入門 Docker](https://y-ohgi.com/introduction-docker/ "入門 Docker")
[Dockerの概要まとめ  |  ITエンジニアの定時退社Tips](https://www.early2home.com/blog/it/docker/post-1994.html "Dockerの概要まとめ  |  ITエンジニアの定時退社Tips")
[いまさらだけどDockerに入門したので分かりやすくまとめてみた](https://qiita.com/gold-kou/items/44860fbda1a34a001fc1 "いまさらだけどDockerに入門したので分かりやすくまとめてみた")

##初心者がdockerでphpの開発環境を整える 一覧
[初心者がdockerでphpの開発環境を整える#1 [dockerとは？]](https://qiita.com/fulu/items/30728918802b6a96520a "dockerとは？")
[初心者がdockerでphpの開発環境を整える#2 [dockerをインストールする]](https://qiita.com/fulu/items/b2c5504278115f440c59 "dockerをインストールする")
[初心者がdockerでphpの開発環境を整える#3 [docker imageの準備方法]](https://qiita.com/fulu/items/b85fa89afc002837510f "docker imageの準備方法")
[初心者がdockerでphpの開発環境を整える#4 [レンタルサーバーでdockerが使えない？]](https://qiita.com/fulu/items/ab599c73354151e8e80a "レンタルサーバーでdockerが使えない？")
三点リーダー症候群なので。。。ビックリマークに変換します！！！

#はじめに…
こんにちは、[ぱそきいろ](https://www.takacpu55.xyz/)と申します…
[三点リーダー症候群](https://twitter.com/i/events/1355710824833142787)なるものがあるみたいですね・・・
私も無意識で使ってしまいがちです。。。
あまり印象がよろしくないと思いますので、「…」や「・・・」などいわゆる「てんてん」が出てきたら「！」に変換するプログラムを書きます、、、

#素直に正規表現で書いてみる
まずは真っ先に思い浮かんだ方法です。
「。」などの普通の「てん」が2個以上並ぶ、もしくは「…」などの複数の「てん」があると「！」に置換します。

```python:main.py
import re
while(True):
    s=input()
    print(">"+re.sub("([,.・•。、，．]{2})|[…‥]","！",s))
```

実行してみます。

```python
python ./main.py

#普通の文章は変換しない
こんにちは。
>こんにちは。

#「…」は「！」に変換
こんにちは…
>こんにちは！

#「。。」は「！」に変換
こんにちは。。
>こんにちは！

#「。。。」でも「！」に変換
こんにちは。。。
>こんにちは！

#「。」がいくつ増えても「！」の数は一つだけ
こんにちは。。。。
>こんにちは！
```

これでも問題はないですが、「。」の数が増えると「！」のマークも増えるようにします。

#ちょっと改良
「。」などの一文字の「てん」と「…」などの「てんてん」の場合を分けて考えます。

```python:main.py
import re

while(True):
    s=input()
    #「。」が二つ以上続く場合
    if re.search("[,.・•。、，．]{2,}",s):
        print(">"+re.sub("[,.・•。、，．]","！",s))
        continue
    #「…」などの場合
    elif re.search("[…‥]",s):
        print(">"+re.sub("[…‥]","！",s))
        continue
    #それ以外の場合はそのまま表示
    else:
        print(">"+s)
```

実行してみます。

```python
python ./main.py

#普通の文章は変換しない

マジむり。
>マジむり。

#「…」は問題なく変換できる
マジむり…
>マジむり！

#「・」が二つ続くと「！」も二つ続く
マジむり・・
>マジむり！！

#「・」が三つ続くと「！」も三つ続く
マジむり・・・
>マジむり！！！

#「・」がたくさん続くと「！」もたくさん続く
マジむり・・・・・・・・・・・
>マジむり！！！！！！！！！！！
```

#まとめ！！！
語尾が「。。。」よりも「！！！」の方が勢いがあって印象がいいですね！！！
LGTMをいただけると嬉しいです！！！
普段は[こちらのブログ](https://www.takacpu55.xyz/)に投稿しているのでよかったらみていただけると嬉しいです！！！
以上、ありがとうございました！！！
Shopify Japanに怒られた話 (Shopifyパートナーの規約違反について)

#はじめに
今回は、Shopify Japan様に怒られた話についてまとめていきたいと思います。

今回は題名に「怒られた」と付けましたが、キャッチーな題名にしているだけで、実際に弊社が重大な規約違反を犯してしまったわけではありません。

重大な規約違反を犯してしまった訳ではないんですが、Shopifyパートナーが犯しがちなミスを弊社もしてしまっていたため、Shopify Japan様から直々にご指導頂きました。

ということで、自己への戒めと周囲への注意喚起という意味で**「Shopifyパートナーが犯しがちな規約違反」**についてまとめていきたいと思います。

ちなみにこちらがShopifyパートナーの利用規約になります。全て英語です。

[Shopifyパートナー利用規約](https://www.shopify.com/partners/terms)

# Shopify アプリのご紹介

Shopify アプリである、「商品ページ発売予告アプリ | リテリア Coming Soon」は、商品ページを買えない状態のまま、発売日時の予告をすることができるアプリです。Shopify で Coming Soon 機能を実現することができます。

https://coming-soon.reterior.jp/

https://apps.shopify.com/shopify-application-314?locale=ja&surface_detail=%E3%83%AA%E3%83%86%E3%83%AA%E3%82%A2&surface_inter_position=1&surface_intra_position=1&surface_type=search

#①Shopify関連の検索ワードに広告を出すべからず
これは結構衝撃的なんですけど、ShopifyパートナーはShopify関連のキーワードに自社の広告を出すことが禁じられています。

ちなみに、この利用規約はあまり知られていないため、色んな会社が破りまくっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/411258/3ecd296a-9ab3-5763-bc96-126eff0e73f2.png)

当たり前のようにShopify関連のキーワードにバンバン広告が出ていますが、これをShopifyパートナーがしていた場合は規約違反になります。

現在は数が多くて対処が追いついていないとのことでしたが、一定の閾値を超えるとShopify Japan様から直々にご指導が入るようです。


#②ドメインにShopifyという単語を入れるべからず
こちらも多くのShopifyパートナーが破りまくっている利用規約になります。

**Shopifyというキーワードに広告を仕込む + ドメイン名にShopifyを含める**というダブル役満の企業も多くあります。

この利用規約を破っているShopifyパートナーの企業さんも非常に多いです。

#③サービス名にShopifyを入れるべからず
こちらの利用規約を破っている企業さんは少ないです。



#④Shopify認定・Shopify公式パートナーなんてものは存在しない！
結論から申しますと、**Shopifyが認定しているパートナー＝Shopifyエキスパート**です。

多くのShopify関連のLPで**Shopify認定パートナー**や**Shopify公式パートナー**という単語が見られますが、それを書いて良いのは**Shopifyエキスパート**の方だけらしいです。

これは多くのShopifyパートナーが犯しがちなミスになります。

#⑤開発サイトのパスワード公開は駄目！
まず、前提としてShopifyパートナーは**開発ストア**という特殊なストアを持つことができます。

こちらは、クライアント様に納品する前のストアで、パスワードを外すことができません。

このストアのリンクをLPなどに貼り付けて、**パスワードは「~」です。どのようなストアができるのかをお確かめ下さい**とするのはNGです。

つまり、開発ストアを多くの人が見れる状態にする、というのが禁止されているみたいです。

#⑥Shopifyテーマを公式以外で販売するべからず
Shopifyテーマは、Shopify公式以外で販売することが禁止されています。

こちらはShopifyパートナーの利用規約というよりか、Shopifyの利用規約になります。




#終わりに
今回で、Shopifyパートナーの利用規約に関する注意喚起の記事は終わりです。

こういう規約は、ついつい犯しがちになってしまいます。

自分も、これからはしっかりと勉強をして、規約を守っていきたいと思います。

皆様のお役に少しでも立てれば幸いです。

ここまでお付き合い頂き、ありがとうございました。
【備忘録】VS CodeでC言語のオートフォーマッタを(私にとっていい感じに)カスタマイズ

# 結論から先に
settings.jsonに以下を追加する。

```json
"C_Cpp.clang_format_style": "{BasedOnStyle: Google, IndentWidth: 4, BreakBeforeBraces: Stroustrup, TabWidth: 4, UseTab: Always}",
"editor.formatOnSave": true,
```

# 背景
最近C言語での開発をするチームのお手伝いを始めました。(~~人がどんどんいなくなるので~~)

普段Pythonを書いているときには、[black](https://pypi.org/project/black/)によるオートフォーマッタで楽をさせてもらっているので、C言語でもオートフォーマッタの設定を行いました。

# 現状のチームのコーディングスタイル
チームでは以下のようなコーディングスタイルを採用しており、これに合うようなフォーマッタの設定を行いました。

+ 関数の後の波かっこ{}は改行する

```c
void Hoge(void)
{
    # 処理
}

```
+ if ~ else文はブロックごとに改行

```c
if (a > 0) {
    # 処理
}
else if(a < 0) {
    # 処理
}
else {
    # 処理
}
```
+ インデントはタブを使う
+ タブのサイズは4

# フォーマッタの設定
## BasedOnStyle: Google
基本スタイルの設定。Googleにすると、#includeの部分とかいい感じに整えてくれるので好きです。
特にこだわりが無ければ、

```json
"C_Cpp.clang_format_style": "{BasedOnStyle: Google, IndentWidth: 4}",
```
とするだけで、十分見た目きれいになります。

## IndentWidth: 4
インデントの幅を4に指定

## BreakBeforeBraces: Stroustrup
波かっこの改行の設定。Stroustrupにすると、関数の後の改行、if~else文のブロックごとの改行などをやってくれます。

##TabWidth: 4
タブのサイズを4に指定

##UseTab: Always
タブを使うように指定

# 参考URL
+ https://yasuharu519.hatenablog.com/entry/2015/12/13/210825
シミュレータで動画を撮影する方法(iOS&Android)

#iPhone
simulatorは起動しておく
cd 作業ディレクトリ
xcrun simctl io booted recordVideo test.mov実行
simulatorを操作
ctrl + cで終了

#Android
simulatorは起動しておく
cd 作業ディレクトリ
adb shell screenrecord /sdcard/test.mp4
adb pull /sdcard/test.mp4
【AtCorder Tips】any()でif文を省略する

Atcorderの問題を題材に、
今後、活用できそうなコードの備忘録集

例えば、標準入力N行の値X,Yが、
それぞれ10未満、10より大きいことを確認する場合。

any()と、条件式を返すchcek()関数を組み合わると、
if文を1回省略できる。

```
■　if文を1回使用（参考例）
def check():
    X, Y = map(int, input().split())
    return X < 10 and 10 > D

if any(check() for i in range(N)):
    print("Yes")
else:
    print("No")
```

```
■　if文を2回使用（良くない例）
for _ in range(N):
  x,y = map(int,input().split(' '))
  
  if X < 10 and Y > 10:
    flag = 1
    break
    
  else:
    flag = 0
 
if flag == 1:
  print('Yes')
else:
  print('No')
```
gin-jwt の使い方

参考ページ
[JWT Middleware for Gin Framework](https://github.com/appleboy/gin-jwt/blob/master/README.md)

## プログラムをクローンします。

```bash
git clone https://github.com/appleboy/gin-jwt
```

## サンプルのサーバーの起動

```bash
cd gin-jwt/
go run _example/basic/server.go
```

## トークンの取得

```bash:get_token.sh
http :8000/login username=admin password=admin  > tmp001.json
#
jq . tmp001.json
```

実行結果

```text
$ ./get_token.sh 
{
  "code": 200,
  "expire": "2023-05-23T16:52:23+09:00",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2ODQ4MjgzNDMsImlkIjoiYWRtaW4iLCJvcmlnX2lhdCI6MTY4NDgyNDc0M30.jGHhFUXtDQAlbRtPxN_suH0DLDFYQb1pr60l_pLyabc"
}

```

トークンの更新

```bash:go_refresh.sh
token=`jq .token tmp001.json | sed 's/"//g'`
#
http :8000/auth/refresh_token "Authorization:Bearer ${token}"
```

## ログインのテスト

```bash:go_login.sh
token=`jq .token tmp001.json | sed 's/"//g'`
#
http :8000/auth/hello "Authorization:Bearer ${token}"
```

実行結果

```text
$ ./go_login.sh 
HTTP/1.1 200 OK
Content-Length: 59
Content-Type: application/json; charset=utf-8
Date: Tue, 23 May 2023 06:58:52 GMT

{
    "text": "Hello World.",
    "userID": "admin",
    "userName": "admin"
}
```

## サーバーのプログラムを改造してメッセージを変更

```go:_example/basic/server.go
（省略）
func helloHandler(c *gin.Context) {
        claims := jwt.ExtractClaims(c)
        user, _ := c.Get(identityKey)
        c.JSON(200, gin.H{
                "userID":   claims[identityKey],
                "userName": user.(*User).UserName,
//              "text":     "Hello World.",
                "text":     "Hello World. こんにちは",
        })
}
（省略）
```

サーバーを再起動します。

```bash
go run _example/basic/server.go
```

再びログインをしてみます。

実行結果

```text
{
  "text": "Hello World. こんにちは",
  "userID": "admin",
  "userName": "admin"
}
```

## 確認したバージョン

```text
$ go version
go version go1.20.3 linux/amd64
```
manimの作法 その41

#概要

manimの作法、調べてみた。
BackgroundRectangle使ってみた。

#サンプルコード


```
from manimlib.imports import *

class test(Scene):
	def construct(self):
		src = r'''菅義偉首相は、自助を中心に据えた社会作りを
提唱するが、人々の生活は、自助では成り立たない程、
追い詰められている。
貧富の差が拡大する今は、公助の役割こそが
求められている時代である。
'''
		text = Text(src, font = 'IPAGothic')
		self.add_sound('20210106112815.wav')
		bg = BackgroundRectangle(text, color = WHITE, fill_opacity = 0.85, buff = 0.25)
		self.play(FadeInFromDown(bg), *[FadeInFromDown(each) for each in text], run_time = 3, lag_ratio = 0.5)
		self.wait()



```

#生成した動画

https://www.youtube.com/watch?v=d7YDQs46kgA

以上。
Rspecセットアップ

##初めに
Rspecの導入方法についての記事です。

##準備

```アプリ.Gemfile
group :development, :test do
gem 'rspec-rails', '~> 3.6.0'
end
```

gemファイルを開き、development,test環境それぞれにrspecをインストールします。
(bundle install)

その後

```
rails g rspec:install
```
を実行

```アプリ.rspec
--require spec_helper
--format documentation
```

.rspecファイルに記述することで読みやすくなる。
ちなみに
--format documentationがある場合、

```
 returns notes that match the serch term
    when no match is found
      returns an empty collection

Project
  does not allow duplicate project names per user
  allows two users to share a project name

User
  is valid with a first name, last name, email, and password
  is invalid without a first name
  is invalid without a last name
  is invalid without an email address
  is invalid with a duplicate email address
  returns a user's full name as a string

Finished in 0.48339 seconds (files took 0.42866 seconds to load)
11 examples, 0 failures
```

まるでnoteのように表示されるので結果の確認がしやすい。
ただ、好みの問題なので記述するかしないかは、その時次第。

```アプリ.Gemfile
group :development do
gem 'spring-commands-rspec'
end
```

bundle installする。
ここでrspecコマンドを実行し、コマンドが機能してるか確認

```config/application.rb
confing generators do |g|

g.test_framework :rspec,
fixtures: false、
view_specs: false,
helper_specs: false,
routing_specs: false

end
```

を記述し、ジェネレータが不要なspecファイルを作成するのを防ぐ。
用途に応じて、routing, view, controller等のファイルは :false(or :true)
にしておくと良い。

trueにした場合、
spec/追加したファイル(controllerなど)
に
spec/追加したファイル(controllerなど)/controller.rb
を追加したいなら
spec/追加したファイル(controllerなど)/controller_spec.rb
を追加する。

この時点でtestディレクトリを削除しても良い。
超初心者のためのスクラム用語集

#スクラムとは

よく言うアジャイル開発の中の開発手法の一つ（アジャイルはマインドセットだから開発手法ではないよという話をよく聞くが、自分でもあまり理解が追いついてないので、「アジャイル」の中にいくつかの開発手法があるよ！という感じで認識している）

複雑な問題を扱うのに適しているおり、5つのイベント(会議)3つのロール(役割)3つの作成物をルールのセットとして構成されている

今回は超初心者(著者も)のためにスクラムの構成と大まかな説明を記載する
#5つのイベント(会議) 

スプリント・・・
繰り返し開発を行う固定期間の区切りの様なもの 

スプリントプランニング・・・
スプリントのゴールを決め、そのスプリント内で作業するプロダクトバックログを選択する　作業内容を分解する 

デイリースクラム・・・
昨日やったこと　今日やること　今おきている障害を15分で確認する 

スプリントレビュー・・・
スプリントで作成した成果物を関係者にデモする 

スプリントレトロスペクティブ・・・
仕事をうまく進めることができるかどうかについて考え、今後のアクションプランを作成する 

#3つのロール(役割分担) 
プロダクトオーナー・・・
プロダクトに責任を持ち、プロダクトバックログの最終決定権を持つ 

開発チーム・・・
プロダクトの開発を行う 

スクラムマスター・・・
スクラムがうまくいくように支援する 

#3つの作成物 

プロダクトバックログ・・・
プロダクトの要求事項をリスト化し順番を決めたもの 

スプリントバックログ・・・
プロダクトバックログを細かい作業に分割したもの 

インクリメント・・・
スプリントで作成したものを評価可能な形にまとめたもの 


#まとめ
正直自分もスクラムをしっかりとした場で経験したことがないため、突然スクラムのプロジェクトに参画することになったときに困らないようまずは最低限の用語から。
この記事はスクラムブートキャンプという本を参考に記載しています。初心者にはとても良い教材だったので是非読んでみてください。
仮想DOMとは

<h1>はじめに</h1>

この概念を知らずにReactは触れない。
ということで仮想DOM。
＊ここではDOMのことを仮想DOMと対比する形で**リアルDOM**と書いておきます。

<h1>仮想DOMとは</h1>

仮想DOMはリアルDOMの分身のようなイメージで、ReactやVueなどで使われる。

では、なぜ仮想DOMが使われるのか。

まず、リアルDOMをそのまま操作して変更を加えようとすると、<br/>その都度HTML要素全体の再構築が発生して労力がかかってしまうという前提がある。

できれば**変更部分だけ**を再構築することで、最小限の労力に押さえてスムーズな状態遷移を実現したい。
（例えば、小説家もたった一文を書き直すために、文章を最初から全部書き直していたら負荷が凄いことになる米。）

ということで、変更部分だけを再構築したいなら、まず変更部分がどこかを検出しないといけない。

そして変更部分を検出するためには、変更前と変更後で比較してどこに差があるか検出する必要がある。（＝差分検出）

この差分検出の比較対象として仮想DOMが登場という流れ。
（二つの仮想DOMを比較することで差分を検出する。）


<h1>まとめ</h1>
仮想DOMを使うことによって、変更前と変更後で差分を検出することができて、<br/>結果差分だけをリアルDOMに反映して再構築することが可能になる。
とても勉強になりました。
初めてのRaspberry Pi Pico ⑥ Resetボタンを追加 Ver.2

　ラズパイのデスクトップにPico（PRI-RP2）をマウントするために、BOOTSELボタンを押したままUSBケーブルをつなげました（抜き差し）。
　miniコネクタ側だとコネクタがつぶれてしまったことがあるので、ラズパイ側で抜き差ししていました。両手を使ってしまい、面倒でした。
###Resetスイッチを取り付ける

　RUN端子とGND間にタクト・スイッチをはんだ付けします。Resetボタンと呼ばれるようです。はんだ付けした反対側は接着剤で浮かないように留めます。
<img width="350" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/59213945-2f56-f047-db94-64426b17cce9.png">

　Resetボタンを押したまま、BOOTSELボタンを押し、Resetボタンを放してから、BOOTSELボタンを放します。これでマウントができます。片手ではできませんが、ずいぶん楽になりました。
![pico201.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/cd2db977-dbf7-0b20-ca3f-32e163f79f59.png)

解説ページ　[How to add a reset button to your Raspberry Pi Pico](https://www.raspberrypi.org/blog/how-to-add-a-reset-button-to-your-raspberry-pi-pico/)

PS.　BOOTSELボタン側に取り付けてみた。
<img width="350" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/3ab5639a-1782-ec89-9726-85795f42bdd6.png">
npm installしようと思ったらgyp: No Xcode or CLT version detected!でダメなのを解決する件

タイトルの通りnpm installしようと思ったら何者かに阻まれてしまった際に参考になった記事とその時のターミナルのコードを記録しておこうと思い、記事にしました。


エラー時
![スクリーンショット 2021-02-03 3.38.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566867/474c1866-45cf-bca1-69b5-9af88e2e0695.png)

この画像の
gyp: No Xcode or CLT version detected!
のせいで、うまいことパッケージがダウンロードされていないことに気づき、いろんな記事を読んでいたところ、
[「gyp: No Xcode or CLT version detected!」を修正する](https://blog1.mammb.com/entry/2020/06/18/090000 "「gyp: No Xcode or CLT version detected!」を修正する")
の通りにやったらうまいことインストールまで行えました。


完了後
![スクリーンショット 2021-02-03 3.51.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566867/d30c0927-b5b7-3910-608d-8b722bca7d45.png)


誰かの参考になっていれば幸いです。
あれ？　XAMPP入らねぇぞ。と思ったら

<h2>ダウンロードされたファイルの端子を確認する

　端子が「.bz2」。これは圧縮ファイルなので解凍する必要がある。ターミナルを使えば、解凍できるらしいが、実行してみると「Xampp.app」の文字がでなかったので、素直に諦める。

　Source forgeというXAMPPがダウンロードできるページがあるので、そこに飛ぶ。

https://sourceforge.net/projects/xampp/files/

　飛ぶと「ダウンロードする環境」→「バージョン」→「.dmg」のファイルが表示されるページに出る。VM用か、インストーラ用かは書いてあるのでわかりやすい。
【Node.js ☓ Redisをasync/awaitで行う】

node-redisとioredisあたりがよく使われているみたい。

redisには型ごとに様々なコマンドがある。
https://www.tutorialspoint.com/redis/index.htm

そのなかでも、結果によって、0 or 1 を返してくれる便利なものもある。

この0 or 1の結果を利用したいときに**ioredisを使ったほうがasync/awaitを使える**のでネストが深くなったりしなくて済む。

以下では、setnxコマンド（keyがすでに存在していれば→0, なければセットして→1を返す）を例に上げて比べてみる。

**node-redisで返り値を取得する方法**

```jsx
const redis = require('redis');
const client = redis.createClient();

client.setnx(['name', name], (err, result) => {
    console.log(result)
		// result = 0 or 1
}
```

**ioredisで返り値を取得する方法**

```jsx
const ioredis = require('ioredis')
const ioclient = ioredis.createClient()
..省略 async () ..
try{
    const result = await ioclient.setnx(['name', name])
    console.log(result)
		// result = 0 or 1
}catch(err){
    console.log(err)
}
..省略
```

**ioredisだとPromiseインスタンスでredisコマンドの結果を返してくれる**のでasync/awaitでネストが深くなるのを阻止できる。

試しにawaitを外してみると、`Promise { <pending> }`が返ってくる。
【Ruby on Rails】gem "jsonapi-serializer"の基本的な使い方

RailsAPIモードで開発をする際に、JSONを整形するためにjsonapi-serializerというものを使用している。
このgemに関してgoogle検索してもほとんど日本語の記事が出てこないため、基本のことではあるがここにメモしておく。

```users_controller.rb
module Api::V1::Admin
  class CompaniesController < ApplicationController

    def index
      companies = User.all
      json_string = CompanySerializer.new(users)
      render json: json_string
    end
  end
end
``` 
コントローラを作成したら、serializers/user_serializer.rbを作成する。

```user_serializer.rb
class CompanySerializer
  include JSONAPI::Serializer

  has_many :likes
  attribute :name, :email, :address
end
```

このように記述すると思いどおりのJSONを受け取ることができる。

ちなみに、user_serializer.rbにある``include JSONAPI::Serializer``これは記述しなくてもシリアライザーとして機能するみたい。


jsonapi-serializerのGitHubは[こちら](https://github.com/jsonapi-serializer/jsonapi-serializer)
[TimeseriesGenerator] LSTM Many to Manyに対応した、時系列データ加工

過去N点のデータを入力とし、未来N点のデータを予測（出力）とする場合のデータを生成します。（※過去と未来のステップ数が等しい場合のみ有効です。）


# 関数
```py

import pandas as pd
import numpy as np
from keras.preprocessing.sequence import TimeseriesGenerator


def create_timeseries(data, lookback):
    generator = TimeseriesGenerator(data, data, length=lookback, batch_size=1)

    X_li, y_li = list(), list()
    for i in range(len(generator))[:-lookback+1]:  # lookback分、x2がnullになるため除く
      x, y = generator[i]
      x2, y2 = generator[i + lookback]
      X_li.append(x)
      y_li.append(x2)

    re_X = np.concatenate(X_li)
    re_y = np.concatenate(y_li)
    return re_X, re_y
```

```
# 実行
data = np.arange(100).reshape(-1, 4)
print(data.shape)
print(data[:5])

X, y = create_timeseries(data=data, lookback=3)

print(X[0])
print(y[0])
print("=====")
print(X[-1])
print(y[-1])

# 出力
(25, 4)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]]
0 [[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]] shape=(1, 3, 4)
=> [[[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]] shape=(1, 3, 4)
=====================
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
[[12 13 14 15]
 [16 17 18 19]
 [20 21 22 23]]
=====
[[72 73 74 75]
 [76 77 78 79]
 [80 81 82 83]]
[[84 85 86 87]
 [88 89 90 91]
 [92 93 94 95]]

```

# 解説

## 準備
```py
import pandas as pd
import numpy as np
from keras.preprocessing.sequence import TimeseriesGenerator

# step数は3に設定
lookback = 3
```

## 使うデータ

```py
data = np.arange(100).reshape(-1, 4)
print(data.shape)
data[:5]
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64293/c78b2bf0-9fd1-37a3-ee6e-d66122828949.png)

## Generator生成
```py
generator = TimeseriesGenerator(data, data, length=lookback, batch_size=1)
```
## 生成されるデータ
```
# lookup分のx、次の値がy
for x, y in generator:
  print('%s => %s' % (x, y))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64293/576b4787-5eed-d354-6fc0-3a9d43922d64.png)

## 生成されるデータからLSTM用に抽出
```py
X_li, y_li = list(), list()
for i in range(len(generator))[:-lookback+1]:  # lookback分、x2がnullになるため除く
  x, y = generator[i]
  x2, y2 = generator[i + lookback]
  X_li.append(x)
  y_li.append(x2)
  print(i, x, "shape={}".format(x.shape))
  print("=>", x2, "shape={}".format(x2.shape))
  print("=====================")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64293/c2223ec0-25a8-2937-450e-cd0673cc2a58.png)

## 抽出したデータを結合

```py
re_X = np.concatenate(X_li)
re_y = np.concatenate(y_li)

# re_X.shape, re_y.shape => (19, 3, 4), (19, 3, 4)
```

## 完成したデータ

```py

for x, y in zip(re_X, re_y):
  print(x)
  print("=>", x2)
  print("=====================")

```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64293/932503f2-ab01-e91a-102d-46d50fd8d54c.png)


# 参考

* [シーケンスの前処理 - Keras Documentation](https://keras.io/ja/preprocessing/sequence/)
* [TimeseriesGeneratorのサンプル (keras.preprocessing.sequence) - Qiita](https://qiita.com/ta1nakamura/items/11b53669ce48219d6475)
ER図作成（どうやってエンティティを絞り出すか？）

#概要
アプリケーションに必要なER図を起こしました。
現状は、非常にシンプルな構造となっています。
今回制作するアプリケーションはマッチングアプリなので、「フォロー/アンフォロー」や「いいね機能」が追加予定です。その都度最新のER図を掲載していきます。
![スクリーンショット 2021-02-02 21.53.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/792954/e604336d-5a60-23be-b000-095f1456844d.png)

#どんなアプリケーションか？
簡単にいうと生産者と料理人のマッチングサービスです。
以下の記事に制作に至った経緯が書いてあるので気になったかはご覧ください！
https://pontyblog.com/364/

#エンティティの絞り方
エンティティを絞り込む場合は、データが登録される場面に着目して絞っていきます。

第三正規化をおこない、情報するエンティティを分離する必要があります。正規化や第一、第二正規化では、「重複するデータ」が存在したり、本来分離されるべきエンティティが混同してしまうからです。


例えばSNSのように、1つの投稿に対して多数のコメントがされる場合を正規化で考えてみます。

####postsテーブル【正規化】
|user_name|tweet_text|comment_text|
|:--:|:--:|:--:|
|masuda|虫が少ないキャンプ場|行ってみたい！/どこのキャンプ場？/景色も良さそう！|
|tanita|今日は犬と戯れています〜|ハスキー飼いたい！|

一見問題なさそうですが、commentのところを見ると、コメントが複数存在しています。これは、アプリケーションのパフォーマンスを落とす原因になります。そして特定の情報を取得する際、情報整理できていないので、効率の悪いものになってしまいます。

#第三正規化をおこなう
第三正規化は、情報の混在するエンティティを分離することです。実際にやってみます。

####usersテーブル
|id|name|
|:--:|:--:|
|1|masuda|
|2|tanita|

####tweetsテーブル
|id|tweet_text|user_id|
|:--:|:--:|:--:|
|1|虫が少ないキャンプ場|1|
|2|今日は犬と戯れています〜|2|

####commentsテーブル
|id|comment_text|tweet_id|
|:--:|:--:|:--:|
|1|行ってみたい！|1|
|1|どこのキャンプ場？|1|
|1|景色も良さそう！|1|
|2|ハスキー飼いたい！|2|

このように第三正規化をおこなうことで、データの重複を回避することができます。同時にエンティティも分離されているのでデータ管理も容易になります。
【SQL】テーブルの縦横を切り替えてサブクエリとして使用する実例

仕事で使用したので備忘録として記載しておきます。

今回は例としてイベントチケット管理システムを開発するという前提で考えていきたいと思います。
イベントは複数開催されていて、それぞれのイベントは子供料金・大人料金が別々のチケットで管理されています。ただ、イベントによっては大人しか参加できないという規制があるとします。イベントAは子供と大人のチケットが存在し、イベントBでは大人のみという形になります。

そして、最終的に実行したい処理は「子供と大人の両方が参加可能なイベントを取得する」とします。

# テーブルの作成
以下のように4つのテーブルとしました。
・イベントテーブル
・チケットテーブル
・料金テーブル
・料金項目テーブル

```SQL:SQL
-- イベントテーブル --
CREATE TABLE events (
	id MEDIUMINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
	name VARCHAR(255) NOT NULL,
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB;

-- チケットテーブル --
CREATE TABLE tickets (
	id MEDIUMINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
	event_id MEDIUMINT NOT NULL,
	event_start DATETIME NOT NULL,
	event_end DATETIME NOT NULL,
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB;

-- 料金テーブル --
CREATE TABLE prices (
	id MEDIUMINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
	ticket_id MEDIUMINT NOT NULL,
	item_id MEDIUMINT NOT NULL,
	price INT NOT NULL,
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB;

-- 料金項目テーブル --
CREATE TABLE items (
	id MEDIUMINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
	item_name VARCHAR(255) NOT NULL,
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB;
```

データはそれぞれ以下になります。

```SQL:SQL
-- イベントテーブル --
INSERT INTO events(name) VALUES('サンプルイベント01')
,('サンプルイベント02')
,('サンプルイベント03');

-- チケットテーブル --
INSERT INTO tickets(event_id, event_start, event_end) VALUES(1, '2021-02-01 10:00:00', '2021-02-01 20:00:00')
,(2, '2021-02-01 10:00:00', '2021-02-01 20:00:00')
,(3, '2021-02-01 10:00:00', '2021-02-01 20:00:00');

-- 料金テーブル --
INSERT INTO prices(ticket_id, item_id, price) VALUES(1, 1, '1000')
,(1, 2, '600')
,(2, 1, '1000')
,(2, 2, '400')
,(3, 1, '1000');

-- 料金項目テーブルル --
INSERT INTO items(item_name) VALUES('大人')
,('子ども');
```

# データを取得する
そして、「子供と大人の両方が参加可能なイベントを取得する」ためのSQLが以下です。

```SQL:SQL
SELECT
	e.id,
	e.name
FROM events AS e
JOIN tickets AS t ON t.event_id = e.id
WHERE t.id IN (
	SELECT
		price.ticket_id
	FROM (
		SELECT
			tickets.id AS ticket_id,
			MAX(CASE item_id WHEN 1 THEN 1 END) AS adult,
			MAX(CASE item_id WHEN 2 THEN 1 END) AS child
		FROM tickets
		JOIN prices AS p ON p.ticket_id = tickets.id
		GROUP BY tickets.id
	) AS price
	WHERE price.adult IS NOT NULL
	AND price.child IS NOT NULL
)
```
サブクエリの中のサブクエリだけを実行すると以下のようなテーブルが取得できます。

| ticket_id | adult | child |
|:---------:|:-----:|:-----:|
| 1         |1      |      1|
| 2         |1      |      1|
| 3         |1      |   null|

料金テーブルには大人料金・子供料金でそれぞれ1レコードづつ入っているとして、大人しか参加できないイベントは大人料金のチケットしか存在しません。
そのため、サブクエリの中のサブクエリのSQL（テーブルの縦横を切り替えたもの）を叩くことでnullになるところが料金レコードがないものとなります。今回のデータではチケットIDが3のものは子供チケットはないことになります。

そのあとはサブクエリとしてadultとchildがnullではないチケットIDを取り出して、上層のwhere inの条件として渡してあげれば「子供と大人の両方が参加可能なイベントを取得する」ことができました。
【JS】配列の中のオブジェクトの値渡し（正確には値渡しではない）

# はじめに

**大前提として、そもそもJSには「値渡し」「参照渡し」がない、というのはひとまず忘れる**
→よくわからない人は、以下の記事が参考になるかも（本文もコメントも参考になる）
→[JavaScriptに参照渡し/値渡しなど存在しない - Qiita](https://qiita.com/yuta0801/items/f8690a6e129c594de5fb)

この記事では便宜上「値渡し」「参照渡し」という単語を使っているが、ご留意いただきたい

# よくあるオブジェクトをコピーできない問題

## 問題点

そのままだと参照渡しになる
（→正確には、オブジェクトを渡すと呼び出し元のオブジェクトが変更される）

```js
const sourceObj = {
  name: "Taro",
  age: 12,
}

const copyObj = sourceObj

copyObj.name = "Jiro"

console.log(sourceObj.name)
// => "Jiro", 元のオブジェクトの値も変わる

console.log(copyObj.name)
// => "Jiro"
```

## 解決策

よくあるオブジェクトを値渡しする方法
（→正確には、オブジェクトをコピーする方法）

```js
const sourceObj = {
  name: "Taro",
  age: 12,
}

const copyObj = Object.assign({}, sourceObj)

copyObj.name = "Jiro"

console.log(sourceObj.name)
// => "Taro", 元のオブジェクトには影響なし

console.log(copyObj.name)
// => "Jiro"
```

``Object.assign({}, sourceObj)``
の部分は、
``{...sourceObj}``
でもOK

# 配列の中のオブジェクトをコピーできない問題

## 問題点

配列の中のオブジェクトという構造を``Object.assign()``でコピーすると、浅いコピー(オブジェクトへの参照のみをコピーし、オブジェクトそのものはコピーしない)になる
※``[...sourceObjs[0]]``でも変わらず

```js
const sourceObjs = [
  {
    name: "Taro",
    age: 12,
  },
  {
    name: "Goro",
    age: 30,
  },
]

const copyObj = Object.assign({}, sourceObjs[0])

copyObj.name = "Jiro"

console.log(sourceObj.name)
// => "Jiro", 元のオブジェクトの値も変わる

console.log(copyObj.name)
// => "Jiro"
```

## 解決策

一度、JSONにパースしてから再度オブジェクトに戻す

```js
const sourceObjs = [
  {
    name: "Taro",
    age: 12,
  },
  {
    name: "Goro",
    age: 30,
  },
]

const copyObj = JSON.parse(JSON.stringify(sourceObjs[0]))

copyObj.name = "Jiro"

console.log(sourceObj.name)
// => "Taro", 元のオブジェクトには影響なし

console.log(copyObj.name)
// => "Jiro"
```

# さいごに

JSONにパースしてから戻す方法がスマートだが、他の方法もあると思う
気が向いたら追記する

**念押しとして、JSには「値渡し」「参照渡し」という概念がそもそもないことに注意**

# 参考
- https://medium.com/@gamshan001/javascript-deep-copy-for-array-and-object-97e3d4bc401a
- https://medium.com/javascript-in-plain-english/how-to-deep-copy-objects-and-arrays-in-javascript-7c911359b089
[Flask]Flaskで別ページ遷移する方法

LoL APIでTFTのmatch idが取れなかった・・・誰か詳しい人いたらmatchidどうやって取るのか教えてください・・・

<h1>
本題
</h1>

自分はプログラミング初心者なので、他の初心者にもわかりやすく、というかあんまり意味を深く考えないでもできるように書きます。
まず、基本のページの表示
ここまでは出来ていると思いますapp.routeに/をいれとけばいいって覚えればいいと思います

```
@app.route('/')
def index():
  return render_template('index.html')
```
ここでいうrender templateはよくわからないですけど、approuteで指定したページに来た時に送るデータだと思ってください
なので、/のページに来たらindex.htmlを渡せっていうことらしいです　多分
変数とかも渡せます

なので、index.htmlを適当に用意しておきましょう

そして別のページを作ってそこに遷移したい！そんなときどうする！

```
@app.route('/another_page')
def another_page():
  return render_template('another_page.html')
```
こんな感じです
上から、/の後にページ名を付けてあげましょう今回は適当にanother_pageにしていますが何でもいいと思いますunkoとか

で、次に関数名を変えます
defの後ですね
ここも多分何でもいいと思います　indexなんて関数使ってないので、多分便宜上必要な設定なんでしょう()

最期に返すhtml名を変えましょう
これは用意したhtml名にしてくださいね

<h2>
html側は？
</h2>

aタグの設定方法だけわかればいいと思うので下記

```
<a href="/another_page">他のページへ遷移</a>
```
hrefに/ページ名を入れればOKです

そんな難しくなかったですが使用頻度は高いと思ったので自分メモにもなるなと思ってまとめました
参考：https://qiita.com/yut-nagase/items/b55e7ab3c1dcc6544e5f

ご参考になった方はLOLのapiでTFTのmatchidを探す方法を書いてください！！！！！！！励みになります！！！！！！！！
「初心者向け」GitHubにPushする方法

#はじめに
マックPCを利用し、SSH接続をしている前提です。
SSH接続について知りたい方は[こちら](https://qiita.com/shizuma/items/2b2f873a0034839e47ce)の記事がおすすめ

#方法
まず、**cd**を使いpushしたいファイルまで移動しましょう。
移動できたら以下のコードを実行していきます。

```
$git init
```
これでgitにpushするための型を作ります。

```
$git add -A
```
-AはALLのAです。フォルダ内にある全てのファイルを選択します。

```
$git commit -m "first commit"
```
"first commit"のところには任意のメッセージを記入してください。これをやることで周りの人にメッセージを伝えることができます。

```
$git remote add origin [sshのURL]
```
これでリモートリポジトリとローカルリポジトリを紐付けます。[]は不要です。

```
$git push -u origin master
```
これでpush完了
最後のmasterの部分は変わります。
**$git branch**を行い、下に出てきたものにしましょう。  

#エラーが出た場合
考えられる主な原因としては以下の２つ

・SSHキーがきちんと登録されていない。
・リモートリポジトリのURLが間違っている。

です。（もちろん他に原因があることもありますが）

##SSHキーがきちんと登録されていない
**Permission denied ~**ってエラーが出てきている場合はこの可能性が高いと思います。もう一度[こちら](https://qiita.com/shizuma/items/2b2f873a0034839e47ce)の記事を参考にしながら接続し直してみてください。

##リモートリポジトリのURLが間違っている
**Aready exit ~**ってエラーが出てきているときはこの可能性があります。そしたら以下を実行してみてください。

```
$git remote -v
origin	git@github.com:taigahara/shopping-bot.git (fetch)
origin	git@github.com:taigahara/shopping-bot.git (push)
```
そしたら上のように2行ぐらい長々とURLが出てくると思います。このURLがリモートリポジトリのURLと同じか確認してみましょう。
もし違えば、以下を実行して新しいURLに変更しましょう（[]は入りません）

```
git remote set-url origin [new url]
```

#最後に
これでエラーは解決したでしょうか？
皆さんの学習の参考になれば幸いです。
Firestoreのフィールドの表示バグ ~18桁以降数が0になる？ ~

## 結論
Firestoreの[公式ドキュメント](https://firebase.google.com/docs/firestore/manage-data/data-types)を見る限り、intは64bit, signedとなっているので、19桁まではいけるはずです。しかし、Firestoreのフィールドの表示が18桁以降は0になります。

ただし、これは表示上ではそうなっているだけでしっかりとfirestoreから取得した値自体はちゃんと返ってくることが確認できました。

## なぜそうなってしまうのか？（推測）
おそらくJSの仕業だろうと思います。JSは[ここに](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger)にあるように`2**53 - 1 `以下の整数値しか扱えません。なので、18桁以降になると0と表示されてしまうのでは？と思います。（確証ではないがおそらくそう）

## 解決策
どうしても気になる場合は文字列に変換する。
または、Googleさんが直してくれるのを待つ。
Docker初心者がWindowsでgitSSH接続からdockerコマンドを打てるまでの流れ

Stripeの決済の練習をしているところ、HTTPS通信でないとできない事例が発生したのでDockerの練習も含め備忘録として書いていこうかなと思います。

本記事はDockerにまだ触れたことがないという方やこれから触れようと思っているといった方々に向けた記事となっています。

いわゆる超初心者向けです。

また、GitBashを使っていくので使い方がわからない方やまだインストールしていないという方は、下記の記事を見てください！

[WindowsにGit Bashをインストールする](https://qiita.com/suke_masa/items/404f06309bb32ca6c9c5)

まぁつべこべ言わずにやっていきましょ！


#SShキーの作成#

コマンドプロンプトもしくはGitBashにて以下のコマンドを打ちます。

```
ssh-keygen -t rsa -b 4096 -C "github.com"

Enter passphrase (empty for no passphrase):  #この後使う好きなパスワードを入力
Enter same passphrase again:  # もう一度入力
```

すると、``C:\Users\usrname\.ssh``に``id_rsa``と``id_rsa.pub``というファイルが作られているかと思います。

人によっては``id_rsa_github``と``git_rsa_github.pub``になっている可能性あり。（多分Macの人はこうなる）

そして、Git Bashにて以下のコマンドを打ち、ちゃんとファイルができているかの確認を行います。

```
ls -l /c/Users/username/.ssh
-rw-r--r--  1 ts  staff  3326 11  8 21:52 id_rsa # 秘密鍵
-rw-r--r--  1 ts  staff   757 11  8 21:52 id_rsa_.pub # 公開鍵
```

#GitHubで公開鍵を設定#

こちらの[リンク](https://github.com/settings/profile)から設定画面に行き、「SSH and GPG keys」という項目をクリックします。

「New SSH key」をクリック。

「Title」はなんでもいいです。

そして、「Key」のところには先ほど作成した``id_rsa.pub``を**テキストエディタ（VScodeとかAtomとか）**で開き（メモ帳でも良い）、公開鍵をコピーして貼り付けます。


公開鍵の例（ssh-rsaも含めコピー）

```
ssh-rsa AAA~
```

#config設定#

GitBushで下記のコマンドを打ちます。

```
vim /Users/ts/.ssh/config
```

そして、以下のようにテキストを入力し閉じます。この時、警告が出るかと思うのですが、無視してOKです。

```
Host github
  HostName github.com
  IdentityFile /c/Users/username/.ssh/id_rsa_github #秘密鍵を指定
  User git
```

#秘密鍵の登録#

そしたら、まずGit Bushで以下のコマンドを打ち、``ssh-agent``が作動しているかの確認をします。

```
$ eval "$(ssh-agent -s)"
```

以下のような結果が帰ってきたら成功です。

```
Agent pid 88473
```

次に秘密鍵の登録を行います。

```
ssh-add /c/Users/username/.ssh/id_rsa(id_rsa_github)

Enter passphrase for /c/Users/username/.ssh/id_rsa(id_rsa_github): # 最初に設定したパスワードを入力
```

#接続確認#

Git Bushで``ssh -T github.com``というコマンドを打ち、下記のように帰ってきたら成功です。

```
ssh -T git@github.com
Warning: Permanently added 'github.com,52.69.186.44' (RSA) to the list of known hosts.
Hi ucan-lab! You've successfully authenticated, but GitHub does not provide shell access.

// 場合によってはパスワードの入力を要求される。その時は、先ほど作成したパスワードを入力
```

``Warning``は無視してOK

#Docker Desktopのインストール#

さて、ここからが本題です。

下記のリンクからDockerをダウンロードします。



・Mac：[https://docs.docker.com/docker-for-mac/install](https://docs.docker.com/docker-for-mac/install)

・Windows：[https://docs.docker.com/docker-for-windows/install](https://docs.docker.com/docker-for-windows/install)

インストールし終えたら、インストールできてるか確認します。

```
docker --version
Docker version 20.10.12, build 2291f61

docker-compose --version
docker-compose version 1.27.14, build 40524192
```

#環境変数の設定#

次に環境変数の設定を行っていきます。

環境変数のPathに以下の変数を追加します。

``C:\Program Files\Docker\Docker\resources\bin``

#dockerコマンド入力#

最後にDockerコマンドを叩きます。

まず、DockerDesktopを開きます。

まだ会員登録を行っていない方は会員登録を行ってください。

そして、起動したまま下記のコマンドを入力してください。

```
docker run hello-world
```

何事もなく出力されたらOKです！

いや～長かった。

ただ、新しくパソコン買ったりしたらまた時間食われかもしれないからメモとして残しておきたかった。

皆さんの時間がつぶれないことを祈っております。

以上、「Docker初心者がWindowsでgitSSH接続からdockerコマンドを打てるまでの流れ」でした！

良ければ、LGTM、コメントお願いします。

また、何か間違っていることがあればご指摘頂けると幸いです。

他にも毎日初心者さん向けに記事を投稿しているので、時間があれば他の記事も見て下さい！！

Thank you for reading
STM32G031J6M6に中華ST-LINKでプログラムを書き込む。

1.何らかの方法でSTM32G031J6M6を入手して
何らかの方法でVDDに結線、GNDに結線
Nretをプルアップする。
2.何らかの方法で中華ST-LINKを入手する。
今回使った中華ST-LINKは、aitendoの
500円のやっです。
ついでに、STM32G031J6M6の
VDD,GND,STCLK,SWDIOを中華ST-LINKと結線する。
3.何らかの方法でSTジャパンの会員になる。
4.STM32 ST-LINK Utilityをダウンロード
STSW-LINK004 STM32 ST-LINK utility 4.6.0
でダウンロードボタンをおす。
5.ダウンロードディレクトリーで
en.stsw-link004_v4-6-0.zipを展開する。
6.en.stsw-link004_v4-6-0ディレクトリーの
setup.exeを実行する。
7.STM32 ST-LINK Utilityを実行する。
msc140.dellがないが自分の環境では、出たが
c++のランタイムを入れたら治った。
vc_redist.x64.exe
vc_redist.x86.exe
詳しくは、各自調べてね!
気にもしていなかったけど、当然、ソフトウェアリセットに設定する。
一旦挿す。
セッテングボタンを押してソフトウェアリセットを選択する。
一旦抜く。
リセットピンを接続しない為に必要な設定です。
8.STM32G031J6M6と中華ST-LINKを繋いだ物をUSBに挿す。
9.接続ボタンを押す。
メモリーマップが表示される。
10.書き込みボタンを押す。
mbed_blinky_031.XXXX (4).binを選択する。　各自違う。
スタートで書き込み開始。
11.切断ボタンを押す。
12.終わり。

![stm32_oled_d_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/20ef4e5b-c474-f46b-c928-2e4a6ac21c0b.jpeg)

![sel_tx_9600_g031_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/85830f54-0301-e5c1-11b7-3b50cd4cebd5.jpeg)
ProgateのHTML・CSS初級編クリアした感想

#Progateとは
ブラウザ上で実行しながらプログラミングを学習することができるサービス。

# 成果物
Progate初級編(無料)をやり終えると下記のようなサイトができる。
https://mo-ri-regen.github.io/Progate_HTML_study_1/

# 学習方法
スライドを見る→スライドをもとにした課題が与えられるのでそれを解くという形式。
課題が解けると、経験値がたまりレベルアップするので、楽しみながら解ける。

# 有料プラン
有料プラン限定のコースがあり、月980円で学び放題。
プログラムを始めたてで雰囲気を学ぶだけならアリだとは思うが、
モダンな書き方を身につけるには有料プランへの移行は必須かなと思った。

# 感想
初級編をやるだけでもボリュームがあった。
忘れてしまっても、すぐスライドに戻れて便利だった。
何も意識しないと、書いてあることをそのまま移してしまい、
ただ答えを書いただけでやった気になる。
なので、今書いたコードはどこに反映されるのかということを意識しながら書かないと、
理解度が進まないと感じた。
node-red-contrib-web-worldmapで一瞬イギリスの地図が表示される問題の対策

# はじめに

某SNSのNode-RED User Groupにて...
`node-red-contrib-web-worldmapで地図を表示するとイギリスが一瞬表示される`
という質問がありました。

解決方法としては
`ブラウザ側で動作するJavaScriptに座標が埋め込まれているのでworldmap.jsを書き換えると良い。`
ということでした。

**ソースを書き換えずに初期位置を自由に変えられるようになったら嬉しいよなぁ....**
と考えて、解決できたので、この記事を書くことにしました。

# ■ (1) 現象

**イギリスの地図が短時間表示されて、違う位置の地図が表示に切り替わる。**
**(通信の状況によっては、イギリスの地図が表示されない事もあります)**

おおよそ下記のような動作となっていました。
*(1-1)→(1-2)で異なる地図が見えるので、チラついて違和感を感じるみたいですね。*

### (1-1) ブラウザで worldmap にアクセスする。

JavaScriptに埋め込まれている情報をもとに白黒のイギリスが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147526/f1695515-a385-9cee-c561-d0c54a388dd8.png)

### (1-2) ブラウザがバックエンド(Node-RED)とWebSocket通信を開始する。

表示位置と拡大率を取得し、改めてフローで設定してある地図を更新する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147526/45064398-a9f7-7233-832b-45595437ecf2.png)


# ■ (2) 修正概要

動作について、３点ほど修正してあります。

### (2-1) チラつきの解消

下記の方針としました。
(1-1)で、希望する地図を表示してくれないのは明白なので(1-1)での地図表示をやめる。
(1-2)は、正常時は必ず発生するので、このタイミングで地図を表示する。

### (2-2) 初期表示位置を記憶する

下記のようなメッセージをworldmapフローに入力すると、地図の表示位置を変更することができます。
この値を記憶しておき、新しくworldmapを開いたユーザーは最後に送信したcommandの値で初期表示されるようになります。

```javascript

msg.payload = { 
    command : { 
        lat:36.68946, 
        lon:139.69187, 
        zoom:12
    } 
};
return msg;
```

ただし、_sessionid が設定されている場合は、セッションごとの個別指示なので記憶しません。
また、セッションはブラウザをリロードすると新規生成されて再接続を認識できないので
セッション間の情報記録はしない事としました。

### (2-3) 初期位置更新メッセージ(preset)を追加

`msg.payload.command` を利用するとworldmapを開いているユーザー全ての表示位置が変わってしまいます。
そこで初期表示位置だけを更新し、新しくworldmapを開く人の初期表示位置を更新できる命令を追加しました。

```javascript
msg.payload = { 
    preset : { 
        lat:36.68946, 
        lon:139.69187, 
        zoom:14
    } 
};
return msg;
```


# ■ (3) 導入手順

公式には修正が取り込まれていませんので、下記手順でプログラムを置き換える必要があります。
**ソースコードを直接修正しても構いません**

おおよそ下記の手順で、導入します。

### (3-1) Node-REDのデータフォルダに移動します。

環境により場所が異なりますので、ご注意を！

```bash
$ cd ~/.node-red/node_modules
```

### (3-2) node-red-contrib-web-worldmap を念のためバックアップします。

```bash
$ mv node-red-contrib-web-worldmap node-red-contrib-web-worldmap_org
```

### (3-3) 対策を行った node-red-contrib-web-worldmap を取得します。

```bash
$ git clone -b initial-position https://github.com/t-kawamorita/RedMap.git node-red-contrib-web-worldmap
```

### (3-4) 必要なライブラリをインストールします。

```bash
$ cd node-red-contrib-web-worldmap
$ npm install
```

### (3-5) Node-REDを再起動します。

普段の手順でNode-REDを再起動してください。
下記はRaspberry PI4を利用したときのコマンド例です。

```bash
$ node-red-start
```


# ■ (4) 終わりに。

個人的には、これで良いのでは？と考えて修正してみました。
使い方の勘違いから、間違った修正をしているよ！ということがあればコメントを頂けると嬉しいです。

ソースは、[こちら](https://github.com/t-kawamorita/RedMap/tree/initial-position)です。
差分を確認したい場合は、[こちら](https://github.com/t-kawamorita/RedMap/commit/e357bef06f0c5b9fafdb5da02a6e1740121f50c6)です。

少しご意見を頂いてからプルリクエストを出してみようと思います。
iOS向けアプリをMac Catalyst対応にするまでに引っかかったこと

# 前置き
実際Mac Catalyst対応するにはMac向けのUI/UXを考える必要があるため、簡単ではないものの
iOSエンジニアがMac向けのアプリをより簡単に作れるようになるのは魅力的です。
業務ではまず触れる機会はないのですが、趣味で開発しているアプリをMac対応することしたので
その際に引っかかったポイントを随時書いているのが本記事です。

トラブルシューティングの対応内容は、プロジェクトによって状況が異なる場合もあるので
参考にならないこともあるかもしれませんが、ご了承ください。。

# Code signing "app-name.app" failed.
![スクリーンショット 2021-02-03 0.06.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114168/936a4a7a-1447-fd37-67a5-ea3899f33f69.png)

Codesign系のエラーでまず引っかかりました。
エラーログは下記のような感じ。

```
2021-02-02 15:05:51 +0000  /var/folders/rr/tkpws2d55h1_t3zgpx_psjy80000gn/T/XcodeDistPipeline.~~~no7TwZ/Root/Applications/hogehoge.app: replacing existing signature
2021-02-02 15:05:51 +0000  /var/folders/rr/tkpws2d55h1_t3zgpx_psjy80000gn/T/XcodeDistPipeline.~~~no7TwZ/Root/Applications/hogehoge.app: code object is not signed at all
2021-02-02 15:05:51 +0000  /usr/bin/codesign exited with 1
```
対応としては、**Enable Bitcode**を**No**とすることで署名に成功しました。
下記の投稿ではXcodeのひとつのバグという風に記載されていました。
[Catalyst app with framework won't codesign - Apple Developer Forum](https://developer.apple.com/forums/thread/666269)

フォーラムの投稿ではFrameworkを利用しているため自動署名に失敗しているようでしたが
今回開発したアプリではサードパーティのFrameworkを利用していませんでした。
ただ、Metalは利用しておりMetal向けの生成ファイル絡みで署名が失敗する要因となったのではないかと思っています。

# ITMS-90242 LSApplicationCategoryType Keyの設定漏れ
Mac向けアプリには、アプリのカテゴリをinfo.plistに含める必要があります。
[LSApplicationCategoryType | Apple Developer Documentation](https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype)
上記のドキュメントからアプリに最も近いカテゴリを選択し、info.plistに追記します。
![スクリーンショット 2021-02-03 0.22.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114168/c5fa281d-83b9-ac66-dc76-282601cb3dbc.png)

# ITMS-90257 The bundle is invaild
これは完全に私の管理ミスなのですが、CFBundleVersionのフォーマットに則っていないVersionを指定していたため起きていました。
Versionを**3.0.0** BuildVersionを**3.0.0.1**とBuildVersionは4つのピリオドで区切った整数で最後の4つ目の整数でBuild番号を管理していました。(Test flightの段階でバイナリに不具合が見つかったらBuild番号をあげるような運用)
iOSでのアーカイブ時は問題ないのですがMac版のアーカイブ時にはフォーマットが適切でないことから弾かれるようです。

本来は、

- 最初の数字は最新のメジャーリリースを表し、最大長4桁まで
- 2番目の数字は、最新の重要な改修を表し、最大長は2桁まで
- 3番目の数字は、最新のマイナーなバグ修正を表し、最大長は2桁まで

のルールで決めるべきものだそうです。
また開発時に向けて更新される番号の後にサフィックスを付与することができるとのことでした。
付与できるサフィックスは、**d,a,b,fc**の4つ。
サフィックス後ろには数字を付与することができ、BuildVersionを示すことができます。
Build Versionの最大値は255まで
アプリリリースの際はサフィックスを削除することが必要になるとのことでした。

[CFBundle Version - Apple document archive](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/20001431-102364)