OmniAICreator/Qiita-1.07M huggingface dataset:
いつの間にかGCPで1万円の課金が発生していた件

#いつの間にかGCPで１万円の課金が発生!

無料枠の中で使用するにとどめていたはずなのに。

###当該プロジェクト

* 保有株の情報(価格推移, 次回決算発表日など)を日本市場、米国市場の終了後に自分のメールに送信する
* 大学の講義情報(アナウンス, 未提出課題, テスト, メール)をまとめて自分のメールに送信する

###使用GCPプロダクト

* Cloud Function
* Cloud Pub/Sub
* Cloud Scheduler

###原因

1. メールの2段階認証を設定した為。stmpを使ったメール送信では送信元メールアカウントのID(メールアドレス)とパスワードが必要だが、2段階認証を有効にした場合、メールアドレスのパスワードではなく例外的に2段階認証なくフルアクセスを許可するアプリケーションパスワードをログインパスワードに置き換える必要がある。例外的に設定したアプリケーションパスワードと from_addr を与えて smtp経由で送信する必要がある。セキュリティー上良くないなと思い2段階認証を設定したが、GCPでそのメールアカウントを使ってsmtp経由で送信しているのを忘れていた。

2. Cloud Functions で失敗時に再試行するようビルドしていた。1日に2~3回しか実行しないはずなのに、上記のstmpエラーがトリガーとなって24時間実行していたようだった。

###解決法

1. 2段階認証をオンにしたままsmtp送信を行うには、例外的に2段階認証なくフルアクセスできるようにアプリケーションパスワードを作成する。ログイン時にはメールアドレスとアプリケーションパスワードを与える。もちろん2段階認証をオフにすることも出来るがセキュリティー上非推奨。

2. Cloud Functions で失敗時に再試行をオフにしてビルドする。自分しか使ってないサービスならこれでも良いだろう。


また、Google Cloud Console をスマートフォンでモニタリングできる Google Cloud Console というアプリがある。モニタリング用途であればパソコンからアクセスするまでもないことが多いし、予算アラートもスマホに通知されるので便利。

iOS: [Google Cloud Console](https://apps.apple.com/jp/app/google-cloud-console/id1005120814)
Android: [Google Cloud Console](https://play.google.com/store/apps/details?id=com.google.android.apps.cloudconsole&hl=ja&gl=US)


いや、全然GCP周りの躓きじゃないじゃん。メールやん。
GlueのWorkflows ジョブ間でパラメータを取得



https://qiita.com/pioho07/items/6d85b0c6b27f56e58d8f

getResolvedOptionsで'WORKFLOW_NAME'と'WORKFLOW_RUN_ID'も取得しています。get_workflow_run_propertiesでこの値を使って、ワークフローの実行プロパティを取得しています。
-----------------------------------------------------------

import sys
#add start
import boto3
#add end

from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job


glue_client = boto3.client("glue", region_name='ap-northeast-1')
args = getResolvedOptions(sys.argv, ['JOB_NAME', 'WORKFLOW_NAME', 'WORKFLOW_RUN_ID'])
workflow_name = args['WORKFLOW_NAME']
workflow_run_id = args['WORKFLOW_RUN_ID']
workflow_params = glue_client.get_workflow_run_properties(Name=workflow_name, RunId=workflow_run_id)["RunProperties"]
#ワークフローのプロパティを取得しています。
run_env = workflow_params['workflow_env']
print(run_env)


## put
#ワークフローのプロパティを設定する
workflow_params['workflow_env'] = 'prod'
glue_client.put_workflow_run_properties(Name=workflow_name, RunId=workflow_run_id, RunProperties=workflow_params)
###add end

---------------------------------------------------------
s3 = boto3.resource('s3')
bucket = s3.Bucket('test-glue00')
bucket_name='test-glue00'
for object in bucket.objects.filter(Prefix='se2/tmp2/country='):
    #print(object.key)
    old_file = object.key

---------------------------------------------------------
ジョブパラメータの取得方法
https://qiita.com/YujiHamada3/items/780af5c7bb5b9d4fa997
import sys
from awsglue.utils import getResolvedOptions
args = getResolvedOptions(sys.argv, ['hoge_string', 'hoge_int'])
step_count = args['hoge_string']
store_name = args['hoge_int']
ts-node config

## package

`package.json`

```
{
  "name": "demo",
  "version": "0.1.0",
  "main": "",
  "scripts": {
    "start": "ts-node src/main.ts"
  },
  "license": "MIT",
  "dependencies": {},
  "devDependencies": {
    "@types/node": "^17.0.2",
    "ts-node": "^10.4.0",
    "tslib": "^2.3.1",
    "typescript": "^4.5.4"
  }
}
```

## tsconfig

`tsconfig.json`

```json
{
  "compilerOptions": {
    "lib": [
      "es6"
    ],
    "target": "es6",
    "module": "commonjs",
    "moduleResolution": "node",
    "outDir": "dist",
    "resolveJsonModule": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "sourceMap": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "**/*.spec.ts"]
}

```



---

参考：

https://github.com/TypeStrong/ts-node/blob/main/README.md

https://stackoverflow.com/q/62096269
PHPで期間(日時)に応じて表示内容を自動的に切り替える

日時によって表示内容を変える

```
<?php
//タイムゾーンのセット
date_default_timezone_set('Asia/Tokyo');

//date(現在日時)が右辺の日時より前の場合
if (date('Y-m-d H:i') < '2021-12-21 13:00'): ?>

<p>2021年12月21日 13時前です。</p>


<?php 
//date(現在日時)が右辺の日時より前の場合
elseif (date('Y-m-d H:i') < '2021-12-25 17:30'): ?>

<p>2021年12月25日 17時半前です。</p>

<?php else: ?>

<p>2021年12月25日 17時半前を過ぎました</p>

<?php endif; ?>

```


# 日本語で曜日を含めて表示させる

```
$weekDays = array('日', '月', '火', '水', '木', '金', '土');
$today = date('Y年m月d日');
$weekdayIndex = date('w');
$weekday = $weekDays[$weekdayIndex];
$todayWithWeekday = $today . '(' . $weekday . ')';
echo $todayWithWeekday;
// 結果「2023年○月○日(月)」
```
【Python】英単語の音声ファイルをスクレイピングして集める

タイトル通りです。
英単語の音声ファイルが欲しかったので、Weblioさんからスクレイピングして拝借したいと思います。

## 1単語の音声ファイルをスクレイピングする

```python
import requests
from bs4 import BeautifulSoup
base_url = 'https://ejje.weblio.jp/content/'
# スクレイピングする英単語
word = 'experience'
url = base_url + word
res = requests.get(url)
soup = BeautifulSoup(res.text, 'html.parser')
try:
    # スクレイピング
    mp3_dl_url = soup.find('source')['src']
    response = requests.get(mp3_dl_url)
    mp3 = response.content
    # 音声ファイルを保存
    save_file = word + '.mp3'    
    with open(save_file,'wb') as f:
        f.write(mp3)
except:
    print('{}は見つかりません'.format(word))
```

## 複数の英単語の音声ファイルをスクレイピングする
1つだけの音声ファイルのためにスクレイピングしても面白くないので、もっと実用的に複数の英単語の音声ファイルを集めます。

### 英単語をスクレイピングして取得する
もともと、音声ファイルを集めたい英単語のリストとかを持っていれば必要ありませんが、今回は英単語のリストもスクレイピングして用意しようと思います。

[こちらのサイト](https://englishlevelup.net/toeic-word-list/)にTOEIC頻出英単語の一覧がありましたので、スクレイピングしてCSVファイルで保存しました。

```python
import requests, csv
from bs4 import BeautifulSoup
url = 'https://englishlevelup.net/toeic-word-list/'
res = requests.get(url)
soup = BeautifulSoup(res.text, 'html.parser')
w = soup.find_all('td')
word = []
for i in range(0,4000, 2): 
    tmp = []
    tmp.append(w[i].get_text())
    tmp.append(w[i+1].get_text())
    word.append(tmp)
# Chapterごとに単語を分ける
w1 = word[0:300]
w2 = word[300:600]
w3 = word[600:900]
w4 = word[900:1200]
w5 = word[1200:1500]
w6 = word[1500:1800]
w7 = word[1800:2000]
word_list = []
word_list += [w1,w2,w3,w4,w5,w6,w7]
# Chapterごとにファイルを分けて保存
for i,x in enumerate(word_list):
    filename = 'dict/chapter' + str(i+1) + '.csv'
    with open(filename,'w') as f:
        writer = csv.writer(f, lineterminator='\n')
        writer.writerows(x)
```

### CSVファイルから複数の英単語の音声ファイルをスクレイピング

集めた英単語リストのCSVファイルを使って、Weblioさんから音声ファイルをスクレイピングします。

※Weblioに負荷をかけすぎないように注意してください。

```python
import requests, csv, time
from bs4 import BeautifulSoup
base_url = 'https://ejje.weblio.jp/content/'
# Chapterの数だけループ
for i in range(1,8):
    filename = 'dict/chapter' + str(i) + '.csv'
    with open(filename) as f:
        reader = csv.reader(f)
        wordlist = [row for row in reader]
    # 各単語ごとにスクレイピングして音声ファイルを保存
    for w in wordlist:
        url = base_url + w[0]
        res = requests.get(url)
        soup = BeautifulSoup(res.text, 'html.parser')
        
        try:
            mp3_dl_url = soup.find('source')['src']
            print(w[0])
            
            response = requests.get(mp3_dl_url)
            mp3 = response.content
            save_file = './pronunciation/' + w[0] + '.mp3'
            
            with open(save_file,'wb') as f:
                f.write(mp3)
        except:
            print('{}は見つかりません'.format(w[0]))
        
        # weblioに負荷をかけすぎないよう間隔をあける！！
        time.sleep(5)
```
１回の送信で複数のレコードを作成したい

### 前提
タイトル参照。
冷蔵庫の在庫管理をするオリジナルアプリを作成中です。
食材は名前、賞味期限、数量、値段を入力（バリデーションはまだ設けてないです）
これらをカテゴリーごとに表示、保存できるように試行錯誤中。

この「保存」の処理が、プログラミングを学び始めて4ヶ月の初心者にはとても難しく...

Ruby on Rails2.6.5を使用していると思います。（.ruby-versionというファイルにそう書いてあったので）


### テーブル名
**Categoriesテーブル**
c_name
user(外部キー）

**Itemsテーブル**
name
category(外部キー）

他にもテーブルやカラムはありますが、試作段階で使用しているのはこれだけです。


### 最終的に落ち着いたコード
フォーム部分

```erb
<%= form_with model: @item_information, url: items_path, local: true do |f| %>
  <div>
  <% @categories.each do |category| %>
    <div>
      <div><%= category.c_name %></div>
      <% 2.times do %> <%# 後々JavaScriptを使用してフォームを増減させる予定 %>
        <div>
        <%= f.text_field :name, name:"[item][name][]" %>
        <input name="[item][category_id][]" type="hidden" value=<%= category.id %>>
　　　　　　　　　　　　　　　　　　　<%# １つ１つのフォームにcategory_idを付与するためにinputでねじ込んでます %>
        </div>
      <% end%>
    </div>
  <% end %>
  </div>
  <%= f.submit '追加' %>
<% end %>
```
こんな感じのフォームができました。
登録したカテゴリーが増えればそのぶんフォームも増えます。
![form.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2314388/554a7b78-c184-4847-439d-14bb876d64b5.png)

コントローラー部分

```ruby
class ItemsController < ApplicationController
# 省略
  def create
    @item_information = item_params
    @item_information.each do |item_data|
      @item = Item.new(item_data)
      if @item.valid?
        @item.save
      else
        render :new
      end
    end
    @categories = Category.where(user_id: current_user.id)
    @item_information = ItemNameInformation.new
    redirect_to root_path
  end

  private

  def item_params
    items = []

    item_data = params[:item]

    item_data[:name].zip(item_data[:category_id]) do |name, category_id|
      item = { name: name, category_id: category_id }
      items << item
    end
    return items
  end
end
```
item_paramsでparamsの値をハッシュを含む配列に作り替えて、
createアクションでeach文で回しながら保存しています。

今のところバリデーションを設けているのが外部キーしかないので、エラーへの本格的な対処は後々実装します。


### 懸念点

```ruby
item_data[:name].zip(item_data[:category_id]) do |name, category_id|
  item = { name: name, category_id: category_id }
  items << item
end
```
この部分...
今は試作段階でnameとcategory_idしか使用してないのでいいんだけれど、
本格的に実装しようとした時、あと4つほどフォームを増やす予定です。
その値を全て.zipで繋ぐとして記述がものすごく長くなる気がしています。
でも、受け取る値を限定するためにカラム名を指定しないわけにもいかないし
改行はしても問題ないんだろうか...?


### あとがき
アプリケーションの使いやすさを求めるためにも１度の送信で済ませる必要がありました。
もともとparamsで送られてきた値は、

```ruby
item => {"name"=>["〇〇", "〇〇"], "category_id"=>["〇〇", "〇〇"]}
```
という感じでした。
これを、

```ruby
item = [{name: "〇〇", category_id: "〇〇"}, {name: "〇〇", category_id: "〇〇"}]
```
という形で扱いたくて、binding.pryを使いながら探り探りでの実装でした。

ここ、こうした方がいいなどございましたらご教授ください:bow:
『pythonで音を鳴らす方法を詳しめに解説』を外部ライブラリなしで実装してみた

# はじめに
この記事は @ShijiMi-Soup 様の [pythonで音を鳴らす方法を詳しめに解説](https://qiita.com/ShijiMi-Soup/items/3bbf34911f6e43ee14a3) を参考にしつつ外部ライブラリ、つまり`numpy`や`pyaudio`を用いないで音を鳴らすことに成功したので共有する記事です。環境や宗教上の理由で外部のライブラリ（`pip`でインストールするようなもの）を用いることが出来ない場合などにご活用ください。
本家様の記事は音ってなんだ的な所から解説していますが、そのような解説は本家様の記事に任せて、この記事では実装例と相違点について取り上げようと思います。それでは参りましょう。

# 環境
```
Python 3.9.0
```
音を鳴らす機構はそれぞれの環境に依存するので後々解説します。

# プログラムの実装
まずはプログラムを見ていただきます。

```Python
import wave
import struct
import math
import os

def arrange(start, end):
    l = []
    x = start
    while(x < end):
        l.append(x)
        x += 1.0
    return l

def mul(x, l):
    return [x * e for e in l]

def div(x, l):
    return [(lambda a, b: a / b if b != 0 else 0)(e, x) for e in l]

def sin(l):
    return [math.sin(e) for e in l]

sec = 1.0 #1秒
note_hz = 440 #ラの音の周波数
sample_hz = 44100 #サンプリング周波数

t = arrange(0, sample_hz * sec)
wv = sin(div(sample_hz, mul(2 * math.pi * note_hz, t)))

max_num = 32767.0 / max(wv)
wv16 = [int(x * max_num) for x in wv]
bi_wv = struct.pack("h" * len(wv16), *wv16)

name_file = "sound.wav"

file = wave.open(name_file, mode='wb')
param = (1,2,sample_hz,len(bi_wv),'NONE','not compressed')
file.setparams(param)
file.writeframes(bi_wv)
file.close()

if os.name == "nt":
    import winsound
    winsound.PlaySound(name_file, winsound.SND_FILENAME)

```

以降は解説です。

# 外部ライブラリに頼らないとどうなるか

外部ライブラリに頼らないとどうなるかという話ですが、まずは`numpy`から、元の記事を見ていただければ何となくわかると思いますが、音声ファイルはたくさんの数値の集まりで出来ています、配列ですね。`numpy`では便利な関数や配列クラスを定義されています。例えば`arrange`関数を使えば浮動小数点数を含む数列を生成できたり、配列に対して掛け算や割り算を行うことが出来ます。それが`numpy`の素晴らしい所なのですが今回はその恩恵にあずかることはできないので自前で実装します。それがソースコード上部の関数群です。

```Python
# 始まりと終わりを指定して等差数列の生成する関数
def arrange(start, end):
    l = []
    x = start
    while(x < end):
        l.append(x)
        x += 1.0
    return l

# リスト型に対して掛け算を行う関数
def mul(x, l):
    return [x * e for e in l]

# リスト型に対して割り算を行う関数
def div(x, l):
    return [(lambda a, b: a / b if b != 0 else 0)(e, x) for e in l]

# リスト型に対してそれぞれの値を`math.sin`関数に入れる関数
def sin(l):
    return [math.sin(e) for e in l]
```

若干お行儀悪い気がしますが許してください。

次に`pyaudio`が使えないとという話ですが、何らかの音声再生機能をかわりに選ばなくてはいけません。実装例ではwindows環境向けに記述されています。

```Python
if os.name == "nt":
    import winsound
    winsound.PlaySound(name_file, winsound.SND_FILENAME)
```

ここはPythonで鳴らすことを考えず`wav`ファイルをコマンドから再生することを考えましょうか。例えば`paplay`がインストールされている環境ならこのように記述することが出来ます。

```Python
import subprocess
subprocess.run(["paplay", name_file])
```

使えるかはターミナルで`paplay --version`と入力してみてください。

他にもMacなら`afplay`というコマンドが使えるはずなのでそれを使います。

```Python
import subprocess
subprocess.run(["afplay", name_file])
```

音声再生はこのようにします。これで大抵のデスクトップ環境はカバーできるのでは？と思うのですが、何か良い`wav`の再生方法があったらコメントで共有してくれると助かります。

# おわりに
今回は以上です。ショートな記事でしたが忘備録としてまとめられる程度には自分の中でまとまったので記事に起こしました。役に立つことがあれば幸いです。
ここまで読んでくださってありがとうございました。次は何しようかな…思いついたらまたやるのでもしよろしかったら次も読んでくださるとありがたいです。
WSL2上のUbuntuでcatkin_make

# catkin_makeでwindowsのファイルシステムが原因のエラー
WSL2上のUbuntuにROSをインストールしcatkin_makeを実行すると

```
-- BUILD_SHARED_LIBS is on
/home/xxxxxx/catkin_ws/devel/env.sh: 4: export: Files/WindowsApps/CanonicalGroupLimited.Ubuntu18.04onWindows_1804.2020.824.0_x64__79rhkp1fndgsc:/mnt/c/Program: bad variable name
Traceback (most recent call last):
  File "/home/xxxxxx/catkin_ws/build/catkin_generated/generate_cached_setup.py", line 22, in <module>
    code = generate_environment_script('/home/xxxxxx/catkin_ws/devel/env.sh')
```

のようなエラーが起こる場合がある。
# 原因
windowsのディレクトリ名にスペース（Program[space]Files）を含むものがあり、UNIXのスクリプトがワードの区切りと誤認識することが原因
# 対策
windowsのPATHを継承しないようにする。[→WSL2でWindowsのPATH設定が引き継がれるのを解除する](https://zenn.dev/o2z/articles/zenn-20210524-01)
あるいは、windowsのPATHを除いてPATH環境変数を再設定してもよい
こちらの記事も類似の事象です[→WSL2にROS導入メモ](https://qiita.com/Decwest/items/05eab6abddd587f7e40a)
python-docxの備忘録

毎日のように「Microsoft Word」を利用する職業の人にとって，ワード文書（docxファイル）を自動で生成するアプリケーションや，文書の内容を自動で解析できるアプリケーションは非常に便利です。

ただ，**毎日のように「Microsoft Word」を利用する人**は，それほど多くはいません。

したがって，上記のようなアプリケーションには需要がなく，（*感覚ベースですが*）なかなか開発されないようです。

既存のアプリケーションが無いなら自分で作るしかありません。

本記事では，**毎日のように「Microsoft Word」を利用する人**の救世主になりうる「python-docx」について，備忘録も兼ねて記載します。

<details><summary>毎日のように「Microsoft Word」を利用する職業の人とは？</summary>ここでは弁護士その他の士業の人を想定しています。</ditails>


#python-docxで実現可能になること
そもそも「python-docx」を使えば，何が可能になるでしょうか？

たとえば，次のようなことが可能になります。

- 段落の挿入
- 文字のスタイル変更（フォントサイズやテキストカラー）
- テーブル（表）の挿入

毎日のように「Microsoft Word」を利用する職業の人が，職業上使用する文書を作成する上で必要な機能は一通り揃っている印象です。

#python-docxについての文献

python-docxに関する日本語の文献は多くはありませんが，python-docxについては[公式のドキュメント](https://python-docx.readthedocs.io/en/latest/index.html#)が非常にわかりやすいため，こちらを見るだけで，相当程度は使いこなすことができそうです。

#python-docxのインストール方法

pipを使ってインストール可能です。

```python
pip install python-docx
```

#python-docxの基本的な使い方

python-docxの基本的な使い方は，次の通りであると理解しています。

##文書の作成及び保存

docx文書を作成し，「sample.docx」という名称で保存するためには，次のコードを書くだけです。

```python
from docx import Document
doc = Document()
doc.save('sample.docx')
```

##段落の挿入

文書の作成後，段落を挿入するためには，次のコードを記載します。

```python
p = doc.add_paragraph()
p.add_run("テキストテキスト")
```

まず段落の枠を作って，その枠に文字を追加するイメージです。

これを繰り返せば，一定の文書が自動で作成可能です。

##テーブル（表）の挿入

文書の作成後，テーブル（表）を挿入するためには，次のコードを記載します。

```python
table = document.add_table(rows=2, cols=2)

th_cells = table.rows[0].cells
th_cells[0].text = 'テキスト'
th_cells[1].text = 'テキスト'

tr_cells = table.rows[1].cells
tr_cells[0].text = 'テキスト'
tr_cells[1].text = 'テキスト'
```

これだけで，2行2列のテーブルを挿入できます。

for文を使って繰り返し処理をすれば，自動で一定の法則に従った表を作れるのでかなり便利だと思います。

##画像の挿入

次に文書に画像を挿入するためには，次のようなコードを記載します。

```python
document.add_picture('サンプル.png', width=Inches(1.25))
```

#最後に
本記事では，python-docxの基本的な使い方を記載しました。

毎日のように「Microsoft Word」を利用する職業の人が，自分でワード文書（docxファイル）を自動で生成するアプリケーションや，文書の内容を自動で解析できるアプリケーションを作りたいと思った時に参考にしていただけると幸いです。

なお，この記事は適時更新する予定です。
MAVLinkを簡単に試せるpymavlinkでHello World!

[ACCESS Advent Calendar 2021](https://qiita.com/advent-calendar/2021/access) 23日目の記事です。

# はじめに

pymavlinkを使って、QGroundControlにHello World!という文字列を簡単に表示させる記事です。
Windows10で確認しています。pymavlink自体はpythonが動く環境であれば利用できますが、動作確認に[前回の環境](https://qiita.com/Hiroshi_Tsukamoto/items/20e6925eb6fd1fc36c46)を使用していますので、動作確認をする場合は用意して下さい。お好みのドローンやGCSを使用しても問題ありません。

# pymavlink

https://github.com/ArduPilot/pymavlink
pymavlinkは、MAVLinkのPython実装です。ライブラリ群だけでなく、便利なツールも存在しています。
GCSやドローンのコンパニオンコンピュータ、その他多くのMAVLinkを使用するアプリに使用できます。個人的には、ドローンやGCSの動作確認を簡単に行いたい場合に利用しています。

# インストール

Pythonがない場合、https://www.python.org/ よりPythonをインストールします。
Pythonをインストール後、コマンドプロンプトで下記コマンドを実行します。

```
pip install pymavlink
```
これだけです。簡単ですね。

# スクリプト

QGroundControl上に、Hello World!と表示させるスクリプトです。

```hello.py
from pymavlink import mavutil

connection = mavutil.mavlink_connection('udpout:localhost:14550', source_system=1)
connection.mav.statustext_send(mavutil.mavlink.MAV_SEVERITY_EMERGENCY, "Hello World!".encode())
```

# 動作確認

スクリプトを実行してみます。予め[この手順](https://qiita.com/Hiroshi_Tsukamoto/items/20e6925eb6fd1fc36c46#%E5%AE%9F%E8%A1%8C)に従い、SITLとQGroundControlを起動しておきます。

続いて、コマンドプロンプトで下記コマンドを実行します。

```
cd (test.pyのあるディレクトリ)
py test.py
```

実行すると、QGroundControl上に黄色いウィンドウと共にHello World!が表示されます。


# おわりに

pymavlinkで簡単なメッセージを表示してみました。これ以外にも色々なMAVLinkメッセージやコマンドを簡単に送ることができるので、https://github.com/ArduPilot/pymavlink で色々試してみて下さい。
Selenium勉強1日目の今のところ分かった基礎的な手法

[パーソンリンク アドベントカレンダー](https://qiita.com/advent-calendar/2021/personlink)22日目です！🎉
今日は冬至です。ここから、日照時間が少しづつ長くなってきますね！

## セレニウムとは
初めて聞いたときは、元素番号がだいぶ最後の方の元素の話かと思いましたが、
webを自動で動かせるツールの一つで、
テストを自動化したり、繰り返し行われるweb上での作業を自動化したりすることが出来ます。
Pythonのライブラリになりますので、Seleniumのインストール等はpipコマンドなどを使って実行します。
(初期設定に関する情報はたくさんありますので、ググってみてください。

### 環境
Mac(10.14.6 Mojave)
Python(2.7.10)
Google Chrome

### 注意点
ブラウザを操作するためにはwebdriverが必要になります。
Chromeのバージョンに合わせてドライバーが必要なので注意してください。

## 基礎的な構文
### 任意のURLへのアクセス
```access.py
// Seleniumでwebdriverを使えるようにする
from selenium import webdriver
fromselenium.webdriver.common.keys import Keys

// 任意のサイトなどにアクセス
// オプションなどの設定も必要ならここで
driver = webdriver.Chrome()
driver.get("https://~~~~~~~~")
```

### 要素の取得方法
要素は、HTMLのidやname,classなど様々な指定方法によって取得することができます。
今回はXPathでの取得で説明をします。

任意のページを開き、右クリック→検証へ進むと画面のどこかにデベロッパーツールが出現します。
その中から任意の要素のHTMLタグを探し出し(ツール左上のスマホモードアイコンの左にあるアイコンで任意の要素をクリックすると該当のものが表示されます)
右クリックでコピー→XPathをコピーとすると要素の取得ができます。

例として、弊社[パーソンリンクの会社ページ](https://www.person-link.co.jp/)からヘッダーにあるCOMPANYの<a>タグ要素をしてみると...
***//*[@id="app"]/div[2]/div/div/div/div[2]/a[1]a***
このようになります。
例えばこれをクリックする様にして、自動でページが進む様になどですね！

### 要素の取得と文字の入力やクリック
```sample.py
// aタグ要素の取得
company_link = driver.find_element_by_xpath('//*[@id="app"]/div[2]/div/div/div/div[2]/a[1]a')
// 要素のクリック
company_link.click()

// fromタグ要素の取得
form = driver.find_element_by_xpath('//*[@id="form"]')
// form要素に任意の文字の入力
form.send_keys("personlink")
// form要素に入っている文字のクリア
form.clear()
```

### 待ち時間の設定
Seleniumに限らずスクレイピング等を行う際には、
ページのロード時間を考慮しなければなりません。待ち時間を用意してあげるというイメージですね。
これがないと、指定した要素がないよとエラーになってしまいます。
***driver.implicitly_wait(10)***
他にも方法はあるみたいですが、基本的な待ち時間の設定はこの様にします。
implicitly_wait(ここに待ち秒数)を使うと、設定した待ち秒数まで待ちますが、ページのロードが完了していると最後まで待たずに次の処理を進めてくれます。
設定すればするだけ時間が伸びてしまわないというのが良い感じですね！


かなり基礎的な内容までになりますが、以上になります。
最後までご拝読いただき、ありがとうございました。
4年目エンジニアから送る「1年目にやってよかった！」3つの行動

この記事はモチベーションクラウドシリーズアドベントカレンダー2021の22日目の記事です。

https://qiita.com/advent-calendar/2021/mcs

# はじめに
リンクアンドモチベーションでプロダクト開発をしている高橋と申します。新卒でSIerに入社してシステムエンジニアとなり、今年で4年目となりました。

この記事では、私がシステムエンジニアになりたての頃に意識していたものの中で学習に効果的だったなあと感じるものについて記載します！
1年目のエンジニアの方々にとって、少しでも有益になる情報があれば幸いです。

# Whyを考える

例えばコードレビューの際に(処理が長くて見通しが悪いので、)「メソッドを切り出しましょう。」といった指摘を受けたとします。

```
def post():
    #
    # 長くて見通しが悪い処理
    #
    return result
```

Why(指摘を受けた理由、切り出すことで何が良くなるのか)を考えずに対応をすると、このような修正を行ってしまうかもしれません。

```
def post():
    #
    # 相変わらず長くて見通しが悪い処理
    #
    num = calc(num1, num2)
    #
    # 相変わらず長くて見通しが悪い処理
    #
    return result

def calc(num1, num2):
    # 簡単な計算処理
    return result
```

特にWhyを考えずに指摘どおりに修正をしてもその場を乗り切れることもありますが、「なぜそのような指摘を受けたのか」、「メソッドを切り出すことによって何が良くなるのか」等を考えて理解しておくと、同じような指摘を受けづらくなったり、別の場面で応用が効きます。

また、**考えた/調べた結果自分の中で結論が出ても、先輩等に確認すること**も重要です。
長い処理の見通しが良くなるというメリットがある、と自分の中で結論が出ていても、先輩に聞いてみることによって、「同じ処理を再利用できる」「処理に名称がついて可読性が上がる」「mockがしやすくなってテスタビリティが上がる」といった他の観点を得ることができます。


# Howを考える
先程の例ではメソッドを切り出すWhyとして、可読性向上、再利用可能化、テスタビリティ向上が挙がりました。
逆に、「可読性向上」等のHowの一つとして「メソッドを切り出す」という行為があるという見方もできます。

Howを考えるというのは、**あるWhyに対して他のHowがないか考えてみる**、ということです。
例えば可読性の向上のHowとしては、処理の区切りで空行を挟むことや必要なコメントを残すこと、分かりやすい変数名をつける等が挙げられます。

Why、Howを行ったり来たりすることで単純に知識を増やすだけでなく、頭の中での関連付けが行われるので、実際に知識を使いこなしやすくなります。

# 基礎的な概念を知る
基礎的な概念を知ることは下記の点において役立ちます。

- 複雑そうな技術も分解して理解できる
- 触ったことのない技術に対するキャッチアップコストが下がる

#### 複雑そうな技術も分解して理解できる
一見複雑そうな技術も**基礎的な技術/概念の組み合わせで実現されています。**

例えばDockerに関しては、namespace, cgroup, overlayfs等の技術で実現されていて、Docker全体を理解するよりもそれぞれの要素技術を理解する方が(ボリューム少ない分)簡単です。(どこまで理解するかにもよりますが)

また、要素技術を理解しておくことで、開発中にハマったときにどこでハマっているかが理解しやすくなります。(参考: [繋がらないを解決するステップ](https://qiita.com/kohlm/items/ba055946b8aa805cb405))

### 触ったことのない技術に対するキャッチアップコストが下がる
当たり前ですが、自分が触ったことのない技術についても同様に、基礎的な技術等の組み合わせを実現されています。

したがって、組み合わせの内1つの要素技術について知っていればキャッチアップコストは下がります。
また、同様の技術を触ったことがある場合はその差分を理解すればいいだけとなります。
(PythonとRubyではループの書き方等に細かい差分はあるものの、大枠の構文は似ているものが多い→どちらかを書くことができれば、もう一方のキャッチアップコストは低い)


# おわりに
最後まで読んでいただきありがとうございます。

偉そうに書きましたが、私自身も日々勉強の毎日です。
皆様の意識していることで効果的なものがありましたら、コメント等いただけると嬉しいです！

また、[弊社のアドベントカレンダー](https://qiita.com/advent-calendar/2021/mcs)の他の記事もぜひご覧ください！

https://qiita.com/advent-calendar/2021/mcs
SwiftUI in UIKit！！！

# A Powerful Declarative layout framework for UIKit written in Swift

If you are interesting in SwiftUI but using UIKit in your project, Puyopuyo is the best choice for you to layout your view in SwiftUI way. Feel free to try it.

## Introduce
Youtube: https://youtu.be/3MOBCtIfRFA
Telegram: https://t.me/swift_puyopuyo

https://github.com/scubers/puyopuyo
AWS シリアルコンソールをFreeBSDで使う。

さくらのクラウドや、GMOクラウドには、仮想コンソールがあって、SSHが効かない時でも安心してコンソール作業ができますが、AWSでは、できないので不便だなーと思っていました。

ところが、AWSにはシリアルコンソールというものがある事を知って試したらちゃんと使えたので報告します。

![スクリーンショット 2021-12-20 17.11.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46046/3ea2ea94-21f7-ec3b-89f2-ef1a291b9e2c.png)

インスタンスの、アクション　モニタリングとトラブルシューティングの中に

EC2シリアルコンソールという項目があります、これです。

これを、クリックすると。
初回はこの様な画面になります。
![スクリーンショット 2021-12-20 17.12.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46046/de533772-7b26-4b87-fff6-9fa356ae50c8.png)

そこで、これを許可にします。
![スクリーンショット 2021-12-20 17.12.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46046/1f1f99ec-c100-df52-2d6b-2847beb17706.png)

この許可は、インスタンス毎ではなく、アカウント毎に設定されますので一度やれば、全部のインスタンスに対応します。

許可した後は、EC２シリアルコンソールをクリックすると

![スクリーンショット 2021-12-20 17.13.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46046/c3b4dc0c-f6c9-44b2-e3f6-e59abe57a380.png)

コンソールが開きます。
後は、通常のメインコンソールとして操作できます。

ただし。
シリアルコンソールが使えるのは、Nitro 仮想インスタンスのみです。
このため、無料枠のt2.sumallでは使えません。t3を使う必要があります。

Nitoroについては下記をどうぞ

https://dev.classmethod.jp/articles/reinvent-2020-report-cmp301/

EC2シリアルコンソールの詳細は、下記のリンクをどうぞ。

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/WindowsGuide/connect-to-serial-console.html
【Django】今まで出てきたエラー集（エラー内容・発生タイミング・原因・解決方法・参考サイトを記載）

##エラーリスト（※随時更新していきます）

###①django.template.library.InvalidTemplateLibrary: Invalid template library specified. ImportError raised when trying to load 'rest_framework.templatetags.rest_framework': No module named 'pytz'

####◆発生タイミング
python manage.py makemigrations実行時に発生。

####◆原因
models.pyに下記フィールドを設定。

```models.py
created_at = models.DateTimeField(verbose_name='登録日時', auto_now_add=True)
```
DateTimeFieldを使用するにはサードパーティ製ライブラリのpytzを使用する必要がある。
このとき初めて仮想環境を駆使して開発していたため、ライブラリをインポートしていなかったのが原因。

####◆解決方法
pytzを仮想環境にインポート。
これで無事makemigrationsが通りました。

```
pip install pytz
```

####◆参考サイト
- [Pythonで、pytzを使ったタイムゾーンの変換](https://blog.narito.ninja/detail/81/)

###②django.db.utils.OperationalError: could not translate host name "host" to address: Unknown host

####◆発生タイミング
python manage.py runserver実行時に発生。

####◆原因
開発用サーバーを立ち上げようとしたら、本番用のDB設定を参照しちゃったという事象でした。
下のコードは本番用のDB設定です。

```settings.py
#herokuデプロイ用のDB設定
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'name',
        'USER': 'user',
        'PASSWORD': '',
        'HOST': 'host',
        'PORT': '',
    }
}
```
エラー内容はつまり、「hostなんて知らねえよ」ということですね。

####◆解決方法

元々は開発環境用のDB設定（sqlite）が施されていたのですが、herokuデプロイ時にsettings.pyからは外してあります。
なので、ここでは開発環境用のDBを設定させる必要があります。

しかしsettings.pyは本番環境用に設定されているので、今回はsettings.pyと同階層のフォルダ内に__local_settings.py__を作成し、そこに開発環境用のDB設定を書いていきます。

```local_settings.py
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR + 'db.sqlite3',
    }
}

DEBUG = True
```
もちろんデバッグ設定もTrueにします。
そしてsettings.pyからlocal_settings.pyを読み込めるよう設定します。

```settings.py
try:
    from .local_settings import *
except ImportError:
    pass

if not DEBUG:
    SECRET_KEY = os.environ['SECRET_KEY']
    import django_heroku
    django_heroku.settings(locals())

```
tryでlocal_settings.pyの全てを読み込もうとしています。
local_settings.pyはgitの追跡対象外としていますので、もし本場環境で読み込もうとしてもインポートエラーが発生します。
しかし特に何をするわけでもなく、passでスルーさせます。

そして開発環境じゃない場合、本番環境用のDB設定を参照する仕組みとしています。
local_settings.pyは忘れないよう、ちゃんと.gitignoreに追加しましょう。

これで問題なく開発用サーバーは立ちあがり、本番ではちゃんとheroku用のDB設定を参照していました。

####◆参考サイト
- [Djangoのsettings.py周りのメモ](https://qiita.com/jp_ibis/items/0ac1ba8aba1398f2a492)


###③また新しいエラーが出たら追加します
CentOS 8からMIRACLE LINUXに移行するのはちょっと待って(2021-12-21現在)

2021-12-21現在の話です。

CentOS 8ユーザーの中でMIRACLE LINUXに移行しようと考えている方がいるかもしれませんが、MIRACLE LINUX 8.5が出るまで移行しない方が良いです。
remi-releaseがインストールできなくて死にます。やり方はあるかもしれませんが、普通にやるとOSのバージョンとして8.5を要求されてインストールできません。

年末までに移行がどうしても必要なら、Rocky LinuxかAlmaLinuxにするのが無難だと思います。
電光表示器制作

<h1>1.はじめに</h1>
株式会社エニプラでは、社内のチームごとにテーマを決め、
毎月行われるチーム会でテーマに沿った活動を行っています。
チーム会で行った内容は、年末に「エニプラアワード」という場で
全社員や来期の新卒に向けて発表を行っています。

今年度私たちは「電光表示器制作」をテーマに活動していきました。

制作学習のきっかけ
チーム学習を前提に皆で意見を出し合い、「電光表示器制作」に決定しました。
・RaspberryPiを弄ってみたい
・実機を触って何かやりたい
・Pythonを学びたい
・組み込み系に挑戦してみたい

<h1>2.スケジュール</h1>
以下のスケジュールでエニプラアワードに向けて取り組みました。

1月～3月
活動内容検討

4月～6月
機材、要件検討

7月～11月
チーム学習

11月
Qiita記事作成

12月
発表

7月にチーム全体でRaspberryPiの概要や注意事項、RaspberryPiで実現できることを学びました。
8月以降から2つのチームに分かれ、要件定義、スケジュール、資材調達、プログラミングなどを実施しました。

<h1>3.環境構築について</h1>
●本体構成
本体：Raspberry Pi Zero WH
<img width="300" alt="iOS の画像 (1).jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299143/cf53d710-5fde-afb0-6de6-16b9d0a44f38.jpeg">
OS：Raspbian


周辺機器：小型電光掲示板（8X32 ドットマトリクスLED）
<img width="300" alt="iOS の画像.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299143/3ebba084-d82d-25f0-05d2-537d5a9d325f.jpeg">

<h1>4.成果物</h1>
Pythonでプログラミングし以下の成果物を作成しました。
【Aチーム成果物】
①ターミナル画面で表示したい文字を入力する。
![無題.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299143/18e8f751-9507-e4e6-6d7c-9559f92f443e.png)



②電光掲示板に入力した文字がループし表示される。
![ラずパイ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299143/9cdb6618-bf57-16a9-7129-94fc4fa6762e.jpeg)

【Bチーム成果物】
①ターミナルで作成したプログラムを起動。
![Bターミナル.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299143/77a6c7d0-6be8-5d91-7575-14510a77a960.png)


②WEBニュースがスクロール表示される。
![Ba.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2299143/49c8279f-dda8-8738-3eb1-79927ef39713.png)



<h1>5.今後の挑戦</h1>
今後の活動で以下ことに挑戦したいと思います。

・電光掲示板にドット絵を表示させる
・電光掲示板に出力させる文字や機能（時計、ウェルカムボード、お知らせetc）をボタンで切り替える
・遠隔で操作できるようにする
・音や音楽を出力させる
M5Stack / M5Core2 + AMG8833 (サーマルカメラ) を試してみた。

# オリジナル
https://github.com/hkoffer/M5Stack-Thermal-Camera-

# 参考記事
https://iot.uc4.net/2020/06/01/m5stack-thermal-camera/

# 参考コード
https://github.com/m600x/M5Stack-Thermal-Camera

# 作成記事
https://macsbug.wordpress.com/2018/06/08/m5stack-thermal-camera-with-amg8833/
https://macsbug.wordpress.com/2018/09/08/m5stack-thermal-camera-part-2/

# 購入したセンサー
AliexpressでAMG8833を購入。
![AMG8833.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/eaffe548-8a96-61d3-d3fe-ab309e21e305.png)


# 動作確認
Arduino UNOで動作確認。I2Cアドレスは0x69だった。

ソースコードはこちら。

https://github.com/kitazaki/M5Stack_AMG883/blob/main/UNO_AMG8833_01.ino

# ビルドエラーが発生
```bash
fork/exec /Users/kitazaki/Library/Arduino15/packages/m5stack/tools/esptool_py/3.0.0/esptool.py: no such file or directory
ボードM5Stack-Core2に対するコンパイル時にエラーが発生しました。
```

解決方法
エディタで ~/Library/Arduino15/packages/m5stack/hardware/esp32/1.0.9/platform.txt を開いて esptool のコマンド名を変更する。

```bash
- tools.esptool_py.cmd=esptool.py
- tools.esptool_py.cmd.linux=esptool.py
- tools.esptool_py.cmd.windows=esptool.exe
+ tools.esptool_py.cmd=esptool
+ tools.esptool_py.cmd.linux=esptool
+ tools.esptool_py.cmd.windows=esptool
```

Arduino IDEを再起動。

https://chocoby.com/blog/2021/08/25/arduino-m5stack-esptool-py-error


# 参考記事
http://blog.livedoor.jp/nandemoke/archives/52246327.html
http://blog.livedoor.jp/nandemoke/archives/52246610.html


# 実際に動かしたコード
### M5Stack_AMG8833_02

M5Stack GreyとAMG8833で動作確認。(8x8マス表示)

![AMG8833_01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/c1b7645a-7d35-087d-7a51-2ee96ae2b1c1.jpeg)

ソースコードはこちら。

https://github.com/kitazaki/M5Stack_AMG883/blob/main/M5Stack_AMG8833_02.ino

### M5Stack_AMG8833_03

M5Stack GreyとAMG8833で動作確認。(8x8マスをバイリニアで画素補完表示)

![AMG8833.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/13ca49df-cac6-9ad6-0b57-8476bc062aa3.jpeg)

ソースコードはこちら。

https://github.com/kitazaki/M5Stack_AMG883/blob/main/M5Stack_AMG8833_03.ino

### M5Core2_AMG8833_02

M5Core2とAMG8833で動作確認。(8x8マス表示)

![M5Core2_AMG8833_02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/18dd8ca1-99d2-a0e1-c409-4a232db1479d.jpeg)

ソースコードはこちら。

https://github.com/kitazaki/M5Stack_AMG883/blob/main/M5Core2_AMG8833_02.ino

### M5Core2_AMG8833_03

M5Core2とAMG8833で動作確認。(8x8マスをバイリニアで画素補完表示)

![M5Core2_AMG8833_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/a66a8d76-f046-29a7-98ed-b2ac4e2ad9bb.jpeg)

ソースコードはこちら。

https://github.com/kitazaki/M5Stack_AMG883/blob/main/M5Core2_AMG8833_03.ino
[py2rb] re.finditer 2

# はじめに
移植やってます
# re.finditer (Python)
```python
import re

rule = r'([KR](?=[^P]))|((?<=W)K(?=P))|((?<=M)R(?=P))'
sequence = "MRGETGVSIKNPRPSRPFSCFWRKGDVENIRKSDIGNEKKIDAKFNRLQYNLYYKPLSHHKAGLLYKELFFRSCFSYTTCSLDFQGKRHQVERKAVDIVL"
print([x.end() for x in re.finditer(rule, sequence)])

# [2, 10, 23, 24, 31, 32, 39, 40, 44, 47, 61, 67, 72, 87, 88, 93, 94]
```

# どうする？ (Ruby)
```NG.rb
rule = /([KR](?=[^P]))|((?<=W)K(?=P))|((?<=M)R(?=P))/
sequence = "MRGETGVSIKNPRPSRPFSCFWRKGDVENIRKSDIGNEKKIDAKFNRLQYNLYYKPLSHHKAGLLYKELFFRSCFSYTTCSLDFQGKRHQVERKAVDIVL"

p sequence.scan(rule).flatten.compact.map{ sequence.index(_1) + _1.size }

# [2, 10, 2, 10, 2, 10, 10, 10, 10, 2, 10, 10, 2, 10, 2, 2, 10]
```
`scan`は正規表現ですが、`index`は単純な検索なんですね。

```NG.rb
rule = /([KR](?=[^P]))|((?<=W)K(?=P))|((?<=M)R(?=P))/
sequence = "MRGETGVSIKNPRPSRPFSCFWRKGDVENIRKSDIGNEKKIDAKFNRLQYNLYYKPLSHHKAGLLYKELFFRSCFSYTTCSLDFQGKRHQVERKAVDIVL"

a = []
sequence.scan(rule).flatten.compact.uniq.each do |k|
  sequence.size.times do |i|
    a << i + k.size if k == sequence[i, k.size]
  end
end

p a.sort

# [2, 10, 13, 16, 23, 24, 31, 32, 39, 40, 44, 47, 55, 61, 67, 72, 87, 88, 93, 94]
```
いい感じではありますが、正しい正規表現での結果とはなっていないようです。

```OK.rb
rule = /([KR](?=[^P]))|((?<=W)K(?=P))|((?<=M)R(?=P))/
rule2 = /^([KR](?=[^P]))|((?<=W)K(?=P))|((?<=M)R(?=P))/
sequence = "MRGETGVSIKNPRPSRPFSCFWRKGDVENIRKSDIGNEKKIDAKFNRLQYNLYYKPLSHHKAGLLYKELFFRSCFSYTTCSLDFQGKRHQVERKAVDIVL"

a = []
sequence.scan(rule).flatten.compact.uniq.each do |k|
  sequence.size.times do |i|
    a << i + k.size if sequence[i..-1].match(rule2)
  end
end

p a.uniq

# [2, 10, 23, 24, 31, 32, 39, 40, 44, 47, 61, 67, 72, 87, 88, 93, 94]
```
ちょっと重複がありますが、目的は達せられた。

```OK.rb
rule = [/^([KR](?=[^P]))/, /^((?<=W)K(?=P))/, /^((?<=M)R(?=P))/]
sequence = "MRGETGVSIKNPRPSRPFSCFWRKGDVENIRKSDIGNEKKIDAKFNRLQYNLYYKPLSHHKAGLLYKELFFRSCFSYTTCSLDFQGKRHQVERKAVDIVL"

a = []
sequence.size.times do |i|
  rule.each do |k|
      a << i + sequence[i..-1].scan(k)[0].size if sequence[i..-1].match(k)
  end
end

p a

# [2, 10, 23, 24, 31, 32, 39, 40, 44, 47, 61, 67, 72, 87, 88, 93, 94]
```
少しだけ、整理。
# メモ
+ Python の re.finditer を学習した
+ 道のりは遠そう
Reactでインスタ投稿を埋め込む方法（instagram graph api）

#はじめに
PHPを使用してinstagram graph apiで投稿をWebサイトに埋め込む系の記事はたくさんあるのですが、他の言語での記事があまりないため、今回はReactを使用してWebサイトにインスタの投稿を埋め込む処理を記載しようと思います。

#環境
|OS|使用言語|ライブラリ|使用環境|
|:---:|:---:|:---:|:---:|
|windows10|javascript|react|visual studio code|
・node.jsインストール済み
・create-react-appでベースとなるアプリ作成済み
・https://lpeg.info/webworks/instagram_api.html を参考にアクセストークンとビジネスアカウントIDを取得済み

※APIの通信はaxiosを使用します。
※フェイスブックとインスタグラムのアカウントが必要です。
※インスタグラムのアカウントはビジネスアカウントにする必要があります。
※フェイスブックでページを作成している必要があります。（自分はここで結構詰まりました）

#Reactでインスタの投稿を取得する

```react:App.js
import React, { useState, useEffect } from 'react'

const App = () => {
  const [posted, setPosted] = useState([])
  const [clickUpdate, setClickUpdate] = useState(false)

  const onClickUpdate = () => {
    setClickUpdate(!clickUpdate)
  }

  useEffect(() => {
    const user_name = "埋め込みたい人のユーザー名" //ビジネスorクリエイターアカウントの必要あり
    const access_token = "取得しておいたアクセストークン"
    const user_id = "取得しておいたビジネスアカウントID"
    const get_count = 5 //取得したい投稿数
    axios
      .get(
        `https://graph.facebook.com/v12.0/${user_id}?fields=business_discovery.username(${user_name}){id,followers_count,media_count,ig_id,media.limit(${get_count}){caption,media_url,like_count}}&access_token=${access_token}`
      )
      .then((res) => {
        setPosted(res.data)
      })
  }, [clickUpdate])

  return (
    <>
      {/*埋め込みたいインスタの情報*/}
      <button onClick={onClickUpdate}>更新</button>
    </>
  )
}

export default App
```
#内容
リクエストパラメータの中にある以下の記載で取得したい投稿内容を指定できます。

```
axios.get('省略・・・media.limit(${get_count}){caption,media_url,like_count}・・・省略')
```

今回は投稿内容と投稿画像といいね数を取得する記載となっています。
※ちなみにmediaの後の「limit(取得投稿数)」記載を削るとすべての投稿が取得できます。
#投稿内容を表示する例
画像を表示する例を以下に記載します。

```
return(
  <>
    <img src={posted.business_discovery.media.data[0].data['media_url']} alt='instagramImage' />
  </>
)
```

上記のような記載でdata配列で取得する要素を変更すると投稿内容であったりいいね数が取得できます。

※複数投稿を取得するように処理を記載している際は、ループ処理などでぐるぐる回して取得するのですが、今回は割愛させて頂きます。

#まとめ
今回は、インスタグラムの投稿内容をReactを使用してWebアプリに埋め込むことを記載しました。
ググるとPHPの記事が多く出回っており、ほかの言語での解説が少なかったため、本記事を少しでも活用して頂けると嬉しいです。
また気分が乗った際は、Webサイトからインスタグラムへ記事を投稿する内容を記載します。
少しでも役立った際はLGMTして頂けるとやる気アップします。。。笑

#参考にした記事
冒頭にも乗せさせていただいた以下の記事です。アクセストークンとビジネスアカウントIDを取得する方法がとても詳しく解説されており、トークン発行などがスムーズにできました。
・https://lpeg.info/webworks/instagram_api.html

あとは、instagram graph apiのリファレンスです。リクエストパラメータの記載例などが載っており活用しました。
・https://developers.facebook.com/docs/instagram-api/reference/ig-user/media#get-media

##注意
ビジネスアカウントであればWebサイトに埋め込むことができるのですが、他人のアカウントを埋め込む際はあらかじめ了承を得てから埋め込むようにしないと問題になるかもなので気を付けてください。。。💦
Former2を使う上で注意するポイント


これは「[弁護士ドットコム Advent Calendar 2021](https://qiita.com/advent-calendar/2021/bengo4com)」の 22 日目の記事です。

# はじめに
AWSに既存で構築しているインフラリソースをコードとして吐き出したいという需要があるかと思います。
その中でFormer2を使う選択をする方は多いのではないでしょうか？
私もこれまでFormer2を使用してCloudFormationやCDKを生成してきましたが、その中で注意するポイントがいくつか見えてきたので以下に記載します。

# ０. Former2とは
既存のAWSリソースからインフラコードを生成してくれるサードパーティのツールです。(AWSのサービスではない)
CloudFormation、CDKやTerraformのテンプレートを生成してくれます。
また、試験段階ではあるもののFormer2 CLIというコマンドライン上でFormer2を実行できるライブラリもあります。
https://former2.com/

# 1. 注意点
## 1.1 出力されるインフラコードを信頼しすぎないこと
Former2はあくまでサードパーティツールです。
そのため、リリースされたばかりののサービスのインフラコードを出力しようとすると対応していなかったり、対応していても不具合があったりします。
私の例で言えば手動で立てたOpenSearchのインフラリソースをコード生成した際に、重複するパラメータが出力される不具合に遭遇しました。
出力されるインフラコードが本当に正しいかは、常に確認を取るのが安全です。

### 1.1.1　Former2のインフラコードの確認方法
CloudFormationについては[cfn-lint](https://github.com/aws-cloudformation/cfn-lint)というlintツールがあります。
cfn-lintはCloudFormationテンプレートの構文が間違っていないかを確認することができます。
そのため、CloudFormationをデプロイする際のバリデーションエラーに遭遇する頻度を少なくすることができます。
cfn-lintはVScodeのプラグインとしても用意されていて、インストールするとプロパティ補完機能も使えます。
また、デプロイするCloudFormationテンプレートが何をAWS上に構築するかを確認する[Rain](https://github.com/aws-cloudformation/rain)を入れるのもオススメです。


## 1.2 アクセスキーの権限は必要最低限にすること
Former2を使用してAWS のリソースを取得するには IAM ユーザーのアクセスキーが必要になってきます。
公式ドキュメントでも書いていますが、Read Onlyの権限のみを渡すようにしてください。
アクセスキーをFormer2に渡すのが不安な方は、以下で公開されているリポジトリをクローンしてDockerを立ち上げることで、ローカルで起動することもできます。
https://github.com/iann0036/former2

## 1.3 CDKはLow Level Constructで生成される
Former2はCDKも生成することができますが、現在(2021年12月時点)の仕様だとLow Level Constructと呼ばれるライブラリで記述されたコードで出力されます。
しかし、CDKをよく使う方はご存知だとは思いますが、High Level Constructと呼ばれるライブラリを使う方が記述量も少なく済み、後にリファクタを進めていく上でもいいです。
また、Former2で生成されたCDKのコードはプログラミング的視点で見ると、冗長なものになっていることが多いので共通化するなどの工夫をした方が後々の運用を考えるといいです。
Former2から生成されたCDKをどう使うかにもよりますが、注意してください。

※Construct Libraryについては以下の記事で詳しく説明してくれています。
https://dev.classmethod.jp/articles/aws-cdk-construct-explanation/

# 3. まとめ
Former2はAWSが推奨しているツールとはいえあくまでサードパーティのツールです。
何かあった際に責任を取るのは使用した側になります。
便利なのは間違いありませんが、注意をして活用するようにしていきましょう。
Kubernetes the hard wayする ~1.Compute Resources展開編~

# はじめに
会社でCKAの奨励金が出るようになったので、取得のためにCKAの勉強をすることにしました。その一環でKubernetes the hard wayに取り組んでみようと思ったのですが、漫然とコマンドを打っていても勉強にならないだろうということで、各章で何をやっているのかをメモしながら一通りやってみました。その内容を投稿していきます。
本記事では元ドキュメントの「Provisioning Compute Resources」の部分をやっていきます。元ドキュメント及びコマンド等は以下のURLを参照のこと。
https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/03-compute-resources.md

# Networkの作成
Kubernetes the hard wayで使用するVPCネットワークをデプロイする。このVPCは`kubernetes-the-hard-way`という名前で、`kubernetes`という名前のsubnetを一つ含んでいる。このsubnetのIPアドレスレンジは`10.240.0.0/24`である。
次に、このVPC内部での通信を許可するFWルールを作成する。このFWルールでは、icmp、tcp、udpのあらゆる通信を許可する。次に、このVPCへ外部からSSH、ICMP、HTTPS(6443)の通信を許可するルールを作成する。

:::note warn
元のドキュメント上ではHTTPSと記載されているが、通常のHTTPSのポート(443)ではなく、kube-apiserverが通信を待ち受けるポート(6443)を開けていることに注意。
:::

最後に、kube-apiserver用のLBが使用するPublic IPアドレスを作成する。耐障害性を確保するため、kube-apiserverが動作するコントロールプレーンのnodeは三台作成し、このLBがアクセスを振り分ける。このLBが使用するためのIPアドレスを`kubernetes-the-hard-way`という名前で作成する。

:::note warn
このIPアドレスの名前はVPCや今後作成するGCEのタグなどと同じ名前である。今後何回かリソース名を指定してリソースの情報を取得する場面があるので、何のリソースの情報を取得しようとしているのか注意する。
:::

# GCE インスタンスの作成
コントロールプレーン用のGCEインスタンスを三台作成する。インスタンスの情報は以下の通り。

* インスタンス名: controller-{0,1,2}
* IPアドレス: 10.240.0.{10,11,12}
* タグ： `kubernetes-the-hard-way`,`controller`

次に、ワーカー用GCEインスタンスを三台作成する。インスタンスの情報は以下の通り。

* インスタンス名: worker-{0,1,2}
* IPアドレス: 10.240.0.{20,21,22}
* タグ: `kubernetes-the-hard-way`,`controller`
* metadata: pod-cidr=10.200.{0,1,2}.0/24

:::note info
ワーカー用ノードのmetadataは、各ノード上で動作するpodが使用するIPアドレスのレンジを示している。のちにkubeletを動作させる際にこの情報を参照する。
:::

# まとめ
kubernetes the hard wayで使用するネットワークとLBで使用するpublic ip、コントローラとワーカー用のGCEインスタンスを作成した。なお、作成したGCEインスタンスは放っておくと課金され続ける(しかも割と高い)ため、作業を中断する際は停止すること。
GASで推しのYouTubeチャンネルの更新をDiscordに通知する

[git]:https://github.com/Tkg-tamagohan/youtube2discord
[YTAPI]:https://developers.google.com/youtube/v3/getting-started?hl=ja
[GSS]:https://www.google.com/intl/ja_jp/sheets/about/
[query]:https://support.google.com/docs/answer/3093343?hl=ja

####この記事はGoogle Apps Script Advent Calendar 2021の21日目の記事です

https://qiita.com/advent-calendar/2021/google-apps-script

# GASで作った理由
YouTube APIがお手軽に扱えるから
Trigger機能がとても便利

# システム詳細
### Discordへの通知
webhookを利用

https://qiita.com/Eai/items/1165d08dce9f183eac74

### YouTubeの情報の取得
[YouTube Data API][YTAPI]とYouTubeのRSSを利用
YouTubeのRSSは以下のURLで取得できる

```
https://www.youtube.com/feeds/videos.xml?channel_id=CHANNEL_ID
```

https://developers.google.com/youtube/v3/getting-started?hl=ja

### 情報の集積
[Google Spread Sheet][GSS]を利用
チャンネルごとのシートに分けて管理

### ソースコード

https://github.com/Tkg-tamagohan/youtube2discord


# 中身の紹介
#### 取得したいチャンネルの管理
辞書形式でChannel IDとチャンネル名で格納しています．
チャンネル名はAPIで動画ごとに取得するとQuota[^quota]が無駄になるし，シート管理でも利用しているのでこんな感じです．

[^quota]: YouTube APIを利用するたびにかかるコスト https://developers.google.com/youtube/v3/getting-started?hl=ja#quota

```javascript:main.gs[5-9]
const channels ={
  'CHANNEL_ID_1': 'CHANNEL_NAME_1',
  'CHANNEL_ID_2': 'CHANNEL_NAME_2',
  'CHANNEL_ID_3': 'CHANNEL_NAME_3',
}
```

#### シート名をエスケープする理由
チャンネル名(= シート名)に`'`が入ってるとスプシの[QUERY関数][query]がお亡くなりになってしまう...

#### 複数のシートから検索する方法
先ほども登場した[QUERY関数][query]を利用しています．
なんといっても爆速なのが良いところ

http://pineplanter.moo.jp/non-it-salaryman/2021/06/03/gas-query/

複数のシートから検索する際はまずこんな感じに

```js:main.gs[97-99]
Object.values(channels).map((val)=>{
    return 'QUERY(\''+escape_html(val)+'\'!A:G, "select * offset 1", 0)'
  }).join(';')
```
`Object.values(channels)`はチャンネル名の配列で，それぞれのシートの見出し以外の部分をすべて取得するクエリ`QUERY('SHEET_NAME'!A:G, "select * offset 1", 0)`を`;`で接続した文字列を作ります．

これで全件取得したのであとは範囲を選択するところに`{}`で囲った上記の値を入れると検索できます．

```js:main.gs[101]
main_sheet.getRange(2,1).setValue('=QUERY({'+query_sheet+'}, "where Col1 is not null order by Col3 desc", 0)')
```
**注意事項**
全件そのまま取得したい場合は`where Col1 is not null`のように`select *`以外の何らかの条件を指定しないとうまく取得できなかったりする．
合成した仮想シートの行は`A, B, C, D...`ではなく`Col1, Col2, Col3, Col4...`となります．

他にコード見て質問あったらコメントでして下さいませ．

https://github.com/Tkg-tamagohan/youtube2discord/blob/main/main.gs

ついでに私の推し[癒月ちょこ先生](https://www.youtube.com/channel/UC1suqwovbL1kzsoaZgFZLKg)をよろしくお願いします．
Warning: unable to build chain to self-signed root for signer "XXX" のビルドエラーで実機ビルドできない場合の対処法

# 事象｜エラー内容

以下のようなエラーが出て実機ビルドができない場合の対処法についてです。
**結論として対症療法までしかわかっていないので、根本解決方法をご存知の方がいらっしゃったらコメントいただけると嬉しいです。**

`Command CodeSign failed with a nonzero exit code`
※このエラーはコード署名に関するもので、さまざまな原因で表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/ebe5db01-dd1a-896c-6140-7bb6f851d80b.png)

さらにビルドログを確認すると、`Warning: unable to build chain to self-signed root for signer "XXX"`と記載されており、コード署名がトラストチェーンを構築できていないことがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/506be34d-af8b-4ce8-6342-6f6f8f9ef8e4.png)

## トラストチェーンとは何か

トラストチェーンとは、コード署名を階層構造にして信頼性を担保する仕組みです。
トラストチェーンが構築できない場合、例えば中間者攻撃を受けてコード署名自体が攻撃者のものにすり替えられる可能性があります。

実機デバッグ時のトラストチェーンは以下のようになります。

**ルート認証局：Apple Root CA**
↓
**中間認証局：Apple World Developer Relations Certification Authority**
↓
**開発者：iPhone Developer: XXX XXX**
↓
**アプリケーション**

## キーチェーンアクセスを確認する

キーチェーンアクセスを確認すると、開発者証明書が信頼されていないことがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/5aadcffe-9c17-8104-4757-4b1b41d1183b.png)

※証明書を右クリックして「”XXX”を評価...」 > コード署名にチェック > 続ける をクリックして確認すると、**ルート証明書が見つかりません**という状態も確認できます。

## ルート証明書、中間証明書を確認する

ルート証明書、中間証明書をキーチェーンアクセスで確認しても問題ないことがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/3efdc6db-ba55-624a-16a5-c849cfa9141a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/5a9504ec-0aff-f6b2-dbf2-3f90274ec03a.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/b204f839-b575-00cd-9b35-1c63253698dd.png)
※私の環境では有効期限が2030年のものと2023年のものが2つありました。
※以下の公式ドキュメントにあるように、2030年のものと2023年のものが共存するのは問題ないようです。

 - https://developer.apple.com/support/expiration/
 - https://developer.apple.com/support/wwdr-intermediate-certificate/

もしもこの時点でルート証明書、中間証明書が含まれていない場合はAppleのサイトからダウンロードすることができます。

https://www.apple.com/certificateauthority/



## 原因

何らかの原因で開発者証明書が信頼されなくなっていることが直接原因ですが、なぜそうなったのか根本原因はわかっていません。

## 対処方法

1. **デフォルトキーチェーンの「ログイン」、システムキーチェーンの「システム」の両方**に以下の中間証明書が入っていることを確認する
   - Apple Worldwide Developer Relations Certification Authority（有効期限が2023年のもの）
   - Apple Worldwide Developer Relations Certification Authority（有効期限が2030年のもの）
1. もしも必要な中間証明書が入っていない場合はAppleのサイトからダウンロードして入れる https://www.apple.com/certificateauthority/
1. 証明書が信頼されていない場合は以下の手順で変更
   1. 開発者証明書を右クリックして**情報を見る**をクリック
   1. **信頼**をクリック
   1. **この証明書を使用するとき** の項目を**「常に信頼」**に変更
   1. **この証明書を使用するとき** の項目を**「システムデフォルトを使用」**に戻す


## 参考

 - https://developer.apple.com/support/expiration/
 - https://developer.apple.com/support/wwdr-intermediate-certificate/
 - https://engineering.linecorp.com/ja/blog/ios-code-signing/
 - https://developer.apple.com/forums/thread/86161
Laravelのログを一日より早くローテーションさせる

「[PONOS Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ponos)」22日目の記事です。
昨日は[@abcdeff](https://qiita.com/abcdeff)さんの記事でした。
# ログがスゴイ貯まる
最近Laravelのログがすぐインスタンスを圧迫するサービスが出てきたので、毎日ローテーションするかつ1日分のログしか残さない運用をしていました。
しばらくは順調だったのですが今度はローテーションする前に満杯になるようになってきました。
Laravelデフォルトのログ設定では`daily`しかない、困った。
というわけで時間毎にローテーションさせるための設定を作成しました。
# Hourlyローテーション
Laravelのログ設定で`daily`を選ぶと`Monolog\Handler\RotatingFileHandler`クラスが使われるので、これを少しいじって1時間毎にローテションされるようにします。
まずは`RotatingFileHandler.php`を適当な場所にコピーしてきて以下のように修正します。

```php:RotatingFileHandler.php
public function __construct(string $filename, int $maxFiles = 0, $level = Logger::DEBUG, bool $bubble = true, ?int $filePermission = null, bool $useLocking = false)
{
    ...
    $this->dataFormat = 'Y-m-d-H';
    ...
}
```

これだけです。かんたん！
あとは`config/logging.php`でこのハンドラーを使うチャンネルを追加して使用すればOKです。

```php:logging.php
return [
    ...
    'hourly' => [
        'driver' => 'monolog',
        'level' => 'debug',
        'handler' => RotatingFileHandler::class,
        'handler_with' => [
            'filename' => storage_path('logs/laravel.log'),
            'maxFiles' => 6,
        ],
    ],
    ...
];
```

これで1時間毎にローテーションされて過去6時間分しかログが残らないようになりました！

明日は[@honeniq](https://qiita.com/honeniq)さんの記事です。お楽しみに！！
探索的テストについて開発本部QAの意見を聞いてみた件


聞きたかったもの
・探索的テストをどう思っているのか
・探索的テストをどのタイミングで実施しているのか
・普段どんなものをしてるのか

- - -
<b>探索的テストをやったことある方たちの意見

Aさん
・タイミング
　ー　QA入る前にやった
　ー　QA完了後、時間足りなかった時に確認をする
・内容
既存の案件、他人の案件からのバグの照らし合わせ
ゲーム特有の知識必要


Kさん
・Aさんと似てるけど、
同じような観点で、別箇所でも確認
・機能と、観点を絞ってやってた、
観点は出たけど、優先度低い部分を別シートで整理して、それを実施
・実施者の時は手順通りじゃない確認をよくやってた
　・テストノートはないのでログが残らない


Iさん
・ケースからの脇道が多い
・スプリントだったので、時間が足らなかった
　・自分で設計し、自分で実施していた
・寄り道からのメモ、後輩と探索的テスト
　・画面、機能で分けてやってた
　・後輩に探索的テストの説明は、テスターちゃんを呼んできてってなった
 　　（師匠が大変喜んでおりました！）
・すごい大きな機能を一人でテストする
　・テストケースを実行することに無理を感じた時


A2さん
・Iさんと同じかな
　・仕様把握のために
・テーマを決めて確認

- - -

<b>探索的テスト未経験の方たちの意見

Sさん
・フリーでバックと探索的テストの違いがわかってよかった
・マインドマップを使っていきたい！！けど使えてないからやる


Eさん
・ケースはMECE完璧じゃないし、
　・探索的テストでどうか解決していくか
　・ケース進行中にメモ残して進む

K2さん
・実施中に気になることをメモして後で見ようとしてる
・機能回収が入ったらAd hocで大まかな確認をしてた
・大まかすぎて不安も感じてた

- - -

<b>共通的なみんなの探索的テストの認識

・Ad hoc
・深掘りするAd hoc
・気になるところを好きなだけみる
・テストケースで寄り道できなかった部分を確認する感じ

- - -
<b>私が普段使っている探索的テスト
ーセッションベースの探索的テスト、テストノート作成

セッションベースの探索的テストのメリット
・ログが残る
・集中力を最大限使う
・ちゃんと休む
・人の確認した内容がみれるので、観点を広げられる


セッションベース探索的テストのデメリット
・言語化が難しい
・ログを残そうとしたことによってテンポが崩れる

- - -
<b>雑談

ゲームQAと普通QAとの違い
・同期
・スキルが多いので組み合わせが大変
・アイテム関連のもの

- - -
<b>最後に

話の内容的にセッションベースの探索的テストはやっていないとのことで、
セッションベースの探索的テストができればと思います！
来年セッションベースの探索的テストについて座学（もしくはワークショップ）開催します！
UnityのLightAnchorについてもう少し詳しく

この記事はこの記事は[PONOS Advent Calendar 2021](https://qiita.com/advent-calendar/2021/ponos)の24日目の記事です。
昨日は@honeniqさんでした。

---

[6日目の記事](https://qiita.com/block/items/396525cddbf5c0044d58)で`LightAnchor`について触れましたが本当に簡単な紹介だったのでもう少し詳しく掘り下げてみようと思います。

## 見た目から照らす方向を選べる
コンポーネントの設定の中にアイコンがあるのでそれをクリックすることで簡単に照らす方向を設定できます。
そこから微調整ということもできるのでまずはこのアイコンで大まかな照らす方向を決めることができます。
![スクリーンショット 2021-12-21 15.20.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98727/0fd2d4ad-65f4-08a9-4c6a-f7fa590ec862.png)
![LightAnchor_2_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98727/4d3daee5-ee5b-2885-b51b-a17fdf510578.gif)

## SpotLight以外でも使える
例だとSpotLightでしか使えなさそうに見えますがそんなことはありません。
(下のgifはライトの種類によって`Intencity`を変更しています。

### DirectionalLight
![LightAnchor_2_2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98727/bf98911e-1b76-1d7c-54fe-7f12b5fa9610.gif)

### PointLight
![LightAnchor_2_3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98727/3fbd4526-b0d7-8588-ff92-d1f0af62dc5f.gif)

### AreaLight
AreaLightはbaked onlyなので照らす対象をstaticにする必要があります。
また毎回ベイクし直す必要があるためリアルタイムで確認することはできません。
![スクリーンショット 2021-12-21 15.40.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98727/62481057-b5a5-ee0e-18fa-3f33b0a26347.png)
![スクリーンショット 2021-12-21 15.51.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98727/04eb3b02-4570-302e-99b4-69229748c757.png)

### `Anchor Position Offset`で照らす位置をずらせる
`Anchor Position Offset`というパラメーターを変更することで照らす位置をAnchorから少しずらすことができます。
Offsetは`Anchor`となるオブジェクトのローカル座標方向にずれるようです。
![LightAnchor_2_4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98727/5e30472d-b55d-5d05-44f7-9cfa44071f9d.gif)

![LightAnchor_2_5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98727/0251fc8c-a179-17f0-59d7-31a6a07524cc.gif)

## まとめ
- `LightAnchor`はSpotLight以外でも使える
- `Anchor Position Offset`で照らす位置をずらせる

以上です。

---

明日も私(@block)です。
SQLiteのデータ型について

SQLiteのデータ型についてまとめておく。
SQLiteには、**格納される値のデータ型**と**カラムのデータ型**の2つが存在する。
多くのデータベースではカラムごとにデータ型を指定するため、格納できる値が決まっている。
しかし、SQLite3 ではテーブル作成時にカラムごとにデータ型を指定しなくてもよいため、カラムには色々な型の値が格納される。

#格納される値のデータ型
データ型を指定しなかった場合、カラムに格納された値を以下のデータ型で区別している。

|データ型 |説明|
|---  |---   |    
|INTEGER|符号付整数。自動選択か、1, 2, 4, 8バイトで格納|
|REAL|浮動小数点数。8バイトで格納|
|TEXT|テキスト。UTF-8, UTF-16BE, UTF-16-LEのいずれかで格納|
|BLOB|Binary Large OBject。入力データをそのまま格納|
|NULL|NULL値|

#カラムのデータ型
SQLite3において、カラムに指定できる型は以下である。
また、格納される値のデータ型によっては型変換されてから格納される。

|データ型 |説明|
|---  |---   |    
|INTEGER|整数で表されるREALやTEXTの場合、INTEGERに変換される|
|REAL|浮動小数点数で表せる場合、REALに変換される|
|TEXT|数値型(INTEGER、REAL)の場合、TEXTに変換される|
|BLOB|変換しない|
|NUMERIC|TEXT型の場合、INTEGERかREALに変換される<br>変換失敗の場合はTEXTのまま|

# 参考
[https://www.sqlite.org/datatype3.html](https://www.sqlite.org/datatype3.html)