OmniAICreator/Qiita-1.07M huggingface dataset:
新卒未経験から情シスへ

文系大学出身がエンジニアってどんな仕事？レベルで入社した未経験の新卒がいきなり情シス配属された経緯や感想を記載しております。

### 入社するまで
コロナの影響もあり、もともと気になっていた職種の募集が無くなってしまいました。
そのタイミングで就活の時期が来て、どうしたらいいのかわからないことだらけでしたが、
自分自身どういうところで働きたいか、何を大切に仕事をしたいか等の軸を決め、職種を絞らず就活を始めました。

私の就活の軸は3つで、
①自分自身が成長し続けられる仕事、環境であること
②常に挑戦し続けられる仕事、環境であること
③人間関係が良いところ
でした。

最終エンジニアになるか、コンサルタントになるかの２択になり、先輩たちとの面談を組んでもらったり、ご飯に連れていってもらったりし、今の会社に就職（エンジニアになること）を決めました。
内定承諾したものの、今まで英語しか勉強してこなかったので、
知らない世界に飛び込む不安があったものの、ワクワクの方が大きかったです。
入社後に1ヶ月の研修があるとは聞いているものの、知らないことが多すぎたので
とりあえずITって何？というところから調べ、ざっとエンジニアって何をするのか自分なりに理解してから入社しました。

### 入社後の研修
入社後に行われた研修では、IT業界の仕組みやビジネスマナー、技術的な用語やテスト等、未経験でもエンジニアとして働けるような基本的な知識をたくさん教えてもらいました。
少しでも他エンジニアに劣らず働けるようになりたい！と、一生懸命インプットを行い、研修後の自分を想像しながら研修を受けていました。

1ヶ月の研修の終盤、入社2日目に行ったテストと同じテストを行い、
どれだけ理解できるようになったかを測りました。
テストが早く終わり、自習を始めていると
「ちょっといいですか」と研修講師の方に呼び出され、
「来月から情シス課を新設するんだけど、やってみない？」とお誘いをいただきました。

お誘いをいただくまで、サーバー系の仕事かな？運用保守とかから始まるんかな？としか頭にありませんでした。
”情シス”って何？？ここに来て初めて聞くワードなんですけどーーー！！と
考えていたキャリアにはない選択肢だったのでとても混乱しましたが、私の研修態度や努力を見ての判断だということ、未経験で配属されるエンジニア業務より質の高い経験を得られることなど、いろいろお話いただき、そのお誘いを受けることにしました。

### 情シスに配属されて
配属が決まったものの、具体的な仕事内容が全く分からず、「情シスとは」とネットで検索するところから始まりました。
ヘルプデスク？資産管理？え？どれ？？状態で、上司からの指示をもらった上にどうしたらいいか教えてもらって仕事する毎日。
大学までしていたバイトなら1ヶ月あればどう動いたらいいのか理解し、自主的に行動できていたのに全然通用せず、ひたすらわからないことだらけ、、、、
振られる仕事も、他部署から飛んでくる業務も、特定の分野ではなく、いろんなことを求められるのでひたすら調べて情報集めて出すの繰り返しをしていました。
日々教えてもらうことも多く、気づいたらメモを取っていたノートはパンパンになっていました。

現在入社して9ヶ月目。やっと定常業務がある程度1人でも行えるようになりました。
また、業者選定や、サーバー移行、新サービスの導入をお手伝いさせてもらうなど、
+αで飛んでくる業務も、何度もやり直しをくらうこともありますが、一生懸命くらいついて取り組んでいます。とてもありがたい経験だなと感じてます・・
自分自身が就活の際に求めていた常に挑戦し続けられる仕事や環境は、今まさに手に入れられていると思うので、これからもがむしゃらに、目の前の仕事に取り組んでいきたいと思います！
ただ、全然資格の勉強ができていなかったので、今年は資格勉強も頑張っていきたいです:punch:

ご覧いただきありがとうございました。
勉強メモ

## メモ

https://qiita.com/k-mat438/items/7d1b8920a3f51c6859f5

https://qiita.com/k-mat438/private/172622ba60ed4b2b062e

https://qiita.com/k-mat438/private/e9d4d134b7dadbdf78f9

https://qiita.com/k-mat438/private/01bf39dabd7558201fac

https://qiita.com/k-mat438/private/b23576e6513cf014e220
2023 年の振り返り：私が使っていた最も便利な 10 の効率ツール

1. Craft.do: その Daily Note は基本的に私の GTD ツールを取って代わりました。
2. Cron: 私の予定や仕事はすべて同期され、Calendly の代わりに人との約束や時間の管理ができました。
3. MindAI.jp : AI とマインドマップツールの素晴らしい組み合わせで、特にモバイルでの体験が素晴らしいです。
4. Arc: 言うまでもなく、現在最も優雅なブラウザです（Sigma.os も良いですね）。
5. Perplexity.ai: 言葉を多く使うことなく、日常の知識検索をサポートするツールです。
6. Journal: 私の日記はこれで十分で、記録の冲動をもっと高めました。Dayone の代わりに使っています。
7. Raycast: 様々なプラグイン機能を組み合わせて、効率が向上しました。
8. Pitch: 神秘的なモバイル機能が登場して以来、プレゼンテーション作成に積極的に利用しています。
9. Superlist: 一貫して内部テスト中で、タスクのネスト化と詳細ページはドキュメントとしてのデザインに非常に適しています。
10. Speak: 英語学習の良いサポートです（felo.me も良いですね）。
HuggingFaceの読込速度の比較（ローカルパス、キャッシュ、pickle）

# 概要
HuggingFaceのモデルのロード速度を3つの方法で比較しました。

* ローカルパス：model_nameにダウンロードしたものモデルフォルダのパス（e.g. "./mulitlingual-e5-large")を指定してロード
* キャッシュ：model_nameにURLのモデル名（e.g. "intfloat/multilingual-e5-large"）を指定してキャッシュ済みのモデルをロードする
* pickle: pickleでダンプしたインスタンスをロード

結果としてはpickleが一番早かったです。ローカルパスとキャッシュは同じくらいでした。

# 背景
HuggingFaceのモデルをなるべく早くロードできる方法が何か知りたくて調べました。
# 環境
* M1 MacBookAirのメモリ16GB、SSD 1TB

# 実装
`intfloat/multilingual-e5-large`の読込み速度を比較します。2GB以上あるので、より軽量なモデルで試したい場合は適宜、変更ください。

## ローカルパス
huggingface_hubを使ってモデルをダウンロードします。
参考: https://wonderhorn.net/programming/hfdownload.html

```Python
import huggingface_hub

MODEL_NAME = "intfloat/multilingual-e5-large"
LOCAL_DIR = "./model/me5_local"

model_id = MODEL_NAME  # 落とすモデル名
local_dir = LOCAL_DIR  # 保存先フォルダ名
huggingface_hub.snapshot_download(model_id, local_dir=local_dir, local_dir_use_symlinks=False)
```

時間を計測します。楽をするためにモデルのロードにはlangchainのHuggingFaceEmbeddingsを使います。

```Python
from langchain.embeddings import HuggingFaceEmbeddings
import time

# 時間を測る
start = time.time()
# モデルを読み込み
model = HuggingFaceEmbeddings(model_name=LOCAL_DIR)
# 時間を出力
print("local:", time.time() - start)
```

```
local: 7.256836175918579
```

## キャッシュ

モデル名をそのまま指定してロード速度を測ります。注意点として、もしキャッシュが存在していない場合は、一度ロードを実施してキャッシュを作成しておきます。

```Python
from langchain.embeddings import HuggingFaceEmbeddings
import os

MODEL_NAME = "intfloat/multilingual-e5-large"
# もしキャッシュがなければ、モデルをダウンロードしてキャッシュする
if not os.path.exists(".cache/huggingface/hub/models--intfloat--multilingual-e5-large"):
    model = HuggingFaceEmbeddings(model_name=MODEL_NAME)

# 時間を測る
start = time.time()
# モデルを読み込み
model = HuggingFaceEmbeddings(model_name=MODEL_NAME)
# 時間を出力
print("cache:", time.time() - start)
```

```
cache: 5.98180627822876
```

## pickle
ダンプ済みのインスタンスをロードします。

```Python
from langchain.embeddings import HuggingFaceEmbeddings
import os
import pickle

MODEL_NAME = "intfloat/multilingual-e5-large"
PICKLE_FILE = "me5.pkl"

model = HuggingFaceEmbeddings(model_name=MODEL_NAME)
# pickleで保存する
with open(PICKLE_FILE, "wb") as f:
    pickle.dump(model, f)
    

# 時間を測る
start = time.time()
# モデルを読み込み
with open(PICKLE_FILE, "rb") as f:
    model = pickle.load(f)
# 時間を出力
print("pickle:", time.time() - start)
```

```
pickle: 2.64107608795166
```

# 結果
出力だけまとめると以下のとおりです。

```
local: 7.256836175918579
cache: 5.98180627822876
pickle: 2.64107608795166
```

1回しか計測していないため誤差の範囲かもしれませんが、pickleが一番早かったです。バージョンやOSが固定で安定性が確保できるなら、pickleを使うのはありかもしれません。
PHPとPythonの比較勉強ー複数行にわたる入力 (paizaランク D 相当)

https://paiza.jp/works/mondai/c_rank_level_up_problems/c_rank_std_in_out_step3

PHPを仕事であんまり使わなくなって今Pythonをやっているんですよね。
で、やらなくなっているとなんというか腕が鈍ってしまったりしまして。
それだとだめだなと思ったので今年から1日1回はPHPも解くようにしました。

ひとまずPythonで書いてみます

```py

n = int(input())
for _ in range(n):
 print(input())
```
簡単ですね。
では次にPHP

```php
<?php
    $n = fgets(STDIN);
    for ($i=1; $i<=$n; $i++){
        print(fgets(STDIN));
    }
?>
```
一応正解はしたのですが。
fgets(STDIN)で取得したものはtrimしないといけないみたいです。
なんでなのか調べたら、どうやら、入力されたときの改行を除去するため、という
意味があるそうです。
問題文にも「改行区切りで与えられるので、、、」というのがありますね。
だから改行を取り除くためにはtrimが必要ということだそうです。
しかし、出力をみてみるとそもそも改行区切りになっているので、このままでも
問題なさそうです。(最初の$nはだめだと思うけど）

念のため改行をtrimでとったあとに改行を付けて出力する場合

```php
<?php
    $n = trim(fgets(STDIN));
    for ($i=1; $i<=$n; $i++){
        $a = trim(fgets(STDIN));
        print($a . "\n";
    }
?>
```
ちなみにSTDINの意味はstandard inputの略で、えすてぃーでぃーあいえぬと呼ぶらしいです。
ええ。。。。なんとなく読みづらいですね。。。
標準入力なんですけど、たとえばコマンドでなにか入力してEnter押すと動くやつがありますが、あのときの入力が標準入力です。もちろんその逆もあり、Enterを押した後出てくるのがそれで、標準出力（STDOUT)ですね。
paizaラーニング解答: ループメニュー1[Ruby]

# ループメニュー1
https://paiza.jp/works/mondai/loop_problems/problem_index?language_uid=ruby

ループ処理は他の問題集を解くために必須となります。
問題も易しいので標準入出力ができるようになったらすぐ取り組んだ方がいいですね。
## STEP: 1 ある数字までの出力 1
```ruby
(1..10).each do |i|
    puts i
end
```
eachループ
## STEP: 2 ある数字までの出力 2
```ruby
n = gets.to_i
for i in 1..n
    puts i
end
```
for inループ
## STEP: 3 数字の受け取り 1
```ruby
a = gets.split.map(&:to_i)
puts a
```
putsは配列の要素を改行して出力するのでループ処理のように使うことができます。
## STEP: 4 数字の受け取り 2
```ruby
n = gets.to_i
a = gets.split.map(&:to_i)
a.each do |i|
    puts i
end
```
配列から要素を取り出す繰り返し処理はeachだと簡単に記述できます。
## STEP: 5 ある数をある回数表示 1
```ruby
8.times do
    puts 3
end
```
timesループ
## STEP: 6 ある数をある回数表示 2
```ruby
n, k = gets.split.map(&:to_i)
k.times { puts n }
```
do endと{}はほぼ同じように使えます。
## FINAL問題 a ~ z までを表示
```ruby
("a".."z").each { |i| puts i }
```

## STEP: 1 数列の和
```ruby
n = gets.to_i
a = gets.split.map(&:to_i)
puts a.sum
```

## STEP: 2 数列の A 番目から B 番目までの和
```ruby
n, a, b = gets.split.map(&:to_i)
array = gets.split.map(&:to_i)
puts array[a - 1..b - 1].sum
```

## STEP: 3 数列の値を全部 * 2 して出力
```ruby
n = gets.to_i
a = gets.split.map(&:to_i)
a.each { |i| puts i * 2 }
```

## FINAL問題 数列同士の引き算
```ruby
n = gets.to_i
a = gets.split.map(&:to_i)
b = gets.split.map(&:to_i)
n.times { |i| puts a[i] - b[i] }
```

## STEP: 1 数列の反転
```ruby
n = gets.to_i
a = gets.split.map(&:to_i)
puts a.reverse
```

## STEP: 2 九九の表示 1
```ruby
steps = []
(1..9).each { |i| steps << 8 * i }
puts steps.join(' ')
```

## STEP: 3 九九の表示 2
```ruby
n = gets.to_i
steps = []
(1..9).each { |i| steps << n * i }
puts steps.join(' ')
```

## STEP: 4 2 の累乗を表示
```ruby
n = gets.to_i
(1..n).each { |i| puts 2 ** i }
```

## FINAL問題 FizzBuzz
```ruby
for i in 1..100
    if i % 15 == 0
        puts "FizzBuzz"
    elsif i % 3 == 0
        puts "Fizz"
    elsif i % 5 == 0
        puts "Buzz"
    else
        puts i
    end
end
```
FizzBuzz問題はpaizaラーニングの講座で詳しく取り上げられています。講座内で使用するプログラミング言語は違いますが、説明に沿って取り組めば問題ありません。

https://paiza.jp/works/algorithm/primer/algorithm1
## STEP: 1 数列の最大値
```ruby
n = gets.to_i
a = gets.split.map(&:to_i)
puts a.max
```

## STEP: 2 数列の最小値
```ruby
n = gets.to_i
a = gets.split.map(&:to_i)
puts a.min
```

## STEP: 3 数列の何番目にある?
```ruby
n = gets.to_i
a = gets.split.map(&:to_i)
puts a.index(1) + 1
```

## FINAL問題 数列の中に何個ある?
```ruby
n = gets.to_i
a = gets.split.map(&:to_i)
puts a.count(1)
```
秋月で買ったDCDCコンバータが結構消費電力を食っていた件

今回はDCDCコンバータが思った以上に電力を消費していたので記事にしてみた！

通常レギュレータは、電力を消費するイメージだが今回購入した
DCDCは、思った以上に電力を消費していた！

https://akizukidenshi.com/catalog/g/gK-16055/
この商品を購入して使用していたのだが
単体で26mAも消費していた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1339052/43cc165c-8697-2d82-68ec-e8ace225b816.png)

以前使用したDCDCコンバータは1mAも消費していなかったので
今回は驚いて記事にした。
https://www.ti.com/product/ja-jp/TPSM84203
以前のDCDCコンバータの消費。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1339052/97625c62-144f-62aa-f67b-b41a55b6771d.png)
単体で使用した場合は、消費電力が低すぎて0mAと表示している。

スペック表を見ても26mAも使わないはずなんだけどな・・・

皆さんも要注意して下さい。
iOS、Androidアプリでスクリーンショットを防ぐ

# iOS
そもそもスクリーンショットは端末の機能なのでデベロッパーの都合で禁止すべきではないし、完全に禁止というのはできない。スクリーンショットされたことを検知したりする機能もない。

画面の一部を隠すという機能もデベロッパーに提供されてはいない。

ただ、Appleが用意した`UITextField`の`isSecureTextEntry`をonにすると、そのテキストフィールドの部分だけはスクリーンショットに映らないという公式の機能がある。これをハックすると、画面の一部または全体をスクリーンショットに映らないようにすることもできる模様。

[Youtube - Prevent Screenshot - SwiftUI - Xcode 15](https://www.youtube.com/watch?v=viao1kTWBoY)
[github - yoxisem544/ScreenshotPreventing-iOS](https://github.com/yoxisem544/ScreenshotPreventing-iOS)

筆者自身も、スクリーンショットに映らないようにしてあるアプリをAppStoreで見つけたこともある。そのため、このような機能を入れてもAppStoreでリジェクトされないようである。ただ、このような利用方法をApple公式が想定しているわけではなさそうなので、いつまでも利用可能とは限らない。

またこのような機能を入れたところで、端末の画面を他の端末のカメラなどで撮影してしたり、パソコンや他のモニタに映した上でそのモニタ上でスクリーンショットを撮るなどはできてしまうと思われる。完璧な対策ではないので、そもそもスクリーンショットを撮られても大丈夫な設計にすべきだろう。

参考: [How to prevent/disable screenshots on iOS 17 beta ?](https://developer.apple.com/forums/thread/736112)

# Android
こちらもスクリーンショットそのものを禁止することはできないが、あるActivityについてスクリーンショットに映らないようにできる機能がある。

[Prevent other applications form capturing/recording screen](https://stackoverflow.com/questions/30617750/prevent-other-applications-form-capturing-recording-screen/30618030#30618030)
MacでESP32のデバッグをする

ESP32を開発時に、Arduino IDEのコンソールで表示されるBacktraceをちゃんと読み解く。

### 0. 試した環境

```
MAC: 12.5.1(Monterey)
Arduino IDE: 1.8.19
```

### 1. 環境設定

##### ツールをインストール

```
$ mkdir -p ~/esp
$ cd ~/esp
$ wget https://dl.espressif.com/dl/xtensa-esp32-elf-macos-1.22.0-97-gc752ad5-5.2.0.tar.gz
$ tar -xzf xtensa-esp32-elf-macos-1.22.0-97-gc752ad5-5.2.0.tar.gz
```

##### パスを設定

```.zshrc:
# 下記を適当な場所に追加
export PATH=$HOME/esp/xtensa-esp32-elf/bin:$PATH
alias get_esp32="export PATH=$HOME/esp/xtensa-esp32-elf/bin:$PATH"
```

##### .zshrcを読み直し

```
$ source .zshrc
```

### 2. コンパイル済みのファイルを取得

↓Arduino IDEでコンパイル時に下記のように出力されるパスを確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2915/695ca4fe-4ee1-8bbe-dbbc-9f6327dc615d.png)

※ もし、見つからない場合は下記でも探せるかもです。

```
$ cd /private/var/folders
$ find ./ -name "arduino_build_*" -ls 2> /dev/null

63836113        0 drwxr-xr-x   13 yuki             staff                 416  1  7 13:10 .//_l/sdy4_tn574g1_0pphk0rv0pm0000gp/T/arduino_build_368937
63836936        0 drwxr-xr-x   13 yuki             staff                 416  1  7 09:48 .//_l/sdy4_tn574g1_0pphk0rv0pm0000gp/T/arduino_build_526620
```

↑
上記の最新の日付っぽいものをもってくると良い気がします

### 3. 実行

※ 元のファイルが「hogehoge.ino」の場合

```
$ xtensa-esp32-elf-addr2line -pfiaC -e hogehoge.ino.elf 0x4008d65d:0x3ffcb5e0

↓ 下記の様な感じで出力される
0x4008d65d: esp_system_abort at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_system/esp_system.c:137
```

### 4. より効率のよいデバッグ

```
$ perl esp32_decode_codes.pl --elf=[elf_file_full_path] --codes=[0x4008388d:0x3ffcb5c0...]
```

下記にまとめてtraceのメッセージを出してくれるスクリプト置いときます

```perl:esp32_decode_codes.pl
#!/usr/bin/perl
#-----------------------------
# esp32のdumpをデコードしてエラーメッセージを確認するプログラム
#
# usage: $ perl esp32_decode_codes.pl --elf=[elf_file_full_path] --codes=[0x4008388d:0x3ffcb5c0...]
#-----------------------------
use strict;
#use warnings;
use XML::Simple;
use Data::Dumper;
use File::Find::Rule;
use Smart::Options;

my $xml = XML::Simple->new(ForceArray => 1);
my $argv = Smart::Options->new->parse;

unless (defined($argv->{'elf'}) && defined($argv->{'codes'})) {
  print 'usage: $ perl esp32_decode_codes.pl --elf=[elf_file_full_path] --codes=[0x4008388d:0x3ffcb5c0...]', $/;
  exit;
}

decode_codes($argv->{'elf'}, $argv->{'codes'});

sub decode_codes {
  my $elf_file_path = $_[0];
  my $codes_as_string = $_[1];

  $codes_as_string =~ s/Backtrace: //g;

  my @codes = split(" ", $codes_as_string);

  print "---------------------------------------------------", $/;
  print "elf_file_path ", $elf_file_path, $/;
  print "codes_as_string ", $codes_as_string, $/;
  print "---------------------------------------------------", $/;
  
  foreach my $code_as_string(@codes) {
    my $command = "xtensa-esp32-elf-addr2line -pfiaC -e $elf_file_path $code_as_string";
    
    print `$command`, $/;
  }
  print $/;

}

exit;
```

### References

https://docs.espressif.com/projects/esp-idf/en/release-v3.3/get-started/macos-setup.html

https://wamisnet.github.io/blog/2020-07-14-esp32-backtrace/

https://romly.com/blog/esp32_addr2line/
【Rails】マークダウン形式で入力された表をHTML側で表として表示してみた

どうもこんにちは。

今回は以下のように入力されたマークダウン形式の表をHTML側では表として表示できるようにしてみたいと思います。

```text
| タイトル | 値 |
| --- | --- |
| 名前 | テスト太郎 |
| 年齢 | 25 |
| 性別 | 男 |
| 出身 | 東京都 |
```

# 実装手順

## 1. gemを導入

今回は`Redcarpet`というGemを使用します。

```Gemfile
gem 'redcarpet'
```

## 2. bundle installをする

ターミナルで以下のコマンドを叩きます。

```
$ bundle install
```

## 3. ApplicationHelperを使用してメソッドを定義

メソッドは、HTMLで画面表示したい時に使用するので、ヘルパーメソッドとして定義します。
私は色々な箇所で使用したかったので、`app/helper/application_helper.rb`に定義しましたが、`users`の機能でのみ使用する場合などは、`app/helper/users_helper.rb`に定義すればOKです。

```ruby
def markdown_to_html(markdown_text)
    # markdown_textがnilの場合には、空文字を返す（エラーが出ちまうので）
    return '' if markdown_text.nil?
    # インスタンスを作成
    renderer = Redcarpet::Render::HTML.new
    markdown = Redcarpet::Markdown.new(renderer, tables: true)
    # マークダウンテキストをHTML形式に変換
    markdown.render(markdown_text).html_safe
end
```

## 4. HTMLでメソッドを呼び出し

テキストを引数にしてメソッドを呼び出します。
仮に、`@sample`にデータが入っているとし、`@sample.text`でtextカラムにマークダウン形式で入力された文字列が入っているとします。

```erb
<%= markdown_to_html(@sample.text) %>
```

# テーブルをカスタムしたい場合

簡単でしたね！

私はテーブルにボーダーを付けたかったので、Initializerを使用して別クラスを継承して定義しました。

## 1. Initializerにファイルを作成

`config/initializer`ディレクトリに`custom_redcarpet.rb`というファイルを作成します。（ファイル名は適当）

## 2. クラスを継承して定義

`custom_redcarpet.rb`に以下を記述します。

```ruby
class CustomRedcarpet < Redcarpet::Render::HTML
  def table(header, body)
    "<table class=\"markdown-table-bordered\">#{header}#{body}</table>"
  end
end
```

これでマークダウン形式の表をHTML形式の表に変換するときに、tableタグに`markdown-table-bordered`というクラスが付与されるようになりました。

## 3. ヘルパーメソッドを変更

先ほど作成したヘルパーメソッドを変更します。

```ruby
def markdown_to_html(markdown_text)
    return '' if markdown_text.nil?
    # ここを変更
    renderer = CustomRedcarpet.new
    markdown = Redcarpet::Markdown.new(renderer, tables: true)
    markdown.render(markdown_text).html_safe
end
```

## 4. markdown-table-borderedクラスを定義

application.cssにスタイルを定義します。

```scss
.markdown-table-bordered {
  border-collapse: collapse;
  width: 100%;
}

.markdown-table-bordered th {
  border: 1px solid #ddd;
  background-color: #ddd;
  padding: 8px;
  text-align: left;
}
.markdown-table-bordered td {
  border: 1px solid #ddd;
  padding: 8px;
  text-align: left;
}
```

## 5. Railsサーバーを再起動

Railsサーバーを再起動しないと、CSSとInitializerは読み込まれないです。忘れずに。

これで表が綺麗な表として表示されます！

| タイトル | 値 |
| --- | --- |
| 名前 | テスト太郎 |
| 年齢 | 25 |
| 性別 | 男 |
| 出身 | 東京都 |

以上
マークダウンエディタの実装手順【React】

Reactの学習にて
React専用のマークダウンエディタを使用しましたので
備忘録として記載させていただきます。

マークダウンエディタとは？
特定の記法を使うことで、見出しや協調などの文字装飾を行うことが出来る
非常に便利なテキストエディタです。

手順

```ruby:title.rb
npm install react-markdown
```
ターミナルに上記を実行します。
```ruby:title.rb
added 79 packages, and audited 350 packages in 12s

164 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
```
上記のような表記が出ればOKです。

```ruby:title.rb
import ReactMarkdown from 'react-markdown';
```
インポートを済ませて
```ruby:title.rb
<div className='markdown-preview'>{activeNote.content}</div>
```
上記のような使用したい部分を下記のように
```ruby:title.rb
<ReactMarkdown className='markdown-preview'>{activeNote.content}</ReactMarkdown>
```
divをReactMarkdownに変更して完成です。


▼今回使用したマークダウンエディタ
https://github.com/remarkjs/react-markdown
react-hook-form + zod でnullableかつメールアドレスの形式チェックを行う方法

# はじめに
Webサイトを作っている時、入力していない場合はバリデーションをかけず、一文字でも入力している場合はemailのバリデーションをかけるという流れで少し詰まったので備忘録として書き残しておきます。

# 問題
emailの入力フォームで
- 入力は任意(nullでもいい)
- 入力した場合はemailの形式であるかバリデーション
 という二つを両立させる方法がわからなかった
 
# 解決方法
<font color="red">**追記です**</font>

X(Twitter)でもっといい方法を教えていただきました！

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">The solution proposed in the referenced thread has always worked well for me.👌<br><br>z.string().email().optional().or(z.literal(&#39;&#39;))</p>&mdash; moshyfawn (@moshyfawn) <a href="https://twitter.com/moshyfawn/status/1744717567225405939?ref_src=twsrc%5Etfw">January 9, 2024</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

ということで、
```ts
z.string().email().optional().or(z.literal(''))
```
のみで今回タイトルにあるバリデーションは問題なく行えるようです！

そもそも正規表現を自前で書いた理由が、zodの`.email`では日本語の文字を許容してしまうという情報に基づいたものでしたが、バージョン3.22.0にて修正されていたようです🫨
実際に確認せずに古い情報を信じてコードを書くのはダメですね...

バージョン3.22.0以前であれば以下の対応で問題ないかと思われます。

`superRefine`を使用する。

https://zod.dev/?id=superrefine

```ts
const registerFormValidation = z
    .object({
        email: z.string().nullable(),
    })
    .superRefine(({ email }, ctx) => {
        if (email !== null && email.length > 0) {

            const regex =
                /^[a-zA-Z0-9_.+-]+@([a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.)+[a-zA-Z]{2,}$/;
            if (!regex.test(email)) {
                ctx.addIssue({
                    path: ["email"],
                    code: "custom",
                    message: "メールアドレスの形式が不正です",
                });
            }
        }
    });
```

まず、`email: z.string().nullable(),`で値がnullの時はバリデーションをとおるようにします。
そのあと、`superRefine`をemailの文字数が1以上の時正規表現を用いてテストします。
正規表現に当てはまらなかった場合、ctx.addIssueを登録してエラーメッセージを生成します。

# おわりに
もっといい方法があれば教えてください...!

# 参考サイト

https://zod.dev/

https://javadrive.jp/regex-basic/sample/index13.html
live-serverでMIME typeエラーが出た話

# はじめに
最小構成でwebアプリを作ろうと思い立ちgithub copilotを使いながら楽々作っていこうと思ったらつまづいたので備忘録として残しておきます。

## 開発環境
* Terminal Zsh
* VSCode Version: 1.85.1 (Universal)
* Google Chrome ver120.0.6099.129（Official Build） （arm64）
* npm v10.2.3
* node v20.10.0
* live-server 

## live-serverのインストール
live-serverはVSCodeの拡張機能でもありますが、ターミナルから実行したかったのでnpmでインストールしました。
```zsh:zsh
npm install -g live-server
```

## ディレクトリ構成
```
.
├── README.md
├── index.html
└── module
    ├── Hello.js
    └── time.js
```

## プログラム
以下のようなコーディングを行いました。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script type="module">
        import Hello from './module/Hello.js';
        
        Hello();
    </script>
</body>
</html>
```
```javascript:module/Hello.js
import time from "./time.js";
const Hello = () =>{
    console.log("Hello");
    time();
}

export default Hello;
```
```javascript:module/time.js
const time = () => {
    const currentTime = new Date();
    console.log(`現在の時間は ${currentTime.toLocaleTimeString()} です。`); 
}
export default time;
```
# エラー

ここでターミナルから以下のコードを実行し、live-serverを立ち上げました。
```zsh
$ live-server index.html
```
立ち上がったindex.htmlから検証モードに入り、コンソールを確認すると以下のように表示されていました。

```console:Chrome
Hello.js:1 Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.
```

>*訳)Hello.js:1 モジュール・スクリプトのロードに失敗しました： JavaScriptモジュール・スクリプトを期待しましたが、サーバーは "text/html "というMIMEタイプで応答しました。モジュール・スクリプトに対しては、HTML 仕様に従って厳格な MIME タイプ・チェックが実施されています。*

同様の現象がnginxなどを利用すると起きることがあるようです。一般的にこのエラーがでる場合、パスの指定が間違っていることなど原因として考えられるようです。今回はパスの指定について間違いはなくハマってしまいました。

# 解決策
おとなしくVSCodeのlive-serverから実行すると上手くいきました。
拡張機能からlive-serverをインストールし、(※筆者の場合)右下にあるGo liveから実行すると期待通りの結果が得られました。

# 終わりに
筆者はnpmなどについてまだ知識が浅い部分があり、htmlのtype=moduleなどについても最近の慣例程度としか認知しておらず、まだ勉強中です。詳しい原因などについて理解のある方でしたらぜひコメント等でアドバイスをお願いします。
元プログラマー自身の困惑 (ソースコードの整理は大変)

# はじめに(起)
現役をリタイヤーして早や、15,6年になります。今でも、趣味で、Webサイトのページを作成しています。そこで、自身に困惑しているなることがあります。これまで作成してきたソースコードは長年のメンテナンスでかなりゴミ交じりのコードになっています。そのたの適宜、ソースコードの整理を始めますがいつでも中途半端で終わっています。いざ、やり始めても、やることが多すぎてあきらめてしまいます。これまで、その繰り返しです。

# なぜできないのか(承)
理由はいろいろあります。
#### ・本気で時間をかけてやろうとする意欲が出ない。
理由は、趣味で行っていることが原因です。仕事であれば、ある程度の結果は出せるとは思いますが、
#### ・これも下手に手を出すと返って藪蛇になることもあります。
よく、システム更新時に起こる問題です。世の中のあるシステムでも問題になっています。プログラムコードはちょっと修正しただけでも、その後にとんでもないシステム事故になることがあります。
今の時代は車でも電気製品が多く組み込まれています。その中にプログラムが組み込まれていてときどき不具合が発生してリコール問題を引き起こします。何百万台の走行中の車を回収してプログラムを入れ替えなければなりません。
#### ・ゴミと化す不要コードが残存している問題(承)
不要コードなのか、必要なコードなのかの判別が簡単ではないため、どちらを選択するかと言えば残存させた方が安全。下手に削除してしまうと、後でundefinedというエラーが発生することがたまにあります。あの時、削除してしまったことが原因だつたということがあります。この不要か不要でないかを見極める手段としてalert()文またはconsole.log()を仕込んでおきます。ただし、alert()文は公開環境で使用することはできません。これは、すべてのパスをテストすることはほぼ不可能だからです。ステップ数が数千規模のプログラムになるとその組み合わせテストが無限とは言わないまでも膨大な数に上ることがあります。そのための罠を仕掛けておきます。それでも、すべての不要コードを見極めることは大変な作業です。

# 整理ツールは使えるか(転)
#### ・ソースコードの整理のためのツール
いろいろなツールがありますが、中々、ピッタリしたものはありません。
#### ・AIツールはどうか?
使ったことはありませんが、どうもしっくりきません。理由は、AIが考え出したものは、自分の脳で考えたものではないので、そのまま鵜呑みにしていると脳の低下になりはしないかと心配になります。昔のプログラマーなので、やはり、自分で考えだしたものでないと信用できないということがあります。なので、AIには頼りたくありません。
#### ・エディターのツールとか(転)
今使っているエディターにもツールはありますが、不要コードの削除というツールはありません。それは、それだけ対処が難しいということだと思います。
#### ・未使用変数のチェック(転)
表示上は、下線が出て、ホバーすると未使用ですという表示が出ています。それらは本当に未使用なので、削除してもいいかもしれません。
#### ・未使用の関数を見つけること(転)
未使用の関数を見つけることは比較的簡単です。エディターの検索機能で、コード全体で呼び出している関数があるかどうかをチェックします。ヒットすれば、使用していることになります。ヒットしなければ、使用していないことになりますので、削除できます。
#### ・削除しないでいったんコメントにしておく(転)
いきなり、削除してしまうと後で後悔することにもなります。もし、必要だったとわかっても元に戻せません。そこで、いったん、コメントにしておきます。見栄えは悪いですが安全かもしれません。

# 重要なこと(結)
ソースコードを整理して見直した場合、しばらくの期間は運用して様子を見ることになります。その期間にもしかしたら、修正ミスでエラーを発生したかもしれません。そのため、修正前のソースコードは保存しておきます。いつでも、復旧できるようにしておきます。



# 結論(結)
この困惑(ソースコードの整理)の今の結論としては、AIに頼ることは危険すぎるので、というより怖い(もし有効なコードが削除されてしまったらという不安)ので、使いたくはありません。確実な方法は自分で、検索、置換を用いて一括修正変更するとか、一件ずつ修正するとかして地道に行うことがいいのかなと思います。

# あとがき(結)
このソースコードの整理は、次のステップとしてのソースコードのクラス化を考えています。今のコードはベタ書きで、オブジェクトやクラス化を考えなくコーディングしてきたものです。かなり長い年月をかけて修正、機能追加を繰り返してきたものです。そのため、ごみのようなコードになっています。いつか整理したいと思っていながら、今日まで来てしまいました。ようやく、気持ちの整理がついてきたところで、そろそろクラス化してすっきりとしたソースコードにしたいと思っています。
(追記)
まだ、書き足りない点があり、随時、補足して行きたいと思います。
Power BI tips

ここではPower BIでレポートを作るにあたって、これやりたかったな情報を掲載していきます。
少しずつ更新していこうと思っております

# Power BIについて学ぶページ
https://lab.pasona.co.jp/data-operation/skill/747/

Power BI を試してみよう(pdf)
file:///C:/Users/JPZ5050/Downloads/PowerBI_SelfLearning01.pdf

Power BI を試してみよう(html版)
http://www.sqlquality.com/Self2016/PowerBIver2/Text/mokuji.html

# サブカテゴリとか列の色変えるときは以下画像のとこの系列を変更してカラーを変えていく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555220/b67255c3-3f03-9695-cbd5-d1831fc5a3f9.png)
PodmanでFTPサーバーを立てる



Podmanの環境を整えた後、以下コマンドを実行してください

# FTPの場合
```
podman run -d -v "ローカルフォルダのパス":/home/vsftpd -p 20-21:20-21 -p 21100-21110:21100-21110 -e FTP_USER=myuser -e FTP_PASS=mypass -e PASV_ADDRESS=localhost -e PASV_ADDR_RESOLVE=YES --name myftp fauria/vsftpd
```
ローカルフォルダのパスは、例えば「C:\work\ftp」とかです。

「PASV_ADDR_RESOLVE=YES」が設定されていないとWinSCPなどで接続できないのでご注意ください。


    # FTPSの場合
```
podman run -d -v C:\ftp:/home/vsftpd \
           -v /path/to/certs/cert.pem:/etc/ssl/certs/vsftpd.pem \
           -v /path/to/certs/key.pem:/etc/ssl/private/vsftpd.key \
           -p 20-21:20-21 -p 21100-21110:21100-21110 \
           -e FTP_USER=myuser -e FTP_PASS=mypass \
           -e PASV_ADDRESS=localhost \
           -e SSL_ENABLE=YES \
           -e REQUIRE_SSL_REHANDSHAKE=NO \
           --name myftp fauria/vsftpd
```
/path/to/certsは実際の証明書のパスに置き換えてください
SSL_ENABLE=YESはSSLを有効にするために必要です
REQUIRE_SSL_REHANDSHAKE=NOは再接続時のSSLハンドシェイク要求を無効にします

# コマンドで証明書を生成:
```
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout key.pem -out cert.pem
```
指定したディレクトリ内に存在しているcsvファイルを一括で削除

<h1>はじめに</h1>
ディレクトリ内に保存されているcsvファイルを一括で削除する必要があったので、備忘録として。

```python:title.py
import os
import glob

# 指定したディレクトリ
dir_path = '/path/to/your/directory'

# ディレクトリ内のすべてのCSVファイルを取得
csv_files = glob.glob(os.path.join(dir_path, '*.csv'))

# 各CSVファイルを削除
for file in csv_files:
    os.remove(file)
```

<h1>補足</h1>
<p>このコードは、指定したディレクトリ内のすべてのCSVファイルを検索し、それらを削除します。</p>
<p>glob.globはワイルドカードを使用してファイルパスを検索する関数で、os.path.join(dir_path, '*.csv')により指定したディレクトリ内のすべてのCSVファイルを検索します。</p><p>その後、os.removeを使用して各ファイルを削除します。</p>
モデル駆動型アプリのビューやフォームを左メニューで切り替えたい！

こんにちは！
今日はモデル駆動型アプリで左のメニューで直接ビューやフォームを切り替える方法をご紹介します。

# どういうこと？完成イメージ
標準でモデル駆動型アプリは同じテーブルであれば、ビューの画面内の左上のビュー名をクリックすることでビューを切り替えることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/f5b26b7b-7361-5ea5-8320-30bddc1820fd.png)

これを左のメニュー（ナビゲーション・サイトマップ）で切り替えたいという要件をたまにお見かけします。その方法について次のセクションからご紹介します！
同様の方法で、フォームやレポートなども指定することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/dd0040a0-8a50-b793-a4d0-c7c08a8c819a.png)

# 作成方法
具体的には、モデル駆動型アプリのURLページを使用します。

## 指定したいページのURLを取得
モデル駆動型アプリの編集画面を開きます。
右上の再生ボタンをクリックし、アプリの再生画面を表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/411fa8bf-b5b3-aaed-4a91-88473a498764.png)

直接表示したいビューを選択し表示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/b7252599-f069-908c-6627-b81428c26c8f.png)

URLの「/main.aspx?appid=」以降をコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/ef90982d-5774-53c6-def4-d91ce188adee.png)

同様に、（新規）フォームの場合は、[新規]ボタンをクリックして、表示されるフォーム画面のURLの「/main.aspx?appid=」以降をコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/a2e38b97-917f-4b63-98f5-8cc55ed330ae.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/1006d36a-36a2-e80b-babb-dc9ac38d9000.png)

## ナビゲーション作成
ブラウザのタブを切り替えて、元のモデル駆動型アプリの編集画面を表示します。
画面左の「ページ」で[新規]ボタンをクリックします。
ポップアップで[URL]をクリックし、[次へ]ボタンを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/e165559b-15ea-69f4-54c0-e641ac1df478.png)

先ほどコピーしたビューやフォームの「/main.aspx?appid=」以降のURLを貼り付けます。
ページの名前を付け、[追加]ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/6288aef7-4253-5e35-7d4c-6dddc4d95c69.png)

::: note
URLの先頭（https://）から指定すると新しいタブで開くことができます。
「/main.aspx?appid=」から指定するとアプリ内で開くことができます。
:::

[公開]をクリックします。完成です！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/c612c91e-921e-5489-323c-79077170f3e0.png)

## アプリの動作
アプリの再生画面を開き、ブラウザのリフレッシュボタンを２回ほどクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/16207570-4e95-092b-0b26-40a40d059980.png)

先ほど作ったページが追加されていることを確認して、これをクリックする
と、指定したビューを直接開くことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2883014/8e37a93c-0a56-6b9c-5552-c869b1165416.png)


# 参考

https://learn.microsoft.com/ja-jp/power-apps/developer/model-driven-apps/open-forms-views-dialogs-reports-url
Xcode14.3でArchiveしたらrsync errorが発生した

## 発生したエラー
Xcode14.3でArchiveを行ったところ下記のエラーが発生。
```
rsync error: some files could not be transferred (code 23) at /AppleInternal/Library/BuildRoots/d9889869-120b-11ee-b796-7a03568b17ac/Library/Caches/com.apple.xbs/Sources/rsync/rsync/main.c(996) [sender=2.6.9]
```

## 結論
cocoapodsを最新にしてpod installを実行しよう

## 対応
とりあえずエラーメッセージで検索してみると「/ios/Pods/Target Support Files/Pods/Pods-AppName-frameworks.sh」を下記のように修正すると直るという記事がいくつか出てきた。
```shell:/ios/Pods/Target Support Files/Pods/Pods-AppName-frameworks.sh
source="$(readlink "${source}")"
```
```shell:/ios/Pods/Target Support Files/Pods/Pods-AppName-frameworks.sh
source="$(readlink -f "${source}")"
```
だがこのファイルはpod installした時に自動生成されるファイルなので手動で修正はしたくない。
おそらくcocoapodsのバージョンが問題だろうとあたりをつけてcocoapodsのrelease noteを見てみる。
https://github.com/CocoaPods/CocoaPods/releases
すると1.12.1にて「lib/cocoapods/generator/embed_frameworks_script.rb」に対して以下のような変更が入っていることを確認した。
```diff:lib/cocoapods/generator/embed_frameworks_script.rb
-    source="$(readlink "${source}")"
+    source="$(readlink -f "${source}")"
```
おそらく「lib/cocoapods/generator/embed_frameworks_script.rb」は「/ios/Pods/Target Support Files/Pods/Pods-AppName-frameworks.sh」を生成するためのファイルなのだろう。
cocoapodsのバージョンを最新にしpod installを行うと、「/ios/Pods/Target Support Files/Pods/Pods-AppName-frameworks.sh」も同じように変更されることが確認できた。
その後再度Archiveを行うと無事成功。

## 追記
Xcode14.2→Xcode15に更新したので気づかなかったがどうやらXcode14.3から発生するみたいなので記事タイトル・本文をXcode14.3に変更。
Blenderでレンズの歪み補正データ(STmap)を作成してみる


物理的なカメラを利用したことがある人は分かるかと思いますが
一般的に現実で真っすぐなものを撮っても　レンズの歪みで画像で直線にはなりません。
補正する方法は色々あるようですが　Blenderの利用して補正データ作成する例を考察したいと思います

# 歪み画像による画像マッピング
３Dでのテクスチャを張り付けるのUVマップを使うかと思います
これは「３D面上のここに　画像のここを対応づける」という情報ですが
画面の大きさを1.0とした割合の値の座標（UV座標）で表されます。

![UVmap](https://camo.qiitausercontent.com/0677107f3887993b2ea96c0320072d945d46a566/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3135313439332f62613039633531342d656666352d386233322d336165342d3434313930653633653834622e706e67)
また、色の画像データもBlender内では各色0-1.0の値として扱われています
ここでUV座標指定に色画像を挟むと・・・
![23122601.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/a7c6460b-cb40-8728-701e-cacabff28433.gif)
色情報をUVの座標値と解釈した座標の色を取得した画像が作成され　歪んだ画像を作ることができます
これ自体を「UVマッピング」と言ったり「STmap」と言ったりするようです
（画像左下が挟み込んだ画像で　圧縮で乱れていますがもっと滑らかな画像です）

ここではBlenderでの用語ではないですが歪ませるための画像を **STmap** と呼称します

このSTmapをうまく作れれば　レンズの歪みの補正ができそうです
今回は広角のアクションカムでの撮影画像で試してみます
## 補正データのマッピング
こちらが補正をするために市松模様を撮影した画像です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/e2439fbd-cca8-4249-aa01-962d03246048.png)
周辺部分が特に大きく歪んでいますね

撮影時に市松模様の実際の大きさとカメラの距離を計測しておいて
３Dソフト上でこれと全く同じものをモデリングして
正確に対応付けされたSTmapを作れれば　理論上は歪みが補正できることになります


まずはグリッド間隔と同じメッシュを作成して
UVマッピングで撮影画像の市松模様とメッシュのUVを合わせる作業をします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/275f6111-838d-0c9e-13f3-736372f3389d.png)
３Dビューのテクスチャマッピングで市松模様が真っすぐになれば終了です

## STmapの出力
撮影画像と同じカメラ位置からレンダリングすることで補正用のSTmapを作成します。

まずはレンダリング解像度とカメラの画角を設定です
このカメラビューに映った範囲が最終出力に映る範囲ということで　これが補正後の画像の出力解像度や範囲に関係します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/6e6a06bc-6d5b-ba36-02dc-5f321296dba0.png)
画像がボケたり 市松模様が欠けている部分は利用できないので　撮影画像よりレンダリング範囲は狭くなります
今回は横方向の画角70度　4000*2000の解像度に設定しました

メッシュのマテリアルでこのようにUVマップの位置ベクトルを色情報として入力する設定にすると
メッシュ上の点がどのUV座標を利用しているかを表す画像になります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/6d1c28f2-ab32-782f-215a-5ea33e17a176.png)
標準では１つピクセルに対して僅かにずらした位置をサンプリングして滑らかにする処理が
入っているので値を０にすることでオフにしています

これをレンダリングしてOpenEXRで保存します。

## STmapの利用
コンポジターのノードには色の対応付けで歪める利用できる**UVマッピング**ノードがあります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/151493/75fe4252-5fa7-2bf6-eaa7-f399e772e841.png)
UVマッピングの　「画像」の入力に変形させたい画像を「UV」の入力にSTmap画像を接続します
出力画像の解像度はSTmapの大きさになります。

ここで画像ビューアーのエディタを表示しておいて表示画像をViewer Nodeにすると
変換結果が表示されて　結果画像の保存もできます
また、Node Wranglerアドオンを利用している場合にはサイドバーのノードタブに
「Save This Image」のボタンが出るので　そこからアクティブなビューアーノードの画像を保存する操作もできます


また、Blenderでは冒頭のようにシェーダー上でUV値を歪めることができるので
私は　以前に記事にした **[カメラプロジェクションアドオン](https://qiita.com/yukimituki11/items/8d709bbaf2966bf7c38c)** を併用して
カメラから見た方向が正確な位置へ投影してデリングのガイドとすることができます
こちらについては　いずれ別の機会に解説したいと思います

個人的な手順の整理のために書いたニッチな内容ですが
何かのお役に立てば幸いです。
rangerからExcelやPDFを開く

rangerのおかげでせっかくターミナル上のディレクトリ移動が楽になったのに、ExcelやPDFを開けないのもどかしい。
とりあえずよく使うこの2ファイルだけでも、直接開けるようになれば！！

ということで、wsl2にてrangerからPDFやExcelを直接開くやり方を調べて、実現が出来たので記載します。

:::note info
以前調べた、[wsl2からPDFとExcelを開く](https://qiita.com/cotoro/items/e1af25a169b672bc3c37)の進化版です。
この時作成したスクリプトをrangerから直接呼び出せるように進化させました。
:::

## 【必要なもの】

- rifle.conf：rangerの設定ファイルです。
- shellスクリプト：PDFやExcelを開くコマンドを記載します。

## 【手順】

1. まず、PDFやExcelを開くshellスクリプトを作成します。
    - 今回は、`~/src/shell`というディレクトリを作成して、そこで管理します。
    
    サンプル）
    
    ```bash
    #!/bin/bash
    
    # open-pdf (only windows)
    function open_pdf {
    	current_dir=$(pwd)
    	if [[ "$1" != "" ]]; then
    		file_name="$1"
    		if [[ "$file_name" == *"/"* ]]; then
    			IFS='/' read -ra segments <<<"$file_name"
    			file_name=${segments[-1]}
    		fi
    
    		file_path_win=$(wslpath -w "$current_dir/$file_name")
    		cmd.exe /c start msedge "" "$file_path_win"
    	fi
    }
    
    open_pdf "$@"
    ```
    
    - 引数で受け取った文字列からファイル名のみを取得して、windowsのファイルフルパスに変換して、edgeで開く、windowsのコマンドを実行します。
    - 作成したコマンドを実行できるように権限を付与します。
    `chmod +x ~/src/shell/open_pdf.sh`
    - Excelを開きたい場合は、
    `cmd.exe /c start excel　"" "$file_path_win"`
    にしてあげるとExcelアプリでファイルを開くようになります。


1. `rifle.conf`に、ファイル拡張子が`~.pdf`の時に作成したコマンドを実行するように設定を追加します。
    
    ```bash
    ext x?pdf?, label editor = ~/src/shell/open_pdf.sh "$(basename -- "$@")"
    ext x?xls?, label editor = ~/src/shell/open_xls.sh "$(basename -- "$@")"
    ext x?xlsx?, label editor = ~/src/shell/open_xls.sh "$(basename -- "$@")"
    ```
    
    - 一応`"$(basename -- "$@")"`でファイル名のみを渡しているはずなのですが、ちょっと不安なので、スクリプトの方にパスが来てもファイル名のみを抽出するように処理を記載してます。
    - この設定は、ファイル拡張子が`pdf`の場合に、`~/src/shell/open_pdf.sh`のコマンドを呼び出し、選択したファイル名（`"$(basename -- "$@")"`）を引数で渡すという設定です。

これで、rangerにてPDFやExcelファイルをtextファイル等を開く時と同様の操作で、Windows上のアプリで開くことが出来る様になります。
個人利用AWSアカウントの作成

# はじめに

学習目的でAWSの個人アカウントを開設しましたので、その際の困りごとなどをまとめてます。

# AWSアカウント作成にかかる費用について

AWSアカウントの作成自体は無料です！

しかしながら、AWSのサービスは従量課金となっているため、アカウントを作成してからAWSサービスを利用した分から料金が発生する可能性があります。
そのため、アカウントを作成する過程では利用した料金の支払いのためにクレジットカードの登録が求められます。
有料となると心配される方もいると思いますが、AWSサービスの中には無料利用枠が設定されているものもあり、うまく活用すれば無料利用枠の中でAWSサービスを利用することも可能です。

# 事前準備

**メールアドレスの準備**

AWSアカウントを作成するためにはメールアドレスが必要になります。

私の場合はここで小さな問題が発生！

私は以前AWSアカウントを開設していたことがありますが、無料利用期間が過ぎてしばらく使わなくなったこともありアカウントを閉鎖しました。

https://repost.aws/ja/knowledge-center/reopen-aws-account

↑のアナウンスを確認すると、閉鎖したアカウントは90日以内であれば再開ができますが、「90 日が経過すると閉鎖したアカウントを再開したり、閉鎖したAWS アカウントに関連付けられたEメールアドレスで新しいAWSアカウントを開設したりすることはできません。」とありました。

そのため、以前に使用していたメールアドレスは使えず、新しいメールアドレスを用意する必要がありました。

**クレジットカードの用意**

前述のとおりAWSアカウントの作成自体は無料ですが、無料利用枠を超えた分は料金が発生するため、アカウント作成の段階でクレジットカードを登録する必要があります。そのためクレジットカードが必要になります。

# AWSアカウントの作成手順

AWSアカウントの作成の流れについては以下の公式のガイドに画像付きでわかりやすく説明されています。

https://aws.amazon.com/jp/register-flow/

詳細はリンク先を参照していただき、ここでは大まかな流れを説明します。

### ステップ０： AWSのサインアップページにアクセス

[AWSのサインアップページ](https://portal.aws.amazon.com/billing/signup#/start) にアクセスします。

### ステップ１：アカウント作成

メールアドレス、アカウント名を入力して送信ボタンを押すと、入力したメールアドレスに確認コードが送られてきますので、確認コードを検証します。次にアカウントのパスワードを設定して次のステップに移ります。

ここで作成したアカウントがrootユーザーとなります。
rootユーザーとはAWSアカウントを作成した時に用意したEメールアドレスとパスワードを使用してサインインできるアカウントであり、 請求情報などの支払い関連、AWSの全てのサービスとリソースに対する無制限のアクセス、解約などの契約変更が実行可能なユーザーです 。

### ステップ２： 連絡先情報の入力

電話番号や住所などの連絡先情報を入力して次のステップに移ります。

ここではAWSの利用用途として「ビジネス」か「個人」かを問われますので、個人利用の場合は「個人」を選択します。ここでの選択によって機能を制限されることはありません。

### ステップ３：請求情報の入力

クレジットカードまたはデビットカード情報などを入力して次のステップに移ります。

前述のとおり、無料利用枠を超過した分が登録されたクレジットカードに請求されます。

### ステップ４：本人確認

SMSまたは音声電話の情報を入力し、選択した方法によって本人確認を行って次のステップに移ります。

### ステップ５：AWSサポートプランの選択

「ベーシックサポート」「デベロッパーサポート」「ビジネスサポート」のいずれかから選択となります。
個人利用の場合であれば「ベーシックサポート」で十分かと思います。

ここまで作業するとアカウントがアクティブ化されていることを示す確認ページが表示されます。
同時にアカウントが有効になったことを確認するメールが届いるはずです（届いていない場合は迷惑メールフォルダに振り分けれている可能性もあるため注意してください）。
また、アクティベーションには数分から最長で24時間かかることもあるようです。

# アカウント作成のその後

アカウントが作成できると、AWSマネジメントコンソールにサインインして、サービスやリソースを管理することができるようになっているはずです。

[こちら](https://console.aws.amazon.com/console/home) からサインインを行います。
アカウント作成直後であればルートユーザーでサインインができるようになっています。

ただし、アカウントを作成した直後の状態ではセキュリティリスクがあるため、セキュリティ対応等を行う必要があります。

アカウント作成後に実施した対応などは今後アップする記事で説明する予定です。
yupを使いこなしたい

## はじめに
yupについての備忘録です。
カタカナのバリデーションを実装しました。
子コンポーネントについては載せておりません。

## yupについて
```js
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';

// バリデーションスキーマ
export const userFormSchema: yup.SchemaOf<UserForm> = yup
  .object().
  shape({
    name: yup
      .string()
      .required('名前は必須です')
      .matches(getKatakanaRegex, 'カタカナで入力してください'),
    email: yup.string().required('メールアドレスは必須です'),
});
```
まずはyup関数を定義します。
`matches(getKatakanaRegex, 'カタカナで入力してください')`では正規表現を用いてチェックしています。

以下は関数の型定義になります。
```js
export type UserForm = {
  nameKana: string;
  email: string;
};
```
以下のように使用することで子コンポーネントで入力した文字に対するバリデーションが発火します。
`mode`のところを変えることで文字が変化したときにバリデーションを通すか、カーソルを移動させたときかを変えることができます。
これらは[useForm](https://react-hook-form.com/docs/useform)で操作しています。
```js
export const SignUpUser: FC = () => {
  const methods = useForm<FormValues>({
    defaultValues: initialValues,　　//yup側と一致しているとバリデーションが機能する
    resolver: yupResolver(userFormValidateSchema),
    mode: 'all', //onChange, onBlur, onSubmit, onTouched が指定可能。all → すべてのタイミングでバリデーション実行
  });

  return (
    <FormProvider {...methods}>
      <Outlet />
    </FormProvider>
  );
};
```
以下はパロメータの初期値です。

```js
const initialValues: FormValues = {
  nameKana: '',
  email: '',
};
```
## おわりに
間違っているところがあれば教えていただけると嬉しいです。

### 参考
https://react-hook-form.com/docs/useform
https://zenn.dev/highgrenade/articles/c378deb2ec3eea
Huggingfaceのdiffusersでpretrainedのモデルの保存先を変更する


Huggingfaceの[diffusers](https://huggingface.co/docs/diffusers/index)では`pipeline.from_pretrained`を実行することでHuggingface Hubからpretrainedのモデルをダウンロードできます

ダウンロードしたモデルは`~/.cache/huggingface`にキャッシュとして保存されます

Docker環境ではキャッシュ先のディレクトリをマウントしておかないとコンテナを破棄したタイミングでキャッシュが失われてしまいます

今回はキャッシュ先のディレクトリを`~/.cache/huggingface`から`<work_dir>/.cache/huggingface`に変更してみます

試してみたところ，キャッシュ先のディレクトリは環境変数`$HF_HUB_CACHE`で指定されています．`$HF_HUB_CACHE`は`$HF_HOME/hub`に設定されているので，環境変数`$HF_HOME`を変えれば良さそうです

```bash
    export HF_HOME=<work_dir>/.cache/huggingface
```

https://huggingface.co/docs/huggingface_hub/package_reference/environment_variables#hfhubcache

Docker環境の場合，ENVコマンドで環境変数を設定できます

```Dockerfile
    FROM nvidia/cuda:11.7.1-cudnn8-runtime-ubuntu20.04

    # ワークディレクトリの指定
    WORKDIR /app

    # 非インタラクティブモードにする (入力待ちでブロックしなくなる)
    ENV DEBIAN_FRONTEND noninteractive
    # .pycを作らないように
    ENV PYTHONDONTWRITEBYTECODE 1
    # バッファの無効化
    ENV PYTHONUNBUFFERED 1
    # torchvisionでpretrainedのモデルを保存する場所
    ENV TORCH_HOME /app/.cache/torchvision

    # ----------------------------------------------------------------
    # setup (root)
    # ----------------------------------------------------------------
    RUN apt update && \
        apt install -y bash \
        build-essential \
        git \
        git-lfs \
        curl \
        ca-certificates \
        libsndfile1-dev \
        libgl1 \
        python3.8 \
        python3-pip \
        python3.8-venv && \
        rm -rf /var/lib/apt/lists

    # ----------------------------------------------------------------
    # create user
    # ----------------------------------------------------------------
    # UIDとGIDは外から与える
    ARG USER_UID
    ARG USER_GID

    # コンテナ内でのユーザー名， グループ名
    ARG USER_NAME=user
    ARG GROUP_NAME=user

    # グループが存在しなかったら，　適当なグループを作成
    RUN if ! getent group $USER_GID >/dev/null; then \
        groupadd -g $USER_GID $GROUP_NAME; \
        fi

    # ユーザーを作成
    RUN useradd -m -u $USER_UID -g $USER_GID -s /bin/bash $USER_NAME

    # 初期ユーザーの変更
    USER $USER_NAME

    # ----------------------------------------------------------------
    # setup (user)
    # ----------------------------------------------------------------

    # pre-install the heavy dependencies (these can later be overridden by the deps from setup.py)
    RUN python3 -m pip install --no-cache-dir --upgrade pip && \
        python3 -m pip install --no-cache-dir \
        torch \
        torchvision \
        torchaudio \
        invisible_watermark && \
        python3 -m pip install --no-cache-dir \
        accelerate \
        datasets \
        hf-doc-builder \
        huggingface-hub \
        Jinja2 \
        librosa \
        numpy \
        scipy \
        tensorboard \
        transformers \
        omegaconf \
        pytorch-lightning \
        xformers

    ENV HF_HOME /app/.cache/huggingface

    CMD ["/bin/bash"]
```

終わり
プロンプト ＝ 日常のコミュニケーション - 良い質問とは

## プロンプト
シンプルな質問でも答えは得られるが、「質の良い答え」を得るために
人も「いい質問」を工夫するといいでしょう。

良い質問とはモデルに答えに必要な情報を渡して、より良い結果を得るよう
個人的には必須とオプションがあると思います。

### 良い質問と答えに必要な情報
| 項目 | 説明 | 必要 or 不要 |備考（理由）|
|:-----------:|:------------:|:------------:|:------------:|
| 指示       | 質問の意図      | 必須         |会話が山に行くので|
| 情報     | あった方がいい      | あった方がいい       |効率がいい|
| 文脈       | あった方がいい        | あった方がいい         |効率がいい|
| 詳細         |    ＋＠のdetail     |   不要かと         |結局、話し合いながら詰めていく|
|出力指示子|出力のタイプや形式のこと|必要な場合|csv,yaml,JSONなど

例えば次の何の「さくら」というシンプルすぎる質問を投げると
何かしら結果は得られるが、意図したものではない可能性が高い

例）
意図
>花見でみた桜の種類について調べたい 

プロンプト
> さくら

出力
> 桜が満開の季節、公園には人々が花見をしながら・・・

⇒桜を素材に短い文を書いてもらえました笑

ここで足りないのは指示と情報かと思いいます。
例えば

プロンプト
> 私が花見でみた桜は何でしょうか。白い花と葉っぱがありました。

出力
> 花見で花と葉っぱが同時に見えた場合、おそらく見た桜の品種は「ヤマザクラ（山桜）」である可能性が高いです。・・・ 理由とヤマザクラへの説明が続く

どうでしょう？ どのような桜なのか聞く（指示）また、葉っぱがあるという（必要な情報）を渡すことで意図通りだと思う可能性が高い回答が得られました。このように、タスクを実行するために最適なプロンプトを設計するアプローチを**プロンプトエンジニアリング**と呼びます。

## よく言われる zero-shot？ few-shot？とは

### zero-shot prompting
最初から質問をするパタン。これは文字通り一発芸みたいなものです。
質問するから答えて、LLMモデルは色は引き出し中一つを出します。
（直前の質問履歴と関連する可能性が高い）
>Q: <質問>?
A: 

### few-shot prompting
いくつかの質問と答えをベースに答えるパタン
>Q: <質問>?
A: <答え>
Q: <質問>?
A: <答え>
Q: <質問>?
A: <答え>

プロンプト:
>リンゴ // 赤
バナナ // 黄色
ブルーベリー // 青
この場合、壁の色は
この壁はブルーベリーのようだ // 


出力:
>文脈から青色とみえますね。

few-shot promptingにより、文脈に沿った学習が可能になります。
言語モデルがタスクを学習するために、わずかなサンプルを提供することができます。

## まとめ

いかがでしょうか。難しく思うかもしれませんが、
日常で行われる人とのコミュニケーションと同じだと思いませんか。
私はよく **「zero-shot prompting」** で妻に質問をしてしまうので **「は？」** って顔で見られたりします。それに比べたら、LLMはなんて優しいんだ💛

要は
### 3つのルール
- 質問の意図を明確する
- 必要な情報を渡す
- （必要であれば例え話も混ぜる）

これでよき答えを得るようにしましょう。

参考
<プロンプトの基礎>
https://www.promptingguide.ai/jp/introduction/basics
コピペができないときにコピペする方法【エラーメッセージなどに】


## 前書き

Windows11以外の環境で使えるかは分かりません。

## 1. 「Win + Shift + S」でスクショ

<img width=600 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649673/8bd5dbc5-d88c-027e-31a3-5e83bc1f9db3.png">

## 2. 右下に通知が来るのでクリックしてSnipping Toolを開く

<img width=600 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649673/d9086643-28b8-13fe-7083-cd9ab6ec00d6.png">

## 3. 「テキストアクション」をクリック

<img width=600 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649673/a4a10de3-f889-7a59-6946-799eca4fb3f5.png">

## 4. 「すべてのテキストをコピーする」をクリック

<img width=600 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649673/da593ff3-3d51-322b-092f-acb759ef75dd.png">

## 5. 後は好きなようにペーストしよう！！！

<img width=600 src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/649673/132fd830-3dae-16c1-bbc8-2a9b29ea6497.png">
ディレクトリ名を当日の西暦日時とした、新規ディレクトリの作成方法

<h1>はじめに</h1>
<p>Pythonでcsvファイルの出力をする際、以下のような調整をしたいと頻繁に考えます。</p>
そのため、コードの備忘録として残します。
<ul>
    <li>新規ディレクトリの作成</li>
    <li>ディレクトリ名を当日の西暦日時</li>
</ul>
※なおGoogle Colaboratory環境です。

<h1>スクリプト紹介</h1>
<p>Pythonで指定したディレクトリ内に新しいディレクトリを作成し、そのディレクトリ名を今日の西暦年月日、時間（秒まで）にし、CSVデータのファイル名を「テレビ型番」とするコードは以下の通りです。</p>
<h2>必要なライブラリのインストール</h2>

```python:qiita.py
import os
import pandas as pd
from datetime import datetime
```
<h2>データフレームの準備</h2>

```python:qiita.py
# データフレームの例
df = pd.DataFrame({
    'テレビ型番': ['型番1', '型番2', '型番3'],
    '価格': [50000, 70000, 80000],
    '製造元': ['メーカーA', 'メーカーB', 'メーカーC']
})
```

<h2>ディレクトリの作成</h2>

```python:qiita.py
# 指定したディレクトリ
dir_path = '/path/to/your/directory'

# 今日の日付と時間（秒まで）
now = datetime.now().strftime('%Y%m%d_%H%M%S')

# 新しいディレクトリのパス
new_dir_path = os.path.join(dir_path, now)

# 新しいディレクトリを作成
os.makedirs(new_dir_path, exist_ok=True)
```

<h2>ファイルの作成</h2>

```python:qiita.py
# CSVデータのファイル名を「テレビ型番」とする
for model in df['テレビ型番'].unique():
    file_path = os.path.join(new_dir_path, f'{model}.csv')
    df[df['テレビ型番'] == model].to_csv(file_path, index=False)
```
Next.jsでToDoリストを作った

# はじめに
Next.jsの学習を兼ねてToDoリストを作りました。

# 完成品
今回はデータベースやローカルストレージ等も使用していないので、入力したToDoはリロードすると消えてしまいます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59285/b435e1cd-5a33-b18a-6762-f4582992fe5a.png)

[https://next-study-todo.vercel.app/](https://next-study-todo.vercel.app/)

# 仕様
- 入力欄にToDoを入力して追加ボタンをクリックでToDoが追加
- 完了したToDoはチェック済にステータスを変更可能

# 使用技術
- Next.js@14.0.2
- Typescript@5
- Vercel

# おわりに
モチベーションが続くようであれば、入力したToDoを保持する形にアップデートしてみようと思います。