OmniAICreator/Qiita-1.07M huggingface dataset:
非デザイナーのための、ちょっと得する「色」のこと

## はじめに
数年前から「デザインの民主化」「非デザイナー向けの〇〇」といったワードをよく見るようになりました。それに伴い、デザイナー不在でも制作ができる便利なツールも増えてきましたね。

ReactなどのUI制作に便利なライブラリが広く使われたり、Canvasを使ってのバナー制作を行う人も増えてきた印象があります。

一方で「たくさんあるテンプレートから何を基準に選べば良いのか分からない」「アレンジしたけど、なんだか上手くいかない」という人もいるのではないでしょうか。

「非デザイナー～」シリーズでは「知っているとちょっと得するデザインTIPS」や「話のネタになるデザイン豆知識」をお伝えしていこうと思います。

## 今回のテーマ「色」
デザインの構成要素にはいくつかの考え方がありますが、非デザイナーがUIやバナー制作で特に意識する必要があるのは以下の4つだと思います。
+ 色
+ フォント
+ オブジェクト（写真・図形）
+ レイアウト（構成）

今回はこの中の「色」をテーマに記事を書いていきたいと思います。

## 伝えたい相手
+ UIも担当するエンジニア
+ デザイン制作をする非デザイナー
+ 初心者UI・Webデザイナー
+ UI・Webデザイナーを目指す人

## 黒はNG
Webデザイナーの有名なセオリーとして「黒（#000）はNG」というものがあります。ここでは、その理由をご紹介します。
まずはこちらの画像をご覧ください。

![Qitta1-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656834/02d66848-adef-c37f-d5c2-fc6aab7e836e.png)

![Qitta1-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656834/7789d58f-4105-fa1b-76e3-562dcc425618.png)

比較してみると黒（♯000）を使った上の画像は、目がチカチカして少し読みにくいと思いませんか？
その理由は単純で「明度」の位置が真逆なんです。

明度？となると思うので、まずは「色の三属性」の説明をしますね。
色を構成する要素は「色相」「彩度」「明度」に分けられます。
+ 色相：赤、青、黄などの「色の違い」のこと
+ 彩度：「鮮やかさ」の度合いのこと
+ 明暗：色の「明るさ」のこと

![Qitta1-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656834/04c1f486-5eb1-1ee4-19e4-69a99b3b0c10.png)


先ほどの画像で使われていた「♯000」は最も黒い色、白は「#FFF」で最も白い色。つまり、最も暗い色と最も明るい色の組み合わせなのです。

人間の目には、見える光の量やピントを調整する機能があります。
極端に明暗が強いものを見ると、入ってくる光の量の調整が難しくなり、目への負担も大きく、結果的に読みにくい文字となるんですね。

もちろん、明暗を対極にすれば極端に目立ちます。一方で目への負担の高いチカチカした見た目になってしまうので、適度な範囲に押さえてあげましょう。

デザインの現場では黒（#000）を避けて、グレーを使うことが多いです。白（♯FFF）は使われることも多いですが、目の負荷が高いことは確かなので、少しだけ色をいれて、避けてあげた方が無難でしょう。

## 純色は避けよう
「白と黒の注意点は分かったけど他の色は？」となると思いますが、こちらも極端な色は避けた方が無難です。特に彩度と明度が100%になる「純色」には注意が必要です。純色は目への負担が高く、チカチカしてしまいますし、デザインで一番重要な「伝えたい内容」が伝わらなくなってしまいます。

## 色数は少なく
「明度や彩度は下げたけど、なんだか見えにくい気がする…」そんな場合もあると思います。そんな時は、色数を減らしてみましょう。

![Qitta1-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656834/136f903d-95af-8851-e3d4-3acc34838c40.png)


「3色にしぼる」こちらもデザインのセオリーとして良く使われる言葉です。
伝えたいメッセージ、演出したい雰囲気に合わせて3色を選び「70% : 20~25% : 10~5%」くらいののバランスで使うと、きれいにまとまりやすいと思います。

![Qitta1-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656834/3b8729c8-5d45-3d8c-017f-0b82598decaf.png)

## モノクロにしてみよう
「まだ、なんかイマイチだな...」というときはモノクロにしてみましょう。
良いデザインは明度のメリハリが効いているものです。

![Qitta1-6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656834/cb175099-fc30-69a1-cc27-c63b9cc22e54.png)

一度グレースケールにして確認することで、目立たせたい場所が目立っているか、見えにくい場所はないかなどを確認することができます。少し珍しい手段ですが、一度モノクロで作ってから色を載せるという手もあったりします。
（Chromeの拡張機能を使ってカラーの画像を白黒にして確認することができます）

## 最後に
以上、ちょっと得する「色」のことでした。
このテーマは本当に奥が深いので、もっとたくさんのTIPSがありますが、一度にたくさんご紹介するとお腹一杯になってしまうと思うので、次の機会とさせてください。

プロのデザイナーも頭を悩ませながら色を決めています。避けるべき強い色を、意図的に使う場合もありますし、印刷とWebでも色の扱いに違いが出てきます。

世の中にある制作物で使われている色を「これはどういう意図でやってるのだろ？」と考えたりすることで、新たな発見も多いと思いますので、ぜひやってみてください。
Electron + Vite + Tailwind CSS

ちょっとDesktopアプリを作る必要がでてきたのでElectronを触ってみた。

https://github.com/alex8088/electron-vite
https://developer.mamezou-tech.com/blogs/2023/05/22/electron-vite/

プロジェクトの作成
```
$ yarn create @quick-start/electron
$ cd プロジェクト名
$ yarn
$ yarn dev
```
TypeScriptのみNoにした

https://electron-vite.org/guide/

cssはTailwind CSSを使ってみる
```
$ yarn add -D tailwindcss
$ npx tailwindcss init
```
tailwind.config.jsのcontentを以下にする
```
['./src/renderer/src/**/*.{vue,js,ts,jsx,tsx}', './src/renderer/index.html'],
```
./src/renderer/src/assets/input.cssを作って中身は以下
```
@tailwind base;
@tailwind components;
@tailwind utilities;
```
./src/renderer/main.jsのcssを置き換え
```
import './assets/output.css'
```
ビルドしてWatch
```
$ npx tailwindcss -i ./src/renderer/src/assets/input.css -o ./src/renderer/src/assets/output.css --watch
```
ターミナルの別ウインドウを開いて起動
```
$ yarn dev
```
cssを変えたのでレイアウトが崩れているのがわかる
![スクリーンショット 2024-01-30 16.16.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56191/56737283-5a91-dcfd-db55-29e72a2dc63e.png)

./src/renderer/src/App.vueを編集してTailwindのクラスに変更する（例えばElectronロゴのimgタグのclassを'w-24 h-24 rounded-full mx-auto'とかにしてみる）
変更が反映されていれば導入完了。

がんばって書き換えてオリジナルに近づけてみた。
![スクリーンショット 2024-01-30 16.09.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56191/f77b347c-e3e2-48c2-716b-4429da55b49a.png)

最終的に作りたかったものはこれなのだがElectronのバグ？で目的を達成できなかった。何かというと[(続)SVG帳票](https://qiita.com/mackey1966/items/48790fb4ab799ac2fdec)の一連の流れでWebアプリからプリンタをコントロールしたかったのだ。ダークモードにも簡単に対応できたりして面白かったのだが残念。ネイティブで作るしかないのか。

![スクリーンショット 2024-02-14 12.03.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56191/64313e3a-c1be-4ae1-e5ba-df0333c4639d.png)

gitに残骸を上げときます。誰かわかる人がいたら教えてください。

https://github.com/mackey66/printer-api

---

その後、Windowsネイティブで作ってみた。WebView2にPDFを読み込んで印刷している。できないこともあるが一応思い描いてた動きはする（ブラウザからダイアログなしで印刷される様はちょっと新鮮かもしれない）。Edgeが入ってないと使えないのが難点だがまぁもういいでしょう。しかし、これのMac版も作るのか...。

![スクリーンショット 2024-03-19 9.40.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56191/fb4ff1f3-17df-3111-7278-cf2efcf12c78.png)

---

Mac版もかなり時間がかかったがなんとか目処がついた。情報が少ないのでChatGPT、Claude 3様さまだった。

![スクリーンショット 2024-03-19 9.34.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56191/22865644-8e04-492f-d081-d697eb4bb71c.png)
Python学習9日～モジュール～

## モジュール
プログラムを構成する部品のこと
Pythonでは作成したファイル(.pyファイル)のこと

## パッケージ
モジュールを詰め合わせたもの

## ライブラリ
組み込みではない(Python処理系に内蔵されていない)機能

### random
擬似乱数に関するモジュール

~~~Python
import random
random.randint(1,10)
~~~
~~~:実行結果
4
~~~

**randint関数**は指定した範囲でランダムに整数を生成してくれる関数

~~~Python
import random as r
~~~
モジュールに任意の名前をつけることができる
上記例ではrandomモジュールをrとしてimportしている

### from節
モジュール名なしで機能を使えるようにする

~~~Python
# 特定のモジュールだけを使えるようにする
from モジュール名 import 機能名,...

# モジュール内すべての機能を使えるようにする
from モジュール名 import *

# インポートした機能に別名をつける
# from モジュール名 import 機能名 as 別名
~~~

~~~Python:example.py
from random import randint as ri
ri(1,10)
~~~
~~~:実行結果
3
~~~

モジュールがパッケージ名を伴う場合にはfrom節付きのimport文を使って、パッケージ名だけを省略したり、パッケージ名、モジュール名の両方を省略することができる

~~~Python:example.py
# パッケージ名を省略してモジュールを使えるようにする
from urllib import parse
print(parse.urlparse('https//:www.python.org/'))

# パッケージ名とモジュール名を省略して機能を使えるようにする
from urllib.parse import urlparse
print(urlparse('https//:www.python.org/'))
~~~
~~~:実行結果
ParseResult(scheme='', netloc='', path='https//:www.python.org/', params='', query='', fragment='')
~~~
xargsでダブルクォーテーションが取り除かれていた

`xargs`はデフォルトだとダブルクォーテーションが取り除かれてしまうようです。
`-0`オプションをつけることで残りました。

```bash
ls -1 | grep -e /条件/ | cat -n | awk '<cp "xxx yyy.txt" zzz.txtみたいに出力>' ¥
| xargs -0 -i sh -c "{}"
```

また、最初`sh -c`を`eval`でできないかと試しましたが、組み込みコマンドらしく利用できないとか。

その他、

- `-t` 標準エラー出力に実行されてるコマンドを出力
- `-p` dryrunっぽい動き
- `-i placeholder` で省略可能なプレイスホルダー(省略時は{})
- `-I placeholder` は省略不可能なプレイスホルダー
ファイルシステムとは？

# どうにかしてディスクの使用率を下げる
df -hをして使用率を下げるべきファイルを探す。

/dev/mapper/centos-rootが97%になっている

# /dev/mapper/centos-rootとは何か?
### ファイルシステムである
OSの基本機能の一つだ。ファイルシステムは「ファイルをとり扱うための枠組みと方法」を指す。
ファイルシステムがないと、データを読み出すときにも「192588セクタと192589セクタのデータを取り出す」といったよくわからない指示をしなければならない。
セクタとはディスクの区画のことで、コンピュータはデータを取り出すときこれを見に行っている。しかし、数字での管理は人間にとってとてもわかりにくい。
ファイルシステムを使うと、「/etc/test.txtファイルを開く」などのように人間でもわかりやすく使えるようになる。
# 出典
https://eng-entrance.com/linux-make-filesystem#i-2:~:text=OS%E3%81%AE%E5%9F%BA%E6%9C%AC,%E3%82%88%E3%81%86%E3%81%AB%E3%81%AA%E3%82%8B%E3%80%82　　

ハードディスクやメモリーカードなどの記憶装置にデータを書き込む際に、データを管理する方式のことをファイルシステムと呼びます。
ハードディスクの中は、データを記録する部分がセクタと呼ばれる単位で細かく区切られています。
# 出典
https://www.rescue-center.jp/explanation/filesystem/index.html#:~:text=%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%A8%E3%81%AF,%E7%B4%B0%E3%81%8B%E3%81%8F%E5%8C%BA%E5%88%87%E3%82%89%E3%82%8C%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82

記憶装置の中にファイルを記録する仕組み。統一的なファイル入出力インタフェースを応用プログラムに提供する
# 出典
令和2年応用情報技術者合格教本　p236

## ファイルシステムの構造　
ファイルシステムの構造はディレクトリ構造を持った階層構造で、全てのファイルは一つの木構造で階層的に管理されています。階層構造の最上位にあるディレクトリをルートディレクトリといいルートディレクトリ、あるいは任意のディレクトリから全てのファイルがたどれるようになっています。
## 出典　
令和2年　応用情報技術者合格教本　p270

# df
ファイルシステムのディスク使用量を報告する
# 出典
man df
# du
ファイルのディスク使用量を推定する
# 出典
man du


# マウント位置以下のファイルのディスク使用量を調べる
```
du -sh /* | sort -r
```
-sで各引数の合計容量のみ表示 で一階層したの容量を調べることができる。

なんとか使用量を把握した

# 感想
まだ他にもいいスクリプリトがあると思う。
これから無駄なファイルを削除していく
ExcelのデータをSqlServerに楽々インサートする取り組み

## 目次
[こんなタスクがありました](こんなタスクがありました)
[pythonで自動化しよう](pythonで自動化しよう)
[下準備](下準備)
[コード](コード)
[まとめ](まとめ)

# こんなタスクがありました
もらったエクセルを元にしてDBにデータを追加するお仕事。
エクセルからコピペでSqlServerにデータを追加するという超アナログタスク。
コピペするにはDBのテーブルカラムと列を合わせる必要があるので
新しいエクセルシートを作成して列毎に情報を一つ一つセルに書き込む下準備が必要。
その上、定期的に仕事がやってくるのでメンドくさいorz

# pythonで自動化しよう
手動でエクセルを用意してデータ追加するのではなく、プログラムにやらせよう。
作業時間が大幅に短縮されるし、コピペミスも無くなる！

ということで openpyxl, sqlalchemy を使ってタスクの自動化をしていこう。
※openpyxlとは、PythonでExcelファイルを作成できる便利なツールである

# 下準備
もらったエクセル（例えばdata.xlsx）をデスクトップに置こう。
次に、デスクトップに auto_insert.py ファイルを作成しよう。
ファイルの中身に以下のコードを書いて保存しよう。
```auto_insert.py
print("Hello, World!")
```
次に、ターミナルを開いて「デスクトップ」ディレクトリに移動しよう。コマンドはこんな感じ。
```
~ $ cd Desktop
```
移動ができたら、以下を実行しよう！
```
~/Desktop $ python auto_insert.py
```
成功すればターミナルに Hello, World! が表示されるよ。
※エラーが出たりわからない人はChatGPTに質問しよう

# コード
それではauto_insert.py, models.py, database.pyの３つのファイルをデスクトップに作成しよう。
それぞれのファイルの中身は以下を書いてくれ。
```auto_insert.py
import openpyxl
from sqlalchemy import insert
from core.database import SessionLocal
from core.models import Fruits


# もらったExcelファイル(data.xlsx)の最初のシートを読み込む
input_excel_path = 'data.xlsx'
input_wb = openpyxl.load_workbook(input_excel_path)
input_sheet = input_wb.active

# DBにデータを追加する
fruits: List[Any] = []
# もらったExcelファイルの１行目にヘッダーがある場合を想定してmin_row=2にする
for row in input_sheet.iter_rows(min_row=2, values_only=True):
    insert_data = {
        "id": row[0],
        "name": row[1],
        "price": row[2],
        "pieces": row[3],
    }
    fruits.append(insert_data)

# バルクインサート
db = SessionLocal()
try:
    db.execute(insert(Fruits), fruits)
    db.commit()
except Exception as e:
    db.rollback()
finally:
    db.close()
```

```models.py
from sqlalchemy import VARCHAR, Integer
from sqlalchemy.orm import Mapped, mapped_column

from core.database import Base


class Fruits(Base):
    __tablename__ = "FRUITS_TBL"
    __table_args__ = {"extend_existing": True}

    id: Mapped[int] = mapped_column("ID", Integer, primary_key=True)
    name: Mapped[str] = mapped_column("NAME", VARCHAR(50))
    price: Mapped[str] = mapped_column("PRICE", VARCHAR(50))
    pieces: Mapped[str] = mapped_column("PIECES", VARCHAR(50))
```

```database.py
from sqlalchemy import create_engine
from sqlalchemy.engine import URL
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker


connection_url = URL.create(
    "mssql+pyodbc",
    username="DBのユーザ名",
    password="DBのパスワード",
    host="DBのアドレス",
    port="ポート",
    database="データベース名",
    query={
        "driver": "ODBC Driver 17 for SQL Server",
        "autocommit": "True",
        "LongAsMax": "Yes",
    },
)
engine = create_engine(
    connection_url,
    echo=True,
    connect_args={"TrustServerCertificate": "yes"},
    pool_pre_ping=True,
    pool_recycle=3600,
    pool_timeout=30,
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)


Base = declarative_base()

```

準備ができたらもう一度以下を実行してみよう。DBにデータが追加されれば無事成功だ！
```
~/Desktop $ python auto_insert.py
```
※openpyxl, sqlalchemyが見つからないエラーが出てきたら pip install でインストールしてあげよう

# まとめ
アナログタスクを自動化したおかげで生産性が爆上がりした。
DBへのデータ追加に2時間かかっていた(データが1万件ぐらいあった)のが1分でできるようになったよ。
ChatGPTを相手に英会話レッスンができるアプリを作成

# 英会話レッスンを（ほぼ）無料でいつでもやりたい！
英会話レッスンを受けたいとき、お金がかかり時間も拘束される。
そこで、ほとんど無料でいつでもpcブラウザやスマホで英会話レッスンができるアプリが欲しいと思い開発することにした。
ほとんど無料である理由は、ChatGPTを呼び出すためにOpenAIのAPIにクエリを投げる際にごく少額の料金がかかるからである。（だいたい１会話0.1円未満）

# アプリの構成
アプリはpcブラウザ、スマホからアクセスできアクセス元の環境に関係なく、前回までの会話を続けられるようにする。
会話の入力はpcブラウザ、スマホの音声入力APIを使って行い、入力に対するChatGPTの応答は音声読み上げAPIを使って読み上げることにより、会話ができる。
また、会話はルーム単位で管理する。
なお、今回は英会話レッスンをすることが目的であるため英会話のみに対応すれば十分であるが、音声入力APIと音声読み上げAPIは多くの言語に対応しているため、中国語、フランス語、スペイン語などAPIが対応している言語のレッスンも本アプリでは行うことができる。（ルーム単位で設定言語を変更できる）

# TL;DR
1. APIサーバーとクライアントを作成する(go,react)
2. ios,androidアプリを作成する(react-native)
3. Dockerコンテナ化する

### リポジトリ
[github](https://github.com/hellotaro/gpteacher)
- server: OpenAIのAPIとやりとりするサーバー
- client: ブラウザ版のクライアント
- phoneapp: ios, android版のクライアント
- infra: docker-composeファイル

# 1.APIサーバーとクライアントを作成する
OpenAIのChatGPT APIにサーバーから会話データを投げ、応答を取得する。そのためにはOpenAIのアカウントを作り、APIキーをあらかじめ取得しておく必要がある。APIキーを取得したら、以下ファイルの```==[Write your key]==```に自分のキーを指定する。
```
server/chatgpt/ChatGPTBot.go
```

サーバー内でルームとしてChatGPTとのトーク履歴を管理しており、ユーザはルームに入ってChatGPTと会話できる。
サーバーはserverディレクトリ以下で```go run main.go```により立ち上がり```http://localhost:8080```にアクセスすると以下画面が表示される。

![スクリーンショット 2024-01-23 14.02.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517503/221a23a5-2830-99df-fb3d-2c999335cb5c.png)

クライアントはReactで作成した。
右下の「S2T」ボタンを押すと、初回はブラウザからマイクの使用を許可するか聞かれるので許可する。すると、S2Tのボタンが赤色になり音声受付状態になる。そこで英語を話すと音声認識により話した英語がフォームに入力される。
意図した英語が入力されたら「Send」ボタンを押すことで、ChatGPTに会話が投げられる。そして、ChatGPTからの返答が返ってくると英語の音声で読み上げられることで、英会話を続けられる。
なお、「Interact」モードを選択することで、S2T→Sendというボタンを押すことなく会話を継続でき、より自然な英会話に近づけることができる。
(以下は中国語。言語を切り替えれば中国語やフランス語、スペイン語なども会話可能)
![スクリーンショット 2024-01-23 14.09.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517503/bc48e1c5-9888-ad58-563a-54621afeecfa.png)

# 2. ios,androidアプリを作成する
今回、ブラウザのクライアントを作成することでios,androidのブラウザからアクセスすればスマホからも英会話レッスンを受けられる予定であったが、iosのgoogle chromeが音声認識に対応していないことが判明し、androidのみの対応となってしまった。
そこで、ReactNativeでios,androidアプリをネイティブアプリとして作成することにした。起動するにはxcode,android studioをインストールし、適宜エミュレータを導入する必要がある。
serverをローカルで起動して、phoneappディレクトリで```npm run ios```を入力することでiosアプリが起動する。
右下の「Voice」ボタンを押すことで音声入力が開始する。終了したら再度ボタンを押し音声入力を終了して、「Send」ボタンを押すことで会話をサーバーに送る。

- 起動直後の画面。会話を選択
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517503/290ffda8-6af4-39c9-07da-99450219043a.png" width=50%>

- ルームを選択した時のルーム画面。ルーム名を選択するとルームの言語とプロンプト設定が行える。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517503/16c0802a-9632-19c0-2046-c5e93b9e3943.png" width=50%>

# 3. Dockerコンテナ化する
最後にサーバーをコンテナ化してdockerなどのコンテナランタイム上で動作するようにする。
とは言っても、Dockerfileを作成しただけであるので開発としてはおまけである。
server/Dockerfileにマルチステージビルドでサーバーをコンテナとしてデプロイするファイルがあり、こちらをbuildすることでコンテナを作成することができる。
[備忘録]JavaScriptのオブジェクトの共有渡しについて

## JavaScriptの共有渡しについて
少ししたら忘れてしまいそうなので、忘れないうちに自分なりにまとめておきます。JS初心者にとって少し混乱する内容だと思います。頑張って理解しましょう！

## コードで説明
memoryは環境変数として宣言しているとします。これはメモリ内を模したオブジェクトだと思ってくれて大丈夫です。
```js
/**
 *  初期のメモリ構造
 *  memory
 *  {
 *      a: undefined,
 *      b: undefined
 *  }
 *  これから使うa, bという値はわかりやすくするために入れてあります。本来は何もありません
 */
 
const a = [1, 2, 3, 4, 5]; 
// memory内に新しい値を追加し、[1, 2, 3, 4, 5]を代入、その座標をaに代入する。
// ここではmemory[value]とする

/**
 *  この時点でのmemoryの構造
 *  memory
 *  {
 *      value: [1, 2, 3, 4, 5],
 *      a: memory[value],
 *      b: undefined
 *  }
 */

 
const b = a;
// 変数aに代入されてるmemory[value]の座標を変数bに代入する。値(value)はコピーされない。

/**
 *  memory
 *  {
 *      value: [1, 2, 3, 4, 5],
 *      a: memory[value],
 *      b: memory[value]
 *  }
 */

modifyArray(a);
// 変数aに代入されているmemory[value]の座標を関数に渡す。

/**
 *  この時点でのmemoryの構造
 *  memory
 *  {
 *      value: [1, 2, 3, 4, 5, 100],
 *      a: memory[value],
 *      b: memory[value]
 *  }
 */

  function modifyArray(array) {
    // ここで新しく関数用の変数であるarrayをメモリ内に宣言し、渡された値を代入する
    // この関数に渡されたのはmemory[a]だが、座標はmemory[value]を指しているため、
    // (memory[array] = memory[a]) == (memory[array] = memory[value])となる。
    
    array.push(100);
    // ここで実行されているのはmemory[array].push(100)であり、
    // memory[array]の値はmemory[value]のため、memory[value].push(100)となり、
    // memory[value]に100が追加される。

    /**
     *  この時点でのmemoryの構造
     *  memory
     *  {
     *      value: [1, 2, 3, 4, 5, 100],
     *      a: memory[value],
     *      b: memory[value],
     *      array: memory[value]
     *  }
     */

    /**
      *  ここでarray = [1, 2, 3]のような値の再代入を行った場合は
      *  memory
      *  {
      *      value: [1, 2, 3, 4, 5, 100],
      *      newValue: [1, 2, 3],
      *      a: memory[value],
      *      b: memory[value],
      *      array: memory[newValue]
      *  }
      *  このようにメモリ内に新しい値(ここではnewValueとする)が追加され、
      *  関数内のarray(memory[array])の参照先がmemory[newValue]に変わり、
      *  arrayの中身を再代入後から編集してもmemory[newValue]の値が編集されるだけなので
      *  元のmemory[value]には何も影響がありません。
      */
}
 
console.log(a); // memory[a]の値を表示(memory[value]: [1, 2, 3, 4, 5, 100])

console.log(b); // memory[b]の値を表示(memory[value]: [1, 2, 3, 4, 5, 100])
/**
 *  この時点でのmemoryの構造
 *  memory
 *  {
 *      value: [1, 2, 3, 4, 5, 100],
 *      a: memory[value],
 *      b: memory[value],
 *  }
 *  JavaScriptは、到達不可になった値はメモリから自動的に削除される機能(通称: GC)があるので、
 *  メモリ内のarrayという値は削除されます。
 *  GCはガベージ・コレクションの略です。
 */
```

## まとめ
共有渡しとは **"メモリの座標を渡すだけ"** っていうシンプルな仕組みです。配列やオブジェクトを共有するというそのままの意味なのですが、コードを脳死で書いているとこのような仕組みを忘れて「なんだこれ！？なんか値が変化してる！？」となりそうなので、一応頭の中に入れておきましょう。ちなみにこのコードの中身が100%合っているという保証はないですが、大体このような感じだと思ってくれれば大丈夫だと思います。
[Flutter 3.16対応]WillPopScopeからPopScopeに変えるときの注意点

# まえおき

Flutter3.16対応時に`WillPopScope`がdeprecatedになるため、今後は`PopScope`を使う必要があり、その際に1点罠にハマったのでメモ


# 概要

`PopScope`の`onPopInvoked`内で`Navigator.of(context).pop();`を使用すると`onPopInvoked`が呼ばれてしまいます。

# 例

```dart
PopScope(
    canPop: false,
    onPopInvoked: (didPop) async {
        if (await webViewController.canGoBack()) {
            await controller.goBack();
        } else {
            if (context.mounted) {
                Navigator.of(context).pop();
            }
        }
  }...
);
```

# 対策

popが行われるさいはdidPopがtrueになるので、canPopを常にfalseにしている場合は、didPopがtrueの場合は処理を行わないようにすれば回避できます。

```dart
PopScope(
    canPop: false,
    onPopInvoked: (didPop) async {
        if (didPop) {
            return;
        }
        if (await controller.canGoBack()) {
            await controller.goBack();
        } else {
            if (context.mounted) {
                Navigator.of(context).pop();
            }
        }
  }...
);
```

# 感想

そもそもWillPopScope時のコードをそのまま使っているのが悪い気がするので、PopScopeに合わせて書き直した方がいいのかなと思います。

# 追記(2024/02/01)

iOSにてcanPopをfalseにするとback gestureがうまく動かないようです。

> PopScope does not invoke `onPopInvoked` for iOS back gesture when `canPop` set to false · Issue #138624 · flutter/flutter
https://github.com/flutter/flutter/issues/138624
AWS SES 検証済みIDの登録時に若干詰まってヘルプを投げたら自分のせいだった

## 概要

SESで検証済みIDを設定後、24時間以上経過しても「IDステータス」が`エラー`になっていたため、大先輩のインフラエンジニアさんに泣きついたら自分のせいだった話を簡単にまとめる。

## ハイライト

1. 金曜日
    1. SESにてドメインを検証済みIDに追加した。DKIMのCNAMEレコードが吐き出されていたが、退勤間際だったので放置した。
    1. 検証済みIDの「IDステータス」が`保留中`で「DKIMの設定」が`保留中`となっていた。※おそらく画面リロードしたら両方ともエラーになっていたと思われる。
1. 月曜日
    1. Route53を操作する権限を持っている方にお願いしてDKIMのCNAMEレコードを当該ドメインのホストゾーンに追加してもらった。

## 問題発生

1. 当該ドメインのホストゾーンにCNAMEレコードを追加してもらってから24h経過しても検証中IDの「IDステータス」と「DKIMの設定」がエラーになっている。
1. 最大72hかかる可能性があると書いてあったが、控えている作業もあったのでちょっと調べて大先輩インフラエンジニアさんに泣きついた。
※このとき検証済みIDのDKIMに「再検証」のボタンがあることは完全に見逃しており、存在に気がついていない。

## 解消までの道のり（インフラエンジニアさんとともに歩んだ道）

1. インフラエンジニアさん宛にAWSから「3日前からDKIMの検証ができず、検証エラーになっている旨」のメールが届いていることが共有いただけた。
1. SESが発行したCNAMEレコードが正しく設定されているか確認した。
    1. 下記コマンドをターミナルで実行し

        ```shell
        dig SESの「DNSレコードの発行」の名前の部分
        ```

    1. レスポンスのANSWER SECTIONの最後のフィールドを確認し、SESが自動発行したDNSレコードの「名前」と「数値」が一致しているか3レコード分確認した。
1. SESの検証済みIDの詳細画面の認証タブのDKIMのフィールドに「再検証」のボタンがあることを発見、押下してみた。
1. 数分後、画面をリロードしてみると「IDステータス」が`検証済み`に、「DKIMの設定」が`成功`になっていた。
1. インフラエンジニアさん宛にAWSから「DKIMの認証に成功したよ。おめでとう。」というメールが届いた旨を共有いただいた。

## 原因

自分が検証済みIDを登録後、すぐに当該ドメインホストゾーンにCNAMEレコード追加の作業を行っていれば今回のようなことにはならなかったはずである。
更に「再検証」のボタンを発見できれば大事にせずに済んだ。すみません。よく周りを見るようにします。

## 参考文献

https://docs.aws.amazon.com/ses/latest/dg/troubleshoot-verification.html
結局DKIMってなんなのさ

## 概要

DKIMについて簡単にまとめる。

## DKIMって？

一言でいうと「メールの送信者が正当かどうかを受信者に証明するために仕組み」である。
フィッシングメールやスパムメールなどの不正メールを防ぐのに一役かっている。
送信者はメールに伝書署名を付与、受信者はDNSに登録されている公開キーを使って付与署名を検証する。
公開キーは当該のメールドメインのTXTレコードかCNAMEレコードに保存されている。
主な仕組みは異なるが、イメージとしてはSSHキーの認証に近い。

実際DKIMの署名を付与しなくてもメールは送信できるらしい。メール送信の必須情報ではない。
ただしDKIMの署名を付与しないともちろん、フィッシングやスパムとして疑われ、受信者のメールクライアントがブロックしてしまう可能性があったり、自身は悪意がなくても送ったメールが改ざんされ受信者が被害を被るかもしれない。これが個人間のメールならまだ傷は浅いかもしれない（浅くても絶対NGだ）が、企業から送信されたメールが改ざんされユーザーが被害を被るなどした場合社会的責任や信用の失墜などの影響がある。
そんなに難しい設定ではなさそうなのでこれからは何も考えず「DKIM署名は付与するもの」として考えたほうが良い気がする。
【Splunk】SPL言語_高頻度_コマンド集2_dedup_fillnull_lookup


# 目次
__[・dedup](https://qiita.com/mizushino/items/5c5b6c66c63ceb067038#dedup%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89)__
__[・fillnull](https://qiita.com/mizushino/items/5c5b6c66c63ceb067038#fillnull%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89)__
__[・lookup](https://qiita.com/mizushino/items/5c5b6c66c63ceb067038#lookup%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89)__

# SPLコマンド一覧
## ▼dedupコマンド
```php:dedup
・・・ | dedup <重複削除したいフィールド名>
```

・サーチ結果の重複を削除する。

```php:dedup_例1_statusフィールドをユニークにして、テーブル表示
| dedup status
| table status
```
・dedup_例1_結果
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501557/601f3f51-0524-95af-5999-7f5e4a49c2f5.png)

```php:dedup_例2_複数フィールドを指定、フィールドの組み合わせ単位でユニークなものをテーブル表示
| dedup http_method,status
| table http_method,status
```

## ▼fillnullコマンド
```php:fillnull
・・・ | fillnull VALUE="値"
```
・NULLを指定の値に置換する
・VALUEで値指定しない場合は、「0」となる

```php:fillnull_例1_NULLをNONEに値指定する
| fillnull VALUE="NONE"
| chart COUNT BY Country ,status 
```
・fillnull_例1_結果
![fillfull_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501557/0a8d5bcf-47d6-6f11-86f2-69ca6d5dbf6c.png)


```php:fillnull_例2_NULLの値指定をしない
| fillnull status
| chart COUNT BY Country ,status 
```
・fillnull_例2_結果
![fillfull_1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501557/6810f54d-a544-32c2-dd3f-432cdf1ae8e9.png)

__・例2ポイント：__
NULLの値指定をしない場合、「0」で表示される

## ▼lookupコマンド
```php:lookup
・・・ | lookup <Lookup Table名> <☆Lookup フィールド名> AS <☆と紐づく指定したサーチ上でのフィールド名> OUTPUT <Lookupの出力したい項目>
```
・Splunkに登録されているLookupファイル(外部参照)を参照して、特定のフィールドを読み込む。
・ルックアップ用のcsvファイルを作成して、Splunkにアップロードされているのが条件

<br>
・Lookup_例1_前提条件

![元DB.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501557/18f4d5c8-7eaf-e1ab-d48a-5c87786f6ccd.png)

```php:lookup_例1_sample.csv(Lookupファイル)とstatus_serverを紐づけて、resultフィールドを抽出する。
| index=status_server
| lookup sample.csv status OUTPUT result
| table status ,result
```
__・例1ポイント：__
LookupTableのフィールド名と紐づくサーチ文のフィールド名が一致している場合は「AS」を省略することができる。

・Lookup_例1_結果
![Lookup.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501557/aa5deabe-f4f5-6d6a-689a-0c2e934b5ed6.png)

<br>
・Lookup_例2_前提条件

![DB_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501557/920c31eb-7c2c-aab0-f115-755662895b37.png)

```php:lookup_例2_sample.csv(Lookupファイル)とstatus_serverを指定したサーチ文のフィールド名と紐づけて、resultフィールドを抽出する。
| index=status_server
| lookup sample.csv ステータス AS status OUTPUT result
| table status ,result ,Country
```

・Lookup_例2_結果
![result_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501557/d25f0189-f1d4-f883-476c-1a402a8fa91c.png)
TiDB 入門

# TiDBとは？
「TiDBはMySQL互換のスケーラブルなデータベースです。」[(引用)](https://pingcap.co.jp/tidb-overview/)
TiDBは既存のMySQLにはないNoSQLのようなスケーラビリティーや便利性を追求したRDBMSです。
[-> 詳しくは Sakura Internet の記事をご覧ください。](https://knowledge.sakura.ad.jp/29695/)

# TiDBの仕組み
TiDBには大まかに３つのコンポーネントがあります。[(引用)](https://knowledge.sakura.ad.jp/29695/)
- **PD**
  - クラスターのマスター的なコンポーネント
  - データの配置決めなど
  - トランザクションの管理など
- **TiDB**
  - ここでのTiDBはコンポーネントの名前
  - SQLの解釈と処理  
- **TiKV/TiFlash**
    - レコードやインデックスを Key/Value として保存
# 構成
![tidb-sakura-iaas.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1589371/5ab5b8c0-7946-d3f9-038c-904aa2f3ef6e.png)

# TiDB構築
> ## 1. `tiup` インストール
> `tiup` のインストールはTiDBの管理用の端末に行ってください。
> クラスターのノードに `tiup` を入れておく必要はありません。
> ```bash
> #!/bin/fish
> curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh
> fish_add_path ~/.tiup/bin/
> tiup completions fish > ~/.config/fish/completions/tiup.fish
> ```

> ## 2. ssh鍵をクラスターのノードに登録
> クラスターの全てのノードに登録。
> ```bash
> #!/bin/fish
> ssh-keygen -t ed25519
> ssh-copy-id -i ~/.ssh/id_ed25519.pub [リモートユーザー]@[リモートサーバーのホスト名]
> ```

> ## 3. `tiup` でデプロイ
> ```bash
> #!/bin/fish
> ## Install cluster component
> tiup cluster
> 
> ## Update component
> tiup update --self && tiup update cluster
> 
> ## Check current version
> tiup --binary cluster
>
> ## Create cluster topology file
> tiup cluster template > topology.yaml
> ```
> `topology.yaml` は下記のように記述し、適切に編集してください。
> ```yml
> global:
>   user: "tidb"
>   ssh_port: 22
>   deploy_dir: "/tidb-deploy"
>   data_dir: "/tidb-data"
>   listen_host: 10.0.1.10
> server_configs: {}
> pd_servers:
>   - host: 10.0.1.10
> tidb_servers:
>   - host: 10.0.1.10
> tikv_servers:
>   - host: 10.0.1.11
>   - host: 10.0.1.12
>   - host: 10.0.1.13
> monitoring_servers:
>   - host: 10.0.1.10
> grafana_servers:
>   - host: 10.0.1.10
> alertmanager_servers:
>   - host: 10.0.1.10
> ```
> ```bash
> #!/bin/bash
> export SSH_USER=ubuntu
> export SSH_IDF="~/.ssh/id_ed25519"
> tiup cluster deploy tidb v7.5.0 ./topology.yaml --user $SSH_USER -i $SSH_IDF
> tiup cluster start tidb
> ## `--init`　を追加するとrootにパスワードが付与されます
> ```

> ### これで完了！
（ワークショップ用手順書) _3D Gaussian Splatting

## 1.PolyComを開く
https://poly.cam/captures


## 2.LumaAIを開く

https://lumalabs.ai/





## 2.「Create Capture」を押下
![スクリーンショット 2024-01-30 13.28.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/eafcc62e-3be1-3594-ed71-05021266f598.png)

## 3.「Create a Gaussian Splat」を選択
![スクリーンショット 2024-01-30 13.29.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/7be88a44-6009-fc98-8e23-e934da8e4e76.png)

## 4.データをアップロード
![スクリーンショット 2024-01-30 13.29.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/29751d7c-496b-6d33-de50-8ccd449a0600.png)

## 5.タイトルを入れる
![スクリーンショット 2024-01-30 13.29.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/539bed35-2c94-eaeb-19f4-a1dd49cae673.png)

## 6.





# 動画を撮影

サンプルとして博物館にあった化石をぐるっと一周撮影

# LumaAIにアップロード

![スクリーンショット 2024-02-14 14.08.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/2439015f-af65-566f-54ad-2370b03e590a.png)


https://lumalabs.ai/capture/e977c90d-9ea9-4d68-a36d-532aee394ead


# Polycamにアップロード

![スクリーンショット 2024-02-14 14.07.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/2e3466ae-6692-4fa9-c393-f686909a62e0.png)


https://poly.cam/capture/57a49f8a-3a53-419a-976f-6c12245cd3f2

# Blenderに読み込み

![スクリーンショット 2024-02-14 14.09.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/d0f26f89-9109-4634-5732-502d7c8afba4.png)





#　参考

https://scaniverse.com/support/no-lidar-no-problem

https://www.rsj.or.jp/content/files/event/international/iros16tutorial_2.pdf

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


|名前  |win/mac  |有料/無料  |URL  |
|---|---|---|---|
|photocatch  |ios,mac  |無料  | https://www.photocatch.app/ |
|metashepe（旧photoscan）  |windows  |  | https://kuu-satsu.com/software/photoscan/ |
|reality capture  |windows  |  | https://kuu-satsu.com/software/reality-capture/ |
|3DF Zephyr  |windows | | https://www.3dflow.net/3df-zephyr-free/  |
|Meshroom  |windows |  | https://alicevision.org/#meshroom  |
|Apple Object Capture | windows  | | https://alicevision.org/#meshroom  |

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

有名なのは、2020年7月に設立された非営利団体ALICEVISION協会が開発したMeshroomというソフト。ドローンで撮影した空撮ビデオなどの写真から3D土地モデルが描かれている映像などが公開されており、AWS IoT TwinMakerの中でも利用されている
https://aws.amazon.com/jp/blogs/news/building-a-digital-twin-with-photogrammetry-and-aws-iot-twinmaker/

https://www.dronedeploy.com/product/photogrammetry/

https://www.reddit.com/r/photogrammetry/comments/secraw/big_comparision_photocatch_vs_meshroom_vs/

無料で写真から3Dプリント出力可能なモデルを生成できるオープンソースの3D再構築ソフト「Meshroom」
https://gigazine.net/news/20220503-alicevision-meshroom/

ObjectCaptureの使い方
https://lilea.net/lab/how-to-use-object-capture/
ポートフォワードのわからなかったところ

# はじめに

先日はじめてローカルポートフォワードなるものの存在を知りました。ほかにもssh configやviなど世の中にはまだまだ分からないことがたくさんです。

今回はポートフォワードについてまとめようかと思います。

## ポートフォワーディング
そもそもポートフォワーディングって何？というところではあるんですが、ググったうえでの辞書的な説明だとこうです。
>ポートフォワーディング（port forwarding）とは、IPネットワーク上のある機器が、自らのIPアドレスのTCPやUDPの特定のポート番号への通信を、別のアドレスの特定のポートへ自動的に転送すること。また、ネットワーク機器などの持つそのような機能。

説明を読むとまあ何となくわかった風になれるやつですね。ssh接続のポートフォワーディングにおいて、接続するIPを限定することでセキュリティ対策となるようです。まさかそれ二種類があるなんて思いもしませんでした。

## ローカルポートフォワード
おそらく一番身近な存在なのかと思います。
ローカルの自分で指定したポートにアクセスすると接続したい対象の指定したポートにアクセスできる仕組みです。

```
ssh -L {ローカルのPort}:{host名}:{接続したい対象のPort} {接続先ユーザー名}@{接続先のhost}
```
たとえば
```
ssh -L 8008:localhost:80 target_user@sample_target.jp
```
のようになります。


## リモートポートフォワード
ローカルポートフォワーディングを調べる中で初めて知ったし、使ったこともないです。詳しい人、こいつを使う状況とか教えてください。
内容としてはローカルの逆で、リモートからローカルの指定したポートのにアクセスできるようにする仕組みです。

```
ssh -R {ローカルのPort}:{host名}:{接続したい対象のPort} {接続先ユーザー名}@{接続先のhost}
```
コマンドでの差はオプションが**L**と**R**ですかね

## ダイナミックポートフォワード
ssh接続したい対象が特定のネットワークからのアクセスのみ許可してるときによく登場します。
踏み台サーバーにssh接続して、踏み台のあるネットワーク内であればどこでも接続できます見たいなやり方ですね。
```
ssh -D {ローカルのPort} {接続先(踏み台)のユーザー名}@{接続先(踏み台)のhost}
```
正直最初は同ネットワーク内に構築されているという前提を知らず、「踏み台にしかアクセスしてないのに、なんで他のPortにアクセスできるんだ？」とずっと悩んでました。

## おわりに
キーの指定したかったらオプションで-iだよとか、hostのところはIPでもいいよ、とか様々細かいやり方あるんだなと思いました。備忘録。

## 参考
[SSHダイナミックポートフォワーディングについて知識整理してみた](https://qiita.com/Stier56/items/6a95306b7c8cddfa9678)
[ssh接続のポートフォワーディングは3種類ある！](https://tech-lab.sios.jp/archives/37377#i-3)
Amazon EventBridgeスケジューラを再度有効化する時の注意

一時的に無効化していたEventBridgeスケジューラを有効化しようとした時に発生したエラーの備忘録です。

# 有効化時に発生したエラー
無効化していたスケジュールを有効化する時に以下の画面のエラーが発生しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268065/67d90674-ee90-d4b5-0058-b2efb12d726f.png)


「The StartDate you specify connot be earlier than 5 minutes ago.」が表示されたエラーで日本語訳すると「指定する開始日は 5 分前であってはなりません。」です。


# エラーの原因は「開始日時」の制限
EventBridgeスケジューラに指定する開始日時は5分より前には設定できないという制限があり、無効化していたスケジュールを再度有効化する時にもこの制限が適用されるというのがエラーの原因でした。
以下の画面は開始日時を5分以上前に設定してスケジュールを新規作成しようとした時に表示されるエラーです。
有効化時のエラーと同じ「The StartDate you specify connot be earlier than 5 minutes ago.」が表示されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/268065/771ac667-15e3-4784-f2fd-15bb3d172b5c.png)

エラーの回避方法はスケジュールの再有効化時(新規作成時も同様)に開始日時を5分前以内に設定し直して有効化することです。
スケジュールの再有効化を自動で実施する場合などはこの制限に引っかかって有効化が完了していない可能性がありますので御注意下さい。
お家サーバー その11

# 概要
お家サーバーやってみる。
さよなら、お家サーバー編。

# 突然、NATの設定が消えた。

- PR-S300HIの接続方式がPPPoEからIPoEに変更されたらしい。
- NATの設定画面が消えた。
- ファームウェアバージョンは、変わっているようには、見えない。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/6cff480b-3ec0-307f-62f8-0f24881aab27.png)


以上。
Meta graph Api アカウント情報取得Api （/me/accounts）が空の配列を返す問題

## 結論
権限を追加するべきだった。
`business_management`権限を追加してください！！

## 原因
ログインURLを生成するときに渡す権限に問題がありました。

以下の権限でアカウント情報を取得しようとしたら失敗した。

```
$permissions = [
    'public_profile',
    'instagram_basic',
    'pages_show_list',
];

$login_url = $helper->getLoginUrl(config('services.facebook.redirect_uri'), $permissions);
```

## 下記のように変えて解決
```
$permissions = [
    'public_profile',
    'instagram_basic',
    'pages_show_list',
    'business_management',
];
```
実践的なオブジェクト指向の考え方を説明してみる

# 目次
[1.オブジェクト指向とは](#1-オブジェクト指向とは)
[2.実例](#2-実例)
[3.おわりに](#3-おわりに)

# 1. オブジェクト指向とは
※前提条件：本記事は初心者に向けてイメージを掴んでいただくものであるため、厳密には違うかもしれません。
私のプログラミングメンタリングを長らく受講していただいている方に向けて、オブジェクト指向をわかりやすく説明したかったのですが、良い記事が見つからず、本記事を執筆することにしました。
オブジェクト指向とは、プログラムを機能単位(オブジェクト)に分けて、その中にメソッド(実行単位)を記載していく設計思想です。PythonやSwiftなど、モダンな言語に採用されています。


# 2. 実例
以下は、挨拶をするプログラムの例です。
- parents:この機能は何を実装すれば良いのかの設計書
- child:parentsに書かれた設計書をもとに機能を実装
- main：プログラムを実行するだけのもの
~~~python:perents_greet.py
# class宣言
class Human:

    # コンストラクタ
    def __init__(self, name, year, birth):
        self.name = name
        self.year = year
        self.birth = birth

    # 自己紹介メソッド
    def introduce(self):
        pass

    # 名前取得メソッド
    def get_name(self):
        return self.name
~~~

~~~python:child_greet_1.py
from perents_greet import Human
#Humanクラスを継承して、HumanHealthを定義
class HumanHealth(Human):

    # コンストラクタ
    def __init__(self, name, year, birth, height, weight):

        #親クラスのコンストラクタを呼び出す
        super().__init__(name, year, birth)

        self.height = height
        self.weight = weight

    # BMIメソッドを追加
    def get_bmi(self):
        return round(self.weight/(self.height**2),2)

    # 自己紹介メソッドをオーバーライド
    def introduce(self):
        text = "私の名前は、{}です。今年で{}歳になります。BMIは{}です。"
        return text.format(self.name, self.year, self.get_bmi())
~~~

~~~python:child_greet_2.py
from perents_greet import Human
#Humanクラスを継承して、HumanHealthを定義
class HumanHealth(Human):

    # コンストラクタ
    def __init__(self, name, year, birth, height, weight):

        #親クラスのコンストラクタを呼び出す
        super().__init__(name, year, birth)

        self.height = height
        self.weight = weight

    # BMIメソッドを追加
    def get_bmi(self):
        return round(self.weight/(self.height**2),2)

    # 自己紹介メソッドをオーバーライド
    def introduce(self):
        text = "私の名前は、{}です。今年で{}歳になります。BMIは{}です。既婚者で趣味はサッカーです。"
        return text.format(self.name, self.year, self.get_bmi())
~~~

~~~python:main.py
import child_greet_1
import child_greet_2

# somaはchild_greet_1を実行
souma = child_greet_1.HumanHealth("souma", 26, "19931013", 1.7, 70)
print (souma.introduce())

# kojiはchild_greet_2を実行
koji = child_greet_2.HumanHealth("koji", 26, "19931013", 1.7, 70)
print (koji.introduce())
~~~

実行結果
~~~
私の名前は、soumaです。今年で26歳になります。BMIは24.22です。
私の名前は、kojiです。今年で26歳になります。BMIは24.22です。既婚者で趣味はサッカーです
~~~
somaさんはシャイなようで、BMIまでしか教えてくれませんでした。
一方でkojiさんは、オープンなようで、家族構成や趣味まで教えてくれました。
このように`同じ親クラスの継承でもそれぞれで挨拶するという機能は同じです。`
一方で、`挨拶内容には違いがあるようです。`
これがオブジェクト指向の考え方です。
`親クラスで、機能とやりたいことを定義しておいて、実装は子クラスに任せる。`これがオブジェクト指向が現場で使われている設計方法です。

# 3. おわりに
最後まで記事を読んでいただきありがとうございます。
オブジェクト指向いかがでしたか？
複数人の開発者で作り上げるプロジェクトでは必須の考え方になります。
コードの中に設計書を設けることである程度実装が統一されます。
ぜひこの考えか方をマスターして、脱初心者を目指してみてください！
記事にディレクトリ構成図を書きたい

# はじめに

ディレクトリ構成図を記事に書きたくて調べた備忘録です。

# Tree

https://tree.nathanfriend.io/

私はこちらのサイトで作成するのが一番わかりやすかったです。

`Eneter`と`Tab`だけでお手軽に作成できます。

# おわりに

こういうの作ってくれる人尊敬します。
【Python】数値を有限bitの符号付整数として解釈する方法

`0b10000000` を8bitの符号付整数として解釈すると `-128` になるが、Pythonでは `128` と解釈されてしまう。
Pythonのintは有限bitではないので、先頭のビットが符号ビットとして解釈されないことが理由なのだが、ラズパイでセンサーなんかを触っていると8bitの符号付整数として扱いたいときがある。
そんな時に、intを任意bitの符号付整数として解釈する方法を残しておく。

# 8bitの符号付整数として解釈する

```python
n = 0b10000000  # 128
mask = 0b11111111  # 8bitのマスクを用意

# nを8bitの符号付整数として解釈する
#   - `n ^ mask` : 単純なビット反転
#   - `~` で符号を考慮したビット反転
~(n ^ mask)  # -128
```

# 16bitの符号付整数として解釈する

```python
n = 0b100000000000000  # 32768
mask = 0b1111111111111111  # 16bitのマスクを用意

# nを16bitの符号付整数として解釈する
#   - `n ^ mask` : 単純なビット反転
#   - `~` で符号を考慮したビット反転
~(n ^ mask)  # -32768
```

# 関数化

```python
def signed(n: int, bits: int = 8) -> int:
    mask = 2 ** bits -1
    return ~(n ^ mask)
```
【New UI】DX Suite の使い方解説！（ワークフローを作成してみよう編）


# はじめに
DX Suite (New UI)の帳票定義設定方法について、これからシリーズで記事を投稿していきます！
DX Suite の基本的な使い方、またより便利に使えるTipsもスクリーンショット多めでお伝えできればと思います！


#### 今回は赤枠内のワークフローを作成してみよう編です！
※本来は読取範囲指定などの設定も含めて「ワークフロー」と呼ばれますが
とても長くなってしまうため、今回は読取範囲指定の前まで作成してみます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625460/735b2667-8359-c293-b1db-f394c1ff926f.png" width=80%>

↓第２弾の帳票画像を読み取る設定方法を知りたい方はこちら！
[【New UI】DX Suite の使い方解説！（帳票画像を読み取る設定をしてみよう編）](https://bstyle-value-technologies.blogspot.com/2024/04/dxsuite-002.html)

# こんな人に参考にしてほしい
- DX Suite で帳票を読み取りたいけど一歩がなかなか踏み出せない…！な人
- ワークフロー設定のやり方をわかりやすく知りたい人
- AI-OCRに興味がある人


# ワークフローを作成してみよう
早速、実際の画面（スクリーンショット）を見ながらワークフローを作成してみましょう！

### 用語集
- DX Suite 
    - AIを活用し、従来と比べて高精度な文字認識が可能になったOCRサービスのこと
- New UI
    - 従来のDX Suite をさらに使いやすくアップデートしたもの
    （UIが一新されただけでなく、機能面でも処理精度の向上やできることが増えました！）
- ワークフロー
    - 帳票を読み取る際の一連の処理を設定し保存したもの

参考：DX Suite ヘルプセンター
[DX Suite 用語集] https://help-dxsuite.inside.ai/hc/ja/articles/24448684360089

### 用意するもの

- DX Suite のアカウント
- 帳票（2枚）
    1. 原紙（何も記入されていない帳票）←ワークフロー設定で使います。
    2. 記入済みの帳票←テストで読み取るときに使います。

1.原紙
![原紙_個人票.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625460/ed4dd338-eb2f-2fd4-b24b-ed4a5ee57808.png)

2.記入済みの帳票
![記入済み個人票1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625460/6f006191-ae3e-50c3-172f-92a7087e5d94.png)





## ① Intelligent OCR に行く
ログイン後に「Intelligent OCR」をクリックしてください。
![TOP画面リニューアル後.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625460/8ede2859-65f4-4f85-ef0c-95e0f126c07d.png)



## ②ワークフローを作成
次に「ワークフロー作成」をクリックしてください。
![ワークフロー画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625460/25fd6e0d-a9c3-56e1-5b88-8737de7fcb47.png)


## ③データを選択
何をデータ化するかを選択できる画面になりますので、今回は定型で新しい画像から読取範囲を指定していきたいので、「読取範囲の指定」をクリックします。
![新_新しい画像から.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625460/1f3a6ca4-fe52-512e-1071-37fdf66aa36e.png)




## ④帳票画像を選択
読み取りたい帳票画像を選択する画面になりますので「ファイルを選択」をクリックします。
※ここにファイルをドラッグして選択することもできます。
![帳票テンプレート指定画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625460/430a0517-f7a8-a409-bae9-1ad67ddbd72f.png)


⑤読み取りたい帳票画像を選択し、「開く」をクリックします。
![帳票選択.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625460/b0520fd8-fb83-e9f6-58ba-00433f90d233.png)


⑥読み取りたい帳票を選択できたら、「作成」をクリックします。
![帳票選択後、作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625460/a7cf32fa-c1f2-2ea6-9cb1-359ffb62a739.png)

## ⑦ワークフロー名を変更
ワークフロー名を任意のものに変更します。
※ワークフロー名はいつでも変更できます。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3625460/467bada0-b7e6-1ecc-f18c-46eb477de48a.png)


# お疲れ様でした！
今回はここまでです！
これでとりあえずのワークフローの骨格ができました！
次の記事で、実際の帳票の読み取りたい箇所を設定していきます。
ここから特に直感的に操作ができる楽しいパートに入りますのでお楽しみに！


【追記】
↓第２弾の記事を投稿しました！
[【New UI】DX Suite の使い方解説！（帳票画像を読み取る設定をしてみよう編）](https://bstyle-value-technologies.blogspot.com/2024/04/dxsuite-002.html)


※「DX Suite」はAI inside 株式会社の登録商標です。
北海道大学構内バスのアイヌ語バス停名

[北海道大学構内バスのアナウンスにアイヌ語を導入](https://www.hokudai.ac.jp/news/pdf/240129_pr2.pdf)(北海道大学、2024年1月29日)というプレスリリースが出ていたので、各バス停名のアイヌ語表記を、カタカナとラテンアルファベットの両方で書いてみた。

1. カンピカㇻ ウシ (kanpikar usi)
1. クラーク エシカルン ウウェカㇻパ チセ (Clark esikarun uwekarpa cise)
1. カムイ イレンカ ウワンパレ カンピヌイェ ウシ (kamuy irenka uwampare kanpinuye usi)
1. イカㇻ カンピヌイェ ウシ (i=kar kanpinuye usi)
1. カンピヌイェ クㇽ ウタㇻ ウタサタサ ウシ (kanpinuye kur utar utasatasa usi)
1. リクン イパカㇱヌ ウコピㇼカレ ウシ (rikun ipakasnu ukopirkare usi)
1. シンルプㇱ カンピヌイェ ウシ (sinrupus kanpinuye usi)
1. チコイキㇷ゚ イカフイェ ウシ (cikoykip ikahuye usi)
1. イメルポ カンピヌイェ ウシ (imerupo kanpinuye usi)
1. アシㇼ カムイ イレンカ カンピヌイェ ウシ (asir kamuy irenka kanpinuye usi)
1. イペ イトゥサレ ピㇼカレ モシㇼ カマ チャシ (ipe i=tusare pirkare mosir kama casi)
1. アシㇼ カムイ イレンカ カンピヌイェ ウシ (asir kamuy irenka kanpinuye usi)
1. イメルポ カンピヌイェ ウシ (imerupo kanpinuye usi)
1. チコイキㇷ゚ イカフイェ ウシ (cikoykip ikahuye usi)
1. シンルプㇱ カンピヌイェ ウシ (sinrupus kanpinuye usi)
1. ウエトゥㇱマㇰ ケンル (uwetusmak kenru)
1. イトゥサレ カンピヌイェ ウシ (i=tusare kanpinuye usi)
1. ニマㇰ トゥサレ カンピヌイェ ウシ (nimak tusare kanpinuye usi)
1. イパカㇱヌ カンピヌイェ ウシ (ipakasnu kanpinuye usi)
1. イチェン カンピヌイェ ウシ (icen kanpinuye usi)
1. カンピカㇻ ウシ (kanpikar usi)

カンピをkanpiとkampiのどちらで綴るか悩ましかったが、佐藤知己, 北原モコットゥナㇱ, シリヤ・イヤス『[「北海道大学キャンパスガイドマップ」のアイヌ語併記作業について](https://doi.org/10.14943/Jais.2.075)』の助けを借りて、kanpiで決め打ちにした。ただ、そうすると、ウワンパレをuwanpareにすべきなのだが、そこは私(安岡孝一)の趣味でuwampareにしてしまった。でも、北海道大学理学部では、uwanpareとuwampareのどちらを正式に使ってるんだろ。
WTFormsで複数チェックボックスを表示する方法

Flaskでマイクロサービス作って使ってます（自分用）
複数選択可のCheckboxを作るのに躓いたので情報共有

## 環境(関係しそうなモノのみ)
Flask:3.0.1
Flask-WTF:1.2.1
WTForms:3.1.2
WTForms-Components:0.10.5 <--意図的に入れた覚えがないので多分依存関係

## やりたい事

あなたが使えるのは？
- [ ] フライス盤
- [ ] 旋盤

みたいな複数回答可なチェックボックスをWTFormsで生成したかった．

## やりかた
作るだけなら[こちら](https://stackoverflow.com/questions/73354015/multicheckbox-field-validation-with-flask-wtforms)を参考にすればできます．
```python:multiCheckbox.py
class MultiCheckboxField(SelectMultipleField):
    widget = widgets.ListWidget(html_tag='ol', prefix_label=False)
    option_widget = widgets.CheckboxInput()
class RegistrationForm(FlaskForm):
    language = MultiCheckboxField('Language', choices=get_languages())
```
(元ネタではValidatorも実装しているのですが，ここでは割愛）
選択肢の追加も[form名].choicesに足していけばいいので，selectFieldと同じです．
## 問題
最初から✓状態で表示したい
SelectFieldの場合は選択状態にしたいvalueを[formオブジェクト].[form名].defaultに設定すればいいのですが，
```python:SelectFieldの場合
class myform(FlaskForm):
  selectForm=SelectField()

choices=[('milling','フライス盤'),('Lathe','旋盤'),('grinding','研削盤')]

form=myform()
form.selectForm.choices=choices #選択肢を追加
form.selectForm.default='Lathe' #初期選択状態を設定
form.process([])
```
複数の場合は？？？となりググると[こちら](https://stackoverflow.com/questions/19564080/how-to-pre-populate-checkboxes-with-flask-wtforms)の様に
[form名].dataに選択状態にしたい選択肢のリストを設定すればよいとあり，
```python:MultipleCheckBoxの場合
class myform(FlaskForm):
  selectForm=MultiCheckboxField()

choices=[('milling','フライス盤'),('Lathe','旋盤'),('grinding','研削盤')]
selected=['milling','Lathe']

form=myform()
form.selectForm.choices=choices #選択肢を追加
form.selectForm.data=selected #初期選択状態を設定
form.process([])
```
やってみたのですが，選択状態が反映されません
そもそもdataプロパティって送信されたフォームの内容を取得するときに使うよなと思いながらさらに調べたら
## 解決策
process([])メソッドの順番変えたら動きました
```python:修正箇所抜粋
form.selectForm.choices=choices #選択肢を追加
form.process([])
form.selectForm.data=selected #初期選択状態を設定
```

process([])メソッド，dataプロパティをクリアしちゃうみたいです
xserverのPHPバージョンアップ

xserverのphpバージョンアップがどん詰まったので備忘録
他にスマートな方法をご存知の方、教えてください…

### xserverへSSH接続して確認したPHPバージョン
```
$ php -v
PHP 5.4.16
```
xserverのPHP Ver.切り替え画面
![スクリーンショット 2024-01-10 14.41.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3113701/016ca1be-8237-a614-7462-1e199f513751.png)


### PHPバージョンを合わせる

xserverのPHP Ver.切り替え画面で設定したPHP8.1.22に変更します

xserver上に存在するPHPのバージョンを検索
```
$ find /opt/php-*/bin -type f -name 'php'
```
結果
たくさんのPHPバージョンが出てきました
```
/opt/php-5.1.6/bin/php
…
/opt/php-5.4.16/bin/php
…
/opt/php-8.1.22/bin/php
…
```
この中からxserverサーバパネルで設定した8.1.22を設定します

#### 以下、作業に詰まった手順と現象
bin/phpにシンボリックリンクを作成するためにln
すでにリンクがある（と思う）ので上書きオプション-iを付けて
```
$ ln -i -s /opt/php-8.1.22/bin/php $home/bin/php
```

実行！

```
$ ln -i -s /opt/php-8.1.22/bin/php $home/bin/php

ln: `/bin/php' を置き換えますか(yes/no)? yes
ln: `/bin/php' を削除できません: 許可がありません
```

私にbin/phpを書き換える権限はないらしい

なるほど、私がrootじゃないからか！

と浅はかに試してみたスーパーユーザ
```
$ sudo ln -i -s /opt/php-8.1.22/bin/php $home/bin/php
-bash: /usr/bin/sudo: 許可がありません
```


### そもそも
xserverのユーザにはroot権限は与えられません
共有サーバなので、root権限を持たれては困りますということ


_では私が私の権限のまま、設定すべきディレクトリはどこ？？_

#### 以下、解決手順
### xserverのサーバパネルを確認
![スクリーンショット 2024-01-10 15.27.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3113701/eeff2e96-6e5b-0a15-29ea-e85df6a2dbff.png)

設定すべきディレクトリ、書いてました

```
$ cd /home/{serverID}
```
（ここって多分ssh接続したときに降り立つ場所では？）

binディレクトリが存在しないので作成
```
$ mkdir ~/bin
$ cd ~/bin
```
ここを宛先としたシンボリックリンクを作成…
```
$ ln -i -s /opt/php-8.1.22/bin/php ~/bin
```
```
php -> /opt/php-8.1.22/bin/php
```
できました〜〜〜〜

### パスを通しましょう
例のホームディレクトリに戻ってパスを通します
```
$ cd ..
$ vi .bash_profile
```

```
…
# PATH=$PATH:$HOME/bin // 元々設定されています、コメントアウトします
PATH=$HOME/bin:$PATH // 追加！ここは「＄HOME/bin」（最初にfindした場所）

export PATH
```

escキー→`:wq`で保存し
下記コマンド実行して更新

```
$ source .bash_profile
```

### PHPを確認してみる
```
$ php -v
PHP 8.1.22
```

無事利用したいphpバージョンに変更することができました


---
参考ページ🙇！
[XServerのSSH接続でPHP5.4ではなくPHP7を指定してbin/console実行](https://umebius.com/eccube/bin-concole-xserver-ssh-php7-cli/)
[XserverのSSH接続でPHPバージョンを変更する方法をわかりやすく](https://ksanhonsan.com/phpversion-change/)
OSが入っていないPCにLinuxを入れる方法

【用意するもの】
・自作PC
・ネット環境
・ネットにつながるPC
・5GB以上のUSB(またはDVD)

【手順】
まず、ネットにつながるPCにUSBまたはDVDを入れる
Ubuntu公式サイトにアクセスし、下記サイトよりisoファイルをダウンロードする(LTSの方が長期間無償でサポートがあるのでお勧め)
https://jp.ubuntu.com/download

★Windowsの場合は下記サイトの手順に従って、別ソフトを使ってDLしたisoファイルをLiveUSBという起動可能な形式に変換する
https://www.kkaneko.jp/tools/linux/lili.html

★Macの場合は下記ソフトを利用(ソフト名：balenaEtcher)
https://blog.kabocy.com/linux/6830/

変換したUSBをUbuntuを入れたい自作PCに差す
Bios画面でUSBを認識していないようであれば、★USBを差した状態のまま電源ボタンでシャットダウンし、再度電源を付けると認識するようになる

認識したら、BIOS画面のboot優先順位（画面下部）に起動で読み込むのがHDDなのかDVDなのかと順序が並んでいるので、USBを一番左に持ってきて保存して退出する

その後、再起動するとUbuntuのインストールが始まる
★途中で「Remove~~~medium, press Enter」のような文字が画面中央に出てきたら、USBを抜く
あとはパスワードなど設定するなどあるが、これ以降はUbuntuのインストール方法で調べたら手順は載っているので割愛する

ポイントは★を付けた部分
ネットではWindowsの上にUbuntuを入れる方法などが多く、LiveUSBを作らないといけないことに気づけず、時間がかかった
またUSBを抜いたり差したりしないと、認識しなかったりセットアップが進まなかったりしたのが苦戦した点

この記事が初めて自作PCを作る人のお役に立てれば幸いです
【10分で試す】Cloud RunのIAM認証

Cloud RunのIAMでの認証を試してみたところ、驚くほど簡単でした
個人用のAPIサーバなど構築するときに便利そうなので備忘録です

## Cloud Runでサービスを作成する

1. Google Cloud ConsoleのCloud Runの画面から`サービスを作成`します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/c0dca19e-c9c6-a2ac-4678-74d044cee590.png)
1. サンプルのイメージを使ってサクッと起動までやります
    - イメージはCONTAINER REGISTRY内の`hello`
    - リージョンを`asia-northeast1(東京)`
    - 念のためスケーリングは最大数を`10`
    - 上り（内向き）の制御は`すべて`
    - 認証は`認証が必要`
    - その他はデフォルト
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/f9c69d8e-f1e4-1369-5efb-04530c8d6395.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/54d0f881-a3b3-1ca0-4622-60c73b56480b.png)
1. `作成`をクリック(1,2分で完了します)

## とりあえずアクセスしてみる

作成できたらこのような画面になりました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/1ef93d70-84f2-300e-c2ec-27d649a2fd03.png)

とりあえずアクセスしてみます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/1989c338-8dea-ddeb-8d44-f3b50d614647.png)
ちゃんと`認証が必要`の設定が効いてるみたいです

## 権限を付与する

1. サービスの一覧画面に戻ります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/85b98f5f-3ea0-bc8d-4cc6-14e80a25f279.png)

1. サービス名の左側にあるチェックボックスにチェックを入れ、`権限`をクリックするとサービスへのアクセス権を編集するダイアログが表示されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/e925ea9b-4443-fd3c-03b2-ea804dabde16.png)

1. `プリンシパルを追加`から設定します。
    - `新しいプリンシパル`には権限を付与したいgoogleアカウント
    - `ロール`は`Cloud Run 起動元`
    - 保存
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/360735/bdcfda89-d49e-416e-7e85-1d188a8cc0c4.png)

## 再度アクセスしてみる

とりあえず正常応答が返るかどうかしか気にしてないので、curlでやります

1. Cloud Shellを起動します（右上のターミナルアイコン）  
  現在のログインアカウントとプロジェクトが設定された状態でTerminalが起動します（めちゃ便利）
1. Cloud Shellで以下のコマンドでログインアカウントのIDトークンを取得します(ダイアログが表示されたら`承認`を選択)
    ```
    $ gcloud auth print-identity-token
    ```
1. 取得したトークンを使って、サービスにアクセスします
    ```
    $ curl {YOUR_SERVICE_URL} -H 'Authorization: Bearer {token}'
    ```
1. 先ほどはForbiddenでしたが、今回は200でHTMLが返ってきているはずです

めっちゃ簡単ですね

## 一応、権限付与していないGoogleアカウントでも試してみる

付与した権限を削除するか、別のgoogleアカウントのIDトークンで同じcurl文を実行してください

```
$ curl ~以下省略~
<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>403 Forbidden</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Forbidden</h1>
<h2>Your client does not have permission to get URL <code>/</code> from this server.</h2>
<h2></h2>
</body></html>
```

403になりました。大丈夫そうです
あとはサービスを削除して終了です

以上です！
【Python】int,bytes,strの相互変換と表示


# ■ 2進数・16進数で数値を宣言する

```python
# 2進数
n = 0b1111111111111111  # 65535

# 16進数
n = 0xffff  # 65535
```

# ■ intからbytesに変換
## int -> bytes

```python
n = 0b1111000111110010
# 2バイトでビッグエンディアン
n.to_bytes(2, 'big')  # b'\xf1\xf2'

# 2バイトでリトルエンディアン
n.to_bytes(2, 'little')  # b'\xf2\xf1'

# 4バイトでビッグエンディアン
n.to_bytes(4, 'big')  # b'\x00\x00\xf1\xf2'

# 4バイトでリトルエンディアン
n.to_bytes(4, 'little')  # b'\xf2\xf1\x00\x00'
```


### structを使う方法
https://micropython-docs-ja.readthedocs.io/ja/latest/library/struct.html#module-struct

```
■ バイト順の指定
- < リトルエンディアン
- > ビッグエンディアン

■ データ型の指定
- b char型 (1バイト)
- B unsigned char型 (1バイト)
- h short型 (2バイト)
- H unsigned short型 (2バイト)
- i int型 (3バイト)
- I unsigned int型 (3バイト)
- l long型 (4バイト)
- L unsigned long型 (4バイト)
```
```python
import struct
n = 0b1111000111110010

# 2バイト(符号なし)でビッグエンディアン
struct.pack(">H", n)  # b'\xf1\xf2'

# 4バイト(符号あり)でリトルエンディアン
struct.pack("<l", n)  # b'\xf2\xf1\x00\x00'
```

## List[int] -> bytes

```python
arr = [0b00000001, 0b00000011]
bytes(arr)  # b'\x01\x03'
```

# ■ bytesからintに変更

```python
# ビッグエンディアン
int.from_bytes(b'\xf1\xf2', 'big')  # 61938
# リトルエンディアン
int.from_bytes(b'\xf2\xf1', 'little')  # 61938
# ビッグエンディアン (先頭の\x00は無視される)
int.from_bytes(b'\x00\x00\xf1\xf2', 'big')  # 61938
# リトルエンディアン (末尾の\x00は無視される)
int.from_bytes(b'\xf2\xf1\x00\x00', 'little')  # 61938
```

# ■ strからbytesに変換

## str -> bytes

```bash
# str -> bytes (エンコード)
'abc123'.encode('utf-8')  # エンコーディング形式を指定する
```

## bytes -> str

```bash
# bytes -> str (デコード)
b'abc123'.decode('utf-8')  # エンコーディング形式を指定する

# 変換できないときは UnicodeDecodeError になる
try:
    b'\x80abc'.decode('utf-8')
except UnicodeDecodeError as e:
    print(e)
```

# ■ 表示

## intの表示
```python
n = 61938

# int -> 2進数表示
bin(n)  # '0b1111000111110010'

# int -> 8進数表示
oct(n)  # '0o170762'

# int -> 16進数表示
hex(n)  # '0xf1f2'
```

## bytesの表示
```python
# bytes -> 16進数表示
n.to_bytes(2, 'big').hex()  # 'f1f2'
n.to_bytes(2, 'little').hex()  # 'f2f1'
n.to_bytes(4, 'big').hex()  # '0000f1f2'
n.to_bytes(4, 'little').hex()  # 'f2f10000'
```

## 自分で実装する

```python
from typing import Union, List
def int_to_binary(n: int, bits: int = 8) -> str:
    """intを2進数に変換"""
    return ''.join([str(n >> i & 1 ) for i in reversed(range(0, bits))])

def bytes_to_binary(data: Union[bytearray,bytes]) -> List[str]:
    """bytesを2進数のリストに変換"""
    return [int_to_binary(byte) for byte in data]


# int -> 2進数表示
n = 61938
int_to_binary(n, 16)  # '1111000111110010'

# bytes -> 2進数表示
b = n.to_bytes(2, 'big')
bytes_to_binary(b)  # ['11110001', '11110010']
```