インドカレーファンクラブ

パソコン、カメラ

Raspberry Pie久しぶりに触って音声認識・レシート出力した備忘

RaspberryPie久しぶりに触った 多少は変わったり不便したことがあるので雑多にメモ

本当に書き散らすような記述

基本的にここを参考にしてく話

fabcross.jp

あとこれ

raspida.com

前提

RaspberryPieのOSはRaspberry Pi OS Lite
2021-05-07-raspios-buster-armhf-lite.img https://www.raspberrypi.org/software/operating-systems/#raspberry-pi-os-32-bit

どうでもいいけど、今はSDにOSを焼いてくれるアプリケーションが出ているらしい
が、自分の端末にアレコレ入れたくなかったので今回は使わなかった https://www.raspberrypi.org/software/

色々いれる

いつもの

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get vim
$ sudo apt-get git

python

pythonコマンドのデフォの参照を3系に変える

$ cd /usr/bin
$ sudo unlink python
$ sudo ln -s python3 python

参考:https://www.ingenious.jp/articles/howto/raspberry-pi-howto/python-3-change/

pipも入ってなかったので入れる pipはそのままだとpip3で呼ぶことになるので、上記のpython3.xへの変更に倣い参照を差し替える

# install pip
$ sudo apt-get -y install python3-dev
$ sudo apt-get -y install python3-pip

# link
$ cd /usr/bin
$ sudo unlink python
$ sudo ln -s pip3 pip

日本語表示

$ sudo apt-get jfbterm 

参考:https://www.raspberrypi.org/forums/viewtopic.php?t=240773

NW接続とか

久しぶりにやるとSSHすら苦労した

これは完全に自分の事情なんだけど、 手元のWindows端末にはLANポートは一つでWifiなし、MacbookにはWifiがあってLANポートすらない

丁度手元にUSBオス-LANメスなケーブルがあったのでどうにかなった

これで繋げばいい ssh pi@raspberry.local
raspberry.localでログインするしIP固定はしなくていいかな

Wifi設定前は以下の構成で接続して設定する [Internert]-[Router]-[LAN]-[Win]-[USB-LAN]-[LAN]-[RaspberryPie]

Wifiの設定をする。最初に以下のようなことも言われるけど、

Wi-Fi is currently blocked by rfkill.
Use raspi-config to set the country before use.

こちらの手順で十分だった http://faster-than-the-sol.blogspot.com/2020/03/raspbian.html

raspi-configのメニューがちょっと変わっていて
1. SystemOption > S1 Wireless LAN にある

今回はraspi-configで設定したけど、上記に記述されている通り/boot直下に書いてしまっておいた方が早い気もする

モニタ準備

準備する
商品の説明書に説明が書いてあることだろうから特に困らないだろう

$ git clone https://github.com/Lcdwiki/LCD-show.git
$ chmod -R 755 LCD-show
$ cd LCD-show
$ sudo ./MHS35-show

困らないだろうと思いきや、
sudo ./MHS35-showの実行中にエラーが出る

$ sudo ./MHS35-show
(省略)
Selecting previously unselected package xserver-xorg-input-evdev.
(Reading database ... 45946 files and directories currently installed.)
Preparing to unpack .../xserver-xorg-input-evdev_1%3a2.10.6-1+b1_armhf.deb ...
Unpacking xserver-xorg-input-evdev (1:2.10.6-1+b1) ...
Processing triggers for man-db (2.8.5-2) ...
dpkg: dependency problems prevent configuration of xserver-xorg-input-evdev:
 xserver-xorg-input-evdev depends on libevdev2 (>= 1.2.2+dfsg-1~); however:
  Package libevdev2 is not installed.
 xserver-xorg-input-evdev depends on libmtdev1 (>= 1.1.0); however:
  Package libmtdev1 is not installed.
 xserver-xorg-input-evdev depends on xorg-input-abi-24; however:
  Package xorg-input-abi-24 is not installed.
 xserver-xorg-input-evdev depends on xserver-xorg-core (>= 2:1.18.99.901); however:
  Package xserver-xorg-core is not installed.

dpkg: error processing package xserver-xorg-input-evdev (--install):
 dependency problems - leaving unconfigured
Errors were encountered while processing:
 xserver-xorg-input-evdev
reboot now
Connection to raspberrypi.local closed by remote host.
Connection to raspberrypi.local closed.

https://www.amazon.co.jp/gp/customer-reviews/R4LL68CCBYJ5E/

この人のコメント通り以下の作業を実施する(ありがたい)

$ sudo vi /etc/rc.local

"fbcp &" の行をコメントアウト、もしくは削除する

Google Cloud Speech APIを使う準備

冒頭のコチラに記載のものとは多少方法が変わっている

# Add the Cloud SDK distribution URI as a package source
echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] http://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list

# Import the Google Cloud public key
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -

# Update the package list and install the Cloud SDK
sudo apt-get update && sudo apt-get install google-cloud-sdk

https://cloud.google.com/sdk/docs/quickstart-debian-ubuntu

sudo apt-get update && sudo apt-get install google-cloud-sdk の実行時に文句を言われた
E: Unmet dependencies. Try 'apt --fix-broken install' with no packages (or specify a solution).

素直に sudo apt --fix-broken install し再度実行したら無事解決

この後説明の通り認証情報つくったり、json置いたりとかする

参考(利用方法):https://cloud.google.com/speech-to-text/docs/libraries?hl=ja#client-libraries-install-python

事前にWindowsで試すなら...

因みにWindowsならGUIでぽちぽちインストールしたあと コード内でこんな感じにCredentialのJsonのパスを設定してあげる

...
from google.cloud import speech
...

credential_path = r"D:\dev\python-speech-test\testhogehoge-xxxxxxxxxxxx.json"
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = credential_path
# 参考:https://stackoverflow.com/questions/51554341/google-auth-exceptions-defaultcredentialserror

サンプルのgitは古くなってるし pip install -r requirements.txtは別にやらなくていいかな
自分で以下をインストールしておけばいい

# 最終的にプログラムを動かす時にはマイクを利用する
# マイクを使う時にsudo python hoge.pyとしないとマイクが使えないよとOSエラーになる
# ということでpipもsudoで入れる
sudo pip install google.cloud
sudo pip install google-cloud-speech

マイク関連

冒頭のコチラの記事の通り、設定ファイル弄る
もしかしたらやらなくていいかも...

ただpyaudioとnumpyはpipで入れてはいけない
以下の手順に従ってインストールする

pyaudio

https://qiita.com/yukky-k/items/0d18ec22420e8b35d0ac#pyaudio%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB

$ sudo apt-get install python3-pyaudio

numpy

https://qiita.com/ohbashunsuke/items/e233b4969e4c410ac74f

$ sudo apt-get install libatlas-base-dev
$ pip uninstall numpy  # remove previously installed version
$ sudo apt-get install python3-numpy

音量ってかインプットゲイン

GUIでいじれるっぽいけど反映されてない気がする
https://www.ukeyslabo.com/raspberry-pi/audio/#gui%E3%81%A7%E6%93%8D%E4%BD%9C%E3%81%A7%E3%81%8D%E3%82%8B-alsamixer%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89

pyaudio側では弄れない。うーん

事前にWindowsで試すなら...

Pyaudioのインストールに苦労する
https://qiita.com/sugi-juku/items/c92f8f170a6b455e15f2

プリンタ

基本的にここの方法でOK

$ sudo pip install python-escpos
$ sudo pip install Pillow

おこられた

$ python print_test.py

>>> import pillow
Traceback (most recent call last):
  File "print_test.py", line 1, in <module>
    import print.py
  File "/home/pi/BookProj/print.py", line 1, in <module>
    from escpos.printer import Usb
  File "/usr/local/lib/python3.7/dist-packages/escpos/printer.py", line 21, in <module>
    from .escpos import Escpos
  File "/usr/local/lib/python3.7/dist-packages/escpos/escpos.py", line 25, in <module>
    from escpos.image import EscposImage
  File "/usr/local/lib/python3.7/dist-packages/escpos/image.py", line 17, in <module>
    from PIL import Image, ImageOps
  File "/usr/local/lib/python3.7/dist-packages/PIL/Image.py", line 109, in <module>
    from . import _imaging as core
ImportError: libopenjp2.so.7: cannot open shared object file: No such file or directory

追加でやったのはこれ

sudo apt-get install libopenjp2-7

プログラムを動かす

プログラムの詳細までは書かなくてもいいかな...?(汚いから書きたくない)
気が向いたら載せるかも

上記の流れでPython 3.xが普通にpythonで呼び出せるようになってるのでコードは3.xの文法で書けばいい

まあ、その結果がmain.pyという名前だとして、起動は sudo python main.py

上述の通り、sudoで実行する
なんでかと言うと、USBマイク、USBサーマルプリンタを使うのにroot権限がほしいから
その辺適切に設定すれば回避できそうな気もしつつ、まあいいかということで

AWS SNSでSMS送信できない問題の解決2021(割当上限増加リクエストのあれこれ)

概要

CognitoとMFAするためにSMSを送信する必要がある

その際にはAWS SNSを利用することになるんだけど、そこに癖がある

SMSの送信は$1分(月100件?)無料らしい
が、その記述も正直怪しい(後述)

今回2環境で異なるエラーが発生し、その両方で割当上限増加の申請をすることになった

AWSのわかりにくい文章のラッパーたる俺たちのクラスメソッド社のブログを見たが正直今回に関しては物足りなかった
故に今回この記事を書いている

Amazon SNS で SMS を一定量送信後に、急に送信できなくなった時の対処法 | DevelopersIO

SMS送信エラーのパターン

まず電話番号ミスでないことを確認したほうがいい

【AWS】Amazon SNSでSMSが届かなかった時の対応 - Qiita

今回僕はそこの問題は確認した上、Cognitoで送信するどころかマネジメントコンソールで送信してエラーになるので問題はもっと根本的なところにある

Cloudwatch logsを有効化しておくことでSMS送信失敗時のログはとれるようになるのでそこを見る(方法は割愛)

今回2環境で動かしていたがエラーのパターンは2つ確認した
どちらも東京リージョンからの送信

まず1つ目の環境

{
    "notification": {
        "messageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "timestamp": "yyyy-MM-dd HH:mm:ss.SSS"
    },
    "delivery": {
        "destination": "+8190XXXXXXXX",
        "smsType": "Promotional",
        "providerResponse": "No quota left for account",
        "dwellTimeMs": 121
    },
    "status": "FAILURE"
}

これは割当上限(デフォルト$1分)超えということでのエラー

最初はSMSを送れていたが途中から送れなくなった。その状況と合致しているので理解できるし納得できる

次に2つ目の環境

{
    "notification": {
        "messageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
        "timestamp": "yyyy-MM-dd HH:mm:ss.SSS"
    },
    "delivery": {
        "destination": "+8190XXXXXXXX",
        "smsType": "Promotional",
        "providerResponse": "Your account requires additional review prior to being able to send SMS messages. Please create a support ticket with an explanation of your use case. https://console.aws.amazon.com/support/cases#/create?issueType=service-limit-increase&limitType=service-code-sns-text-messaging",
        "dwellTimeMs": 103
    },
    "status": "FAILURE"
}

こちらの環境では最初から1通も送信ができず、このエラーで謎

取り敢えず1件目のエラーも2件目のエラーも、2件目のエラーログにあるURLから割当上限増加のお願いをする

サポートとのやりとり

1件目は正直雑に送った
雑とはいえ必須な項目は全て記入したし、ユースケースも想定件数も記入した

で、返信が返ってきた

平素は Amazon Web Servicesをご利用いただき、誠にありがとうございます。

お客様の使用限度額の引き上げの申請内容を慎重に審査した結果、ユースケースに関する情報が不十分であるという判断に至りました。

ケースの審査をお受けいただくには、次の内容に関しての詳細をこのメールへ返信してください。

-- SMSを送信するサイトまたはアプリのリンクまたは名前:
-- オプトインプロセスと、リクエストした人にのみメッセージが送信されるようにする方法の詳細:
-- メッセージの受信者が所在している国のリスト :
-- 予想される1日あたりの最大メッセージ送信数 :
-- 送信予定のメッセージテンプレート:
--その他、ユースケースをより理解しやすくする為の情報:


お客様からの情報を受け取り次第、申請を審査させていただきます。24 時間以内にこちらからご連絡いたします。必要な情報をすべてご提示いただいた場合は、24 時間以内に申請を承認いたしますが、追加で情報が必要な場合は、お客様のリクエストを解決するのに通常より時間がかかる可能性がございますことをご了承ください。

(以下省略)

これは申し訳ないことをしたかもしれないと殊勝に反省し、上のテンプレートに沿って追加で書いて送信したところ、今回の割当増加要求は認められた

次に2つ目の環境

こちらでは1つ目の環境での反省を活かし、最初から全ての項目に丁寧に記入し申請した
が、最初と同じ定型文で不十分だと文句を言われた

何が不十分なのか不明確で困ったが、一旦テンプレートに沿って同じ内容の返信をしておいた

すると申請が認められた

と思いきや上限$8で申請したはずが上限$100で通っていた
ナンデェ

料金よくわからん

彼らはこういう形で料金請求してくる

Asia Pacific (Tokyo)

Amazon Simple Notification Service APN1-Requests-Tier1 $0.00
First 1,000,000 Amazon SNS API Requests per month are free 104.000 Requests $0.00
Amazon Simple Notification Service APN1-SMS-Price-ROW $0.97
Variable Pricing for Amazon SNS SMS Notifications to Rest Of World (non-US) 0.969Dollars $0.97

利用していたのはSMS送信のみ
それも104回利用したことになっているらしい

100件という数字は超えているが1,000,000Requestsという数字はなんなのか

よくわからん

大した金額じゃないので目を瞑るけども、もう少しどうにかしたほうがいい気もする

まとめ

AWS SNSの利用にはそれなりの精神的苦痛が伴う

2021年6月時点でこんな感じで大いに不満だったけど、今後改善されることを祈って星3/5です(アマゾンカスタマー)

AWS Summit Online2021 しょぼ感想

aws.amazon.com

大した内容じゃないけどせっかくだから書き残そうかな程度の話です

このブログ読むより当該セッション見たほうがいいよ。見れるのかわからんけど...

以下セッションを見た

  • AWS Lambda を攻撃してみた ~サーバレスのセキュリティの考え方~(パートナーセッション)
  • インフラエンジニアだけでサービス開発してみた~フルスタックエンジニア育成への取り組み~(パートナーセッション)
  • リモートワークを実現した中堅中小企業が語る導入前と後の実際のところ-中堅中小企業にとっての働き改革とは- (CUS-26)
  • ZOZOTOWNにおけるAmazon EKSを中心に据えたマイクロサービスアーキテクチャへの変遷(CUS-01)
  • Amazon CodeGuru ~機械学習で実現するコードレビュー自動化とアプリケーションパフォーマンス最適化~(AWS-02)

AWS Lambda を攻撃してみた ~サーバレスのセキュリティの考え方~(パートナーセッション)

セッションの内容

Lambdaはマネージドサービス。ユーザ側が気にしなくてはいけないことは

  • コードのセキュリティ
  • 機密データの管理
  • 認証認可
  • モニタリングやログ記録

Lambdaのセキュリティ的な観点からみた特徴としては

  • イベントソースが多い
    • HTTP API, Message Que, CloudDB, IoT Device
      • それぞれが攻撃の対象になりうる
  • システム設計が複雑になる
    • モジュールの構成管理が複雑
    • インシデント・レスポンスのための可視化・検知が困難
  • セキュリティテストだけでは不十分
    • 構成が複雑で本番環境を複製しにくかったりも

リスクが予想しにくいことが一番のリスク
リスクの発生頻度と影響度が予想しにくいため負けない設計が重要
→リスクを予測して対応するより設計にセキュリティを組み込むことが大切
 攻撃ゼロではなく被害ゼロを目指す

侵入を前提としたセキュリティ設計。各レイヤで防御すること
WAF(Botブロック)-API Gateway(匿名遮断)-Function(RASP)

境界防御(企業内NWは安全とみなす)のみではなくゼロトラストで内部を守ろう

トレンドマイクロはCloudOneという製品を出してるよ~~~
→ 僕の個人開発ではお金をかけられないのでスルー

感想

REST以外のアクセスしか想定してないからイベントソースについては気にしたことなかったかも

システム設計が複雑になるってのは確かに身に沁みて感じるところで、どっかで設定漏れがあったりしそうで怖いなと思う

レイヤ防御、監視について、取り敢えず作ってから考える。みたいなことが多い(当社比)けど、構築前からもっとしっかり考えていかないと穴作りそう

あと最近はクレデンシャルを盗むのが流行ってるらしい

ツイッターで書いてたら

クレデンシャル,結局は実行プロセスの環境変数に埋まっているので,何らかの方法で任意のコマンドを実行できたら流出しますね(env 叩けさえすれば盗めちゃう)
CI は実行ログをユーザに見せるので, CI にそういう脆弱性が埋まってたら終わり

とか教えてもらえました

調べてたらここらへんが参考になった

www.slideshare.net

インフラエンジニアだけでサービス開発してみた~フルスタックエンジニア育成への取り組み~(パートナーセッション)

セッションの内容

資料を載せてるひとがいたのでそれ見るとわかりやすい
全部のってるわけじゃないよ

AWS Summit Japan | インフラエンジニアだけでサービス開発してみた~フルスタックエンジニア育成への取り組み~(パートナーセッション)|hase / 長谷川 正雄|note

社内インフラエンジニアを集めて社員向けAWS学習サイトを開発したよ。という話

サイトの中身はAWSの学習的なやつ。資格勉強用の選択問題とか社内講演の動画視聴とか

構築は

  • Amplify + React
  • Serverless

あとはサイト構築にまつわる体験や苦労などの話

経験者が少ない中、興味をもった社員に参画してもらってメンバーを増やしていった...とかそういう話

感想

それぞれについては既にぼんやり理解したつもりになっていたので実務的なサーバ構成の再確認的ができてよかった

AWS AppSync(GraphQL)のことは知らなかったので勉強しないといけない...

あとAWSのWorking Backwordsって文化があるみたいでその話も面白かった

【レポート】『「あなたのお客様は誰ですか?」から始まるAmazonのイノベーションのメカニズム、その手法をハンズオン』を受けてきた #AWSSummit | DevelopersIO

すごいざっくり言うとAWS流の「お客様への価値提供を見直してみよう」手法の話
この話は事業部長ポジの人とかに共有したら覚えが良さそう

サイト構築というか社員に参画してもらってたような部分について言うと、社内でこういった文化があるのは羨ましい

大企業ならでは?大企業だと逆にやりにくかったりしないのだろうか?

リモートワークを実現した中堅中小企業が語る導入前と後の実際のところ-中堅中小企業にとっての働き改革とは- (CUS-26)

セッションの内容

省略...

感想

AmazonWorkSpacesというもののご紹介がメインだった
https://aws.amazon.com/jp/workspaces/

シンクラ的に仮想デスクトップを使えるというもの
初期の手間がかからず比較的安いのが良さそう
AWSでAD作ってるとより連携便利だとか

VPNより手間かからなそうなのがいいね

ただ接続料従量課金以外にも固定費がかかるのがネック
スポット利用で従量課金のみだと嬉しいんだけど

このセッションを見て思ったけど、「リモートワーク環境を作りたい」と言われた時に自分がやると即答できるくらいの技術的引き出しとリテラシないとダサいと思う
「もし自分が情シスだったら?」というような仮定で、自分が実際やるかどうかはさておき

ZOZOTOWNにおけるAmazon EKSを中心に据えたマイクロサービスアーキテクチャへの変遷 (CUS-01)

セッションの内容

省略...

感想

ClassicASPで組んだモノリシックなWebアプリケーションをマイクロサービスアーキテクチャにするという話
カビたような技術に親近感を感じたくないけど親近感を感じますね~涙

僕が常々「段階的移行せえ!」と唱えている仕組みがストラングラーパターンと呼ばれていることを知った(言葉が分かると知識を深めやすく便利) https://www.atmarkit.co.jp/ait/articles/2001/23/news003.html

その他セッションではアーキテクチャの要点が紹介されてて参考になるだろう
僕はまだEKSがまるで分かっていないので資料だけ落としていつか読むということで...

あとZOZOではAPI Gatewayを内製して色々機能盛ってるらしい。すごい!

Amazon CodeGuru ~機械学習で実現するコードレビュー自動化とアプリケーションパフォーマンス最適化~(AWS-02)

セッションの内容

省略...

感想

aws.amazon.com

CodeGuruはCodeGuru ReviwerとProfilerの二つに分かれる

それぞれ概要はこんな感じ。他のサイト見たほうがいいよ!

  • Review
    • コードレビューするやつ
    • PRに組み込みこんだりできる
      • 発火タイミングはいろいろ
    • いまのところJava, Pytyon(Beta)
  • Profiler
    • パフォーマンス計測, 可視化するやつ
      • 実行コストを行単位で出してくれるみたい
    • JVMエージェント, Lambdaに組み込んだりする
      • Javaアプリケーション起動時に仕込んだり、コードに仕込んだり
    • いまのところJava/JVM言語

どっちも既存のCI/CDに統合しやすいよ!というのが売りみたい

Reviewはまあそのまま役に立ちそう
PRに毎回指摘が出てくると面倒くさそうではあるが触ってみたい

Profilerは便利そうだけど使い所が難しい?ずっと走らせておくWebアプリケーションで使えるのかこれは???
起動 - E2E - 終了みたいな感じにユーザ動作を想定したシナリオを通じたパフォーマンス取得とかはいけるかも

Profilerのその他の使いどころを考える
LambdaでがっつりJavaってパターンは正直無い気がしていて、Pythonの場合も計測するほどの処理をしない場合が多いと思う
と思うとJava/JVM言語でバッチ書く時くらいにしか役立たない?

とはいえどっちにしても料金の問題があるので個人使用はないかな。無料期間はありつつも...

私事だけど転職する時にやたら「パフォーマンスチューニングの経験は?」と聞かれ「ないですが興味あります」からのお祈りパターンを受けまくった
※僕のように薄給で働く人間はそんな高級なことはなかなかできない。大概、事業の売上向上施策の工数に割り当てられる歯車の一つに過ぎないからだ

そういうことを思うと、気軽にパフォーマンスチューニングやった感出せるこのサービスは良いものかもしれない

眼瞼下垂手術レポ~保険適用で二重整形できるのマジ?~(画像つき)

術後経過の写真は記事の最後に

自己紹介(いるか?)

都内在住 27歳 男性

眼瞼下垂とは?

この辺を参考に

眼瞼下垂手術 | 中島眼科

簡単に言うと上まぶたが下がって目が細くなる病気
老人がよくなっていて、今回のような手術症例を調べても若者の例はあまりないように思う

あらすじ

そもそもこの手術について調べたのは頭痛について調べていたのが発端
眼瞼下垂は頭痛の原因とも言われているということで興味をもった

眼瞼下垂について

眼瞼下垂とは、まぶたを引っ張り上げている筋肉が伸びてしまって弱くなっている状態だとお考えください。
筋肉をゴムに例えますと、ゴムが古くなり伸びてしまっている状態を想像してください。このゴムが再び引っ張り上げる力を回復するためには、ゴムを短くして再びつけ直せばよいですね?
眼瞼下垂手術は、まぶたの中の板(瞼板)に付着しているゴム(眼瞼挙筋腱膜)を探し出し、短く縫いつけることによって再び張力を回復する手術です。
眼瞼下垂(がんけんかすい)手術|診療・手術案内 - 森井眼科医院

この縫い付け課程で事実上 二重まぶたに整形することになる
で、この手術は条件を満たせば保険適用になる

つまり... 保険適用で二重整形を?

実際に行った手術について

手術についての説明はこちらが分かりやすい

【挙筋前転法(クイックデカ目術)】目を大きくし、より綺麗でパッチリとした二重に | 美容整形なら東京中央美容外科【TCB公式】

僕が受けたのは「眼瞼下垂手術(眼瞼挙筋前転術)」で、 ここでいう「眼瞼挙筋前転術切開法(切る挙筋前転法)」に該当する(ページ内検索してね)

ここでは「挙筋前転法(クイックデカ目術)」と記載されていたりして想像以上にフランクだがやっていることは同じである

手術へのモチベーションなど

もともと目が細く人相が悪いのもあるし、面白そうだったのでやることにした

気持ち:

  • 3割 話のネタに悪くない
  • 3割 顔面のメジャーバージョンアップするか
  • 2割 偏頭痛改善したい
  • 2割 本当に保険適用できるのか試したい

ダウンタイムとか知り合いにどう思われるかとか、そういった社会的な懸念は全くなかった
それは人と会わないからとかではなくて、単純に知り合いの整形は面白イベントだろうとフルオープンにしていたから

会社の人には「整形してくるんで楽しみにしていてください!」と伝えていた
皆楽しみにしてくれていた

いきなり閑話休題

僕の家は父が一重、母が二重、そして僕は一重、妹も一重だ

僕はさておき、妹は既に二重に整形している
その時のエピソードが気に入っていて、妹が「私が一重なのは遺伝的に誰のせい?」と父を問い詰めた手術費用(自費)をせしめたというもの

ろくでもない妹だと思う

僕自身今回の手術費用(保険適用)は自分の財源から出しているが、親に対して「孫の顔が見たいなら美容整形で顔全部弄るための費用を工面してくれたらどうにかなるかもね」と言ったことはある

ろくでもない兄妹だと思う

次の親戚の集まりにおける父の立場が心配で仕方ない
(母親は僕同様に奔放な性格なので寧ろ僕の目のことを面白がって喧伝するだろう)

病院の選定

そんな大層な選定をしたわけじゃないけど、3つ軸にした

デザイン性の要望を聞いてくれるか

デザイン性って何?というと二重の幅とかのことを指す

保険適用で眼瞼下垂手術をする場合には、病院としては必ずしもデザイン性の話を聞く必要がないみたい なのでそこらへん見てくれそうなとこを探した
探すといっても明記しているところはないから結局は勘だけど

僕は二重にするにしてもド派手な幅になると流石に心苦しさがある
そこをうまいことやってくれる病院を探そうというのが狙い(ちょっとずるいけどね)

※例えば当記事冒頭「眼瞼下垂について」で記載した眼科なんかは

※当院の眼瞼下垂手術は美容(整容)目的ではありません。

と謳っている。僕みたいな人間の場合、こういうところは避けたほうが安心だろう

病院のレビューがマシか

GoogleMapで病院のレビューをそこそこ見た 正直いって整形外科のレビューなんて精神科くらいアテにならないだろうと思いつつ、 「院長の態度が悪い」等々の記載が多いところは避けた(自分の機嫌を重視するスタイル)

病院のサイトのクオリティ

仕事柄、病院のサイトのできも見た
作りがしっかりしているサイトはそれ則ちSEOも重視している訳でレビューに変なことが書かれないよう診察も丁寧なはず

結局どこにしたのか?

院名出していいのかわからないから伏せる(個人的に聞いて)

2日にかけて3時間くらい調べて決めた

診察から手術まで

こんな流れ

  1. 診察: 4月半ば(いつだったか忘れた)
  2. PCR: 5/2(執筆時は2021年!)
  3. 手術: 5/4
  4. 消毒: 5/5(手術翌日)
  5. 抜糸: 5/11(手術1週間後)
  6. 経過観察: 6/12(抜糸1ヶ月後くらい)

おおよそPCR~手術~抜糸までのスパンは固定なはず
診察~手術までの期間は予約状況によるのだろう

診察

そもそも保険適用になるのか不安だったけど余裕で判定が出た
調べればヒットすると思うけど、瞳孔にどれくらいまぶたがかかっているかとかを診ていたみたい

自費だと60万くらいの手術が保険適用によって5, 6万。そこからIT健保の力により自己負担2万で済む結果になった
今回は正直お金の大小より保険適用できるかどうかの実験という趣が強かったのだけれど、60万か!と思うとなかなか悪くない気分

上述のデザイン性についても相談ができて、無事そこまで派手にならない方向で調整してもらえた
実際の仕上がりはどうイメージさせてもらえるかというと、まぶたの目頭のあたりをターゲットに箸のように細い金具を当てがい、それを軽く押し込むことで二重線を出すような形
図解するほどのことでもないので一生懸命イメージしてほしい
僕は白目を剥くように目に力を入れると一時的に二重まぶたにすることが出来たので、それをやった上で「これくらいにしてください」とお願いした

さて実際の術式としては眼瞼挙筋前転法というものになったが、これは冒頭に述べたとおりである

最終的に「手術やりますか?」と聞かれ「やります」と即答して実施が決定

日を改めPCRを受け、その後に手術を実施した

手術

局所麻酔で痛くないというけれど、ちょくちょくが麻酔切れて痛かった
僕は特別に麻酔が効きにくい体質というわけではない
鼻中隔湾曲症の手術の局所麻酔で鼻の中から大量のガーゼを抜く処置(30分もかからない)より痛く辛かった

皮膚の切除込で1時間半くらいみたいなことを謳っている整形外科を見かけたが、僕の場合は3時間 ちゃんと事前にトイレに行っておいて良かった

皮膚はレーザで焼き開けられ、その際に人肉の焦げる臭い
これは耳鼻科で経験した臭いなので寧ろ懐かしさすら感じられる

度重なる麻酔注射の際には看護婦さんが子守唄のように肩をトントンと叩いてくれていた
「(僕は幼児か何かか)」と照れくさく感じつつも、存外それが心地よく、
肩トントンがなくなると「(おいおいもっと続けてくれよ。まだ注射痛いよ)」と思ったりもしていた
自分のよくわからない感情に気づく貴重な機会をも美容整形は提供してくれるらしい

それはさておき痛いし疲れた

人間の目は基本的に二つ備わっている
これがどういうことかというと右目で感じた痛みと近いものを左目でも同様に感じることになるということで、これがまた恐ろしい
ガンダムのプラモデルの脚や腕パーツを左右セットで作る時のことが思い出される

世の中の整形美人をその言葉通り揶揄する人もいるけど、 最終的にこれだけ痛い思いを繰り返してるのはなかなかの精神力のある人たちなんだな。と尊敬するよう気持ちを改めた

術後経過

手術直後は多少瞼がピリピリ・ジンジン・チクチクする程度だったけど怖かったので処方されたロキソニンを飲んだ
(その後痛みが増すことはなかった)

それより目全体にガーゼがかけられ、その状態で電車に乗る責苦が待っている
と普通なら思うだろうが僕は比較的恥知らずなところがあるので、そこはあまり問題ではなかった

翌日来院してガーゼを取ると目がとんでもなく腫れていて、とても外に出られる容姿ではなかった
が、今回もその状態で電車に乗らないといけない。恥知らずでよかった

ガーゼがついている手術後の夜はガーゼを濡らさなければシャワーしてもいいらしい
翌日ガーゼを取ってからは顔を濡らしてのシャワーもOK。まぶたも優しく洗うようにとのこと
軟膏を塗ったり薬を飲んだりする

術後5日後くらいには割と落ち着いていてやっとスーパーで買い物
それまでは一切家から出なかった

抜糸

パチパチとハサミで糸を切っていく
多少ちくりと引っ張られる感触はあるが痛いということはない
あっさり施術は終わり1ヶ月後の経過観察の予約をとった

副作用的なもの

特にないと言えば嘘になる
しかしそれぞれ腫れの引きと共にじわじわ治まる

多少目が突っ張る感じがする(そういう処置をしているのだからそりゃそうだという話ではある)
これは術後経過と共にほぼ気にならなくなるが、ギュッと目を瞑るとまだその感覚はある
手術が失敗、あるいは腫れが酷いと寝る時に目が閉じられなくなる?!みたいな話もたまに聞くけど、それはなかった

そして術後すぐは左目から妙に涙が滲み出て、しばしば視界を遮った

また、目の腫れによって目を閉じた時の密閉性が落ちたのか、あるいは二重のラインが問題なのかシャワーの時にやたら目に水が入る
最初は辛かったが徐々にマシになっていった

感想

術後経過の写真を撮る内に段々見慣れて「あんま変わってないんじゃない?」と思っていたが、

リモート越しに僕の顔を見た会社の人曰く
「目が綺麗になった」
「明らかに前より目が大きい」
「次どこいじる?」

と、それなりに違いがあるみたい

僕は別に美容整形に抵抗があるわけではなく、どうせやるならやりまくった方が面白いと思っているけど、やるなら次はどこをいじろうかな
目頭か、なんかの注入で鼻を高くするか、鼻を切って小さくするか、唇を切除して薄くするか

術後経過の写真

わざわざ部屋の真ん中に三脚とライトスタンドを立てて、おおよそ同じ条件で観察を続けた
(2週間も部屋の真ん中に機材が置いてあるのはかなり邪魔だった)

被写体が固定できていないのでそれなりにブレはあるけど、術前はすっぴん・術後はバッチリメイクみたいな例よりは遥かに参考になるだろう

と思いきや風呂上がりに撮ったり、目の力の入れ具合が微妙に違ったりで見た目の印象がそこそこ違うっぽいことに後から気づいた
その辺はご容赦いただきたい

f:id:omdwn:20210516175725j:plain
術後経過

手術前はちょうどストロボのキャッチライトが入らず、より一層目つきが悪い

【C#】List<T>(Tはユーザ定義型)でパラメータを受ける実装を考える(考えるだけ)

はじめに

List<T>(Tはユーザ定義型)でパラメータを受ける実装を考える

その際にどうすれば処理が散らからないようになるのか?とか何がベストプラクティスなのか?とか考える
考えるだけ

VS起動しないでコンパイラ通さず書いたコードなので通らなかったらごめんなさい(暇な時に実際つくる)

長いあらすじ・問題提起

例えばこういうパラメータクラスと、それをパラメータとして受ける(≒モデルバインドする)コントローラ及びメソッドがあるとする

public class Param
{
    List<Person> PersonList {get; set;}
}

public class Person
{
    public string Name {get; set;}
    public int Age {get; set;}
}
public class SampleController : Controller
{
    [Route("/Sample")]
    public IActionResult Index(Param param)
        =>  Content("hoge");
}

ここでいうList<Person> PersonListがユーザ定義型のリストになる

そうしたコントローラに例えばこういうリクエストをすると

# 分かりやすく改行
/sample
?personList[0].Name=john&personList[0].Age=10
&personList[1].Name=tom&personList[1].Age=9 # name は省略
&tom&personList[1].Age=0

PersonListはこんな感じのオブジェクトとしてバインドされる

// param.PersonList =
new List<Person>()
{
    new Person {
        Name = "john",
        Age = 10
    },
    new Person {
        Name = "tom",
        Age = 9
    },
    new Person {
        Name = "",
        Age = 9
    },
}

次にこのList<Person>の中には「必ず1人は名前ありかつ10歳以上のPersonが含まれていないといけない!」というValidationルールを設ける
※例えば体験型施設の申し込みとか、そういうものに応募する時みたいなイメージ
(そうするとPersonという命名がイケてないけど、今回は許して)

そんな条件はアプリケーションサービスの中で弾け!という話かもだけど、今回はモデルバインド時のValidationで対応しよう
例えばこのWebアプリケーションがガッツリ体験型施設申し込みサービス、みたいな感じだったらList<Person>という括りを強固に保有し、そこにValidationをかけることにも違和感は少ない(と思うよね!)

public class Param : IValidatableObject
{
    List<Person> PersonList {get; set;}

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        ValidatePerson().ToList().ForEach(x => yield return x);
    }

    private IEnumerable<ValidationResult> ValidatePerson()
    {
        // error! null or empty
        if (PersonList == null || !PersonList.Any())
        {
            yield return new ValidationResult("", new[] { nameof(PersonList) });
        }

        // rule: at least one person has name and over the age of 10 is required
        var namedOver10AgePerson = GetNamedPersons().Where(person => person.Age >= 10);
        if (!namedOver10AgePerson.Any())
        {
            yield return new ValidationResult("", new[] { nameof(PersonList) });
        }
    }

    // something additional method
    // public: it could be used in other classes
    public IEnumerable<Person> GetNamedPersons()
        => this.PersonList.Where(person => !string.IsNullOrEmpty(person.Name));

}

こんな感じにValidationとか色々なメソッドが増えるとParamクラスがどんどん肥大化していってしまう

大抵の場合はこのまま実装すると思うんだけど、Paramクラスの中に他のフィールドも増えだすと散らかり具合が大変なことになってくる

#regionで臭いものに蓋をせずに問題に向き合おう

対応策1 Listの拡張クラスをつくる

これを防ぐためには方法が2通りあるかな。と思う

まず1つ目の方法は諸々の処理をList<Person>の拡張クラスに逃がすこと

public class Person
{
    public string Name {get; set;}
    public int Age {get; set;}
}

public static class PersonListExtensions
{
    public static IEnumerable<ValidationResult> ValidatePerson(this IEnumerable<Person> value)
    {
        // error! null or empty
        if (value == null || !value.Any())
        {
            yield return new ValidationResult("", new[] { "PersonList" });
        }

        // rule: at least one person has name and over the age of 10 is required
        var namedOver10AgePerson = value.GetNamed().Where(person => person.Age >= 10);
        if (!namedOver10AgePerson.Any())
        {
            yield return new ValidationResult("", new[] { "PersonList" });
        }
    }

    public static IEnumerable<Person> GetNamed(this IEnumerable<Person> value)
        => value.Where(person => !string.IsNullOrEmpty(person.Name));
}

やっていることは簡単かな

でもList<Person>という括りがちょっと弱いかも
List<Person>に名前がついていないから、このリストが即ち何を表したオブジェクトなのかが伝わりにくい?

対応策2 Listを有する値オブジェクトをつくる

2つ目の方法はList<Person>をまるっと一つのクラスのようにすること
DDD文脈だと値オブジェクトとか言われてる気がする

public class Param
{
    Persons PersonList {get; set;}
}

public class Persons
{
    List<Person> Values {get; set;}
    // Maybe I should use Complete Constructor Pattern
}

public class Person
{
    public string Name {get; set;}
    public int Age {get; set;}
}

で、Personsに色々処理をもたせる

public class Persons : IValidatableObject
{
    List<Person> Values {get; set;}

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        // error! null or empty
        if (this.Values == null || !this.Values.Any())
        {
            yield return new ValidationResult("", new[] { nameof(Persons) });
        }

        // rule: at least one person has name and over the age of 10 is required
        var namedOver10AgePerson = GetNamed().Where(person => person.Age >= 10);
        if (!namedOver10AgePerson.Any())
        {
            yield return new ValidationResult("", new[] { nameof(Persons) });
        }
    }

    public IEnumerable<Person> GetNamed()
        => this.Values.Where(person => !string.IsNullOrEmpty(person.Name));
}

実際はPersonsってよりもっといい名前をつけよう

こっちだとより収まりが良い 気がする
気がするけど論理的に説明できない

クラスというかドメインに責務がまとまっていて、
責務というかそうした振る舞いを定義するという点ではDDD的にきれいだと思う

しかし一番の問題は自前でカスタムモデルバインダを定義しないとパラメータをPersonsにモデルバインドできないこと!!
この実装の話はまた別途

まとめ

どっちがいいんでしょうね

List<Person>を値オブジェクトとするパターンはその括り(リスト)に名前をつけられることに強い意味があるように思う

実際運用していくとメリット・デメリットがはっきりしてくるんでしょうけど、まだこうした実装に水をあげ花を咲かせたことはない

【Terraform】【AWS】【RemoteDevelopment】Terraform+AWSな環境をDevContainerでつくった

あらすじ

github.com

Terraformを触ったことがなかったので環境をつくりたかった
当然ローカルを汚さないよう...このRemoteDevelopmentでな!

Githubに載せてるので見れば概ねわかると思いつつも簡単な説明や補足とか

参考文献はReadMeの方もみてね

概要

ベースとするDockerImageはAWS CLI(なんでかというと後述)

その上にTerraformをインストール

AWSの認証情報はDevContainerのホスト側の環境変数に設定(or .devcontainerにベタ書き)しておいてコンテナに伝播させる

というのもファイル見たほうがはやい

作るときのメモ

ベースとするイメージについて

本当はterraformのイメージをベースにして、その上にAWS CLIを載せたかった(AzureとかGCPに切り替えやすそうだし)

しかし...

terraformのイメージにはlight(latest)とfullがあって、後者でないとコンテナにログインできないみたい?
(少なくとも僕は/bin/bashがないと怒られた)

そしてfullのイメージはサイズ大きいしバージョン指定できなそうだった(常にlatestのfullっぽい)
バージョン固定したいので断念

参考:https://qiita.com/cyack2002/items/15216503178f5601e578

ビルド引数への環境変数の伝播について

そこそこ苦労した内容を↓にまとめた

omdwn.hatenablog.com

でも.envの方がよくない?って思い始めたのでいつか変えそう

terraform使ったことなくて拡張機能とか色々あると思うので、そのへん追って追加したい

実行イメージ

記事の内容がスカスカだし折角だからterraformを実行したときの出力でも貼っておこうかな!

bash-4.2# terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/aws...
- Installing hashicorp/aws v3.36.0...
- Installed hashicorp/aws v3.36.0 (signed by HashiCorp)

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
bash-4.2# terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_s3_bucket.HogeSampleImage will be created
  + resource "aws_s3_bucket" "HogeSampleImage" {
      + acceleration_status         = (known after apply)
      + acl                         = "private"
      + arn                         = (known after apply)
      + bucket                      = "hoge-sample-images"
      + bucket_domain_name          = (known after apply)
      + bucket_regional_domain_name = (known after apply)
      + force_destroy               = false
      + hosted_zone_id              = (known after apply)
      + id                          = (known after apply)
      + region                      = (known after apply)
      + request_payer               = (known after apply)
      + website_domain              = (known after apply)
      + website_endpoint            = (known after apply)

      + versioning {
          + enabled    = (known after apply)
          + mfa_delete = (known after apply)
        }
    }

Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_s3_bucket.HogeSampleImage: Creating...
aws_s3_bucket.HogeSampleImage: Creation complete after 3s [id=hoge-sample-images]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

【Docker】【RemoteDevelopment】DevContainerのbuild.argsに環境変数を渡したいけどうまくいかないときのメモ

はじめに

https://code.visualstudio.com/docs/remote/devcontainerjson-reference

VisualStudioのドキュメント的にはこんな感じでホスト側の環境変数をRemoteDevelopmentもといDevContainerのビルド引数に渡せるはずなのだ

"build": {
    "args": { 
        "HOGE": "${localEnv:HOGE}"
    }
},

けど、どうにもうまくいかないときのメモ

環境はmacOS Catalina 10.15.7

結論

.zshrcだの.zshenvに設定した後でDockerを再起動するといいかも?

解決

まずexport HOGE=xxxで一時的に環境変数を設定しても上記の方法ではビルド引数のlocalEnvとして拾ってもらえなかった

※ DevContainerというかRemoteDevelopmentを使わず普通にDockerをビルドするときにはexportでいいみたいだけど
参考:https://techblog.recochoku.jp/1979

なので結局.zshrcだの.zshenvだのに設定する
設定した後にはsource ~/.zshrcとかを忘れずに

そうしてもビルド引数に設定したlocalEnvに拾われず、でも以前から設定してあるUser(->${localEnv:User})なんかは普通に通る

最終的にDockerを再起動したらうまく拾われるようになった
根本的な解決になってない気がするけど動いたからまあいいや
誰かの参考になれば...