OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonで画像を読み込み⇒別名再保存するツールを作った話

#概要
友人「大量の画像ファイルが入ったフォルダから、適当な画像ファイルをピックアップして、フォルダと同じ階層にコピーしたうえで、画像ファイルの名前をフォルダ名に変更するツール作って」
私「おかのした」

#仕様

###1．こんなフォルダがあったとします
「KizunaAI」「MiraiAkari」等の各フォルダには大量の画像が入っており、001～のようなファイル名が付いています

```C++:対象フォルダ
images
 ├ KizunaAI
 │  ├ 001.jpg
 │  └ ....ipg
 │ 
 ├ MiraiAkari
 │  ├ 001.jpg
 │  └ ....ipg
 └ ....
```

###2．それをこうします
各画像フォルダから適当な画像ファイルをコピーし、フォルダと同じ階層に置いたうえで、ファイル名を所属していたフォルダの名前にリネームします。

```C++:対象フォルダ
images
 ├ KizunaAI
 │  ├ 001.jpg
 │  └ ....ipg
 ├ KizunaAI.jpg
 ├ MiraiAkari
 │  ├ 001.jpg
 │  └ ....ipg
 ├ MiraiAkari.jpg
 └ ....
```

以上！！！！　

#実装
ひとまず手持ちのAnaconda環境で実装しました。
私「ずいぶん改まって頼んで来たからどんな深刻な内容かと思いきや」
<img width="301" alt="キャプチャ.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150780/2ba697b6-5214-14a3-e3b7-687db5f674f7.png">

```Python:main.py
from PIL import Image
import os, glob

rootpath = "./images"
files = os.listdir(rootpath)
#rootpath配下の全ディレクトリを取得
dirs = [f for f in files if os.path.isdir(os.path.join(rootpath, f))]
#せっかくなのでコンソールに表示
print(dirs)    # ['dir1', 'dir2']
#全ディレクトリに対して実行
for dir in dirs :
    #全ファイルをスキャン
    files = glob.glob(rootpath +"/"+ dir + "/*.*")
    #拡張子を引っこ抜く
    targetFile = files[0]
    root, ext = os.path.splitext(targetFile)
    #画像オープン
    img = Image.open(targetFile)
    #リネームしてルートパス直下に保存
    img.save(rootpath +  '/' + dir + ext)
```

#EXEにする
Anacondaは少々面倒くさいので、EXE化してワンクリックで動くようします
####1．Pyinstallerのインストール
Anaconda の仮想環境ごとEXEにしてくれる凄いツールです。つまりこいつがあればPython環境がない人でも動かせるらしいです。
1KBもないコードに対して300MB越えのEXEを吐き出す困ったちゃんですが、便利なので許します。

```C++:AnacondaPronpt
conda install -c conda-forge pyinstaller
```
余談ですが私はcondaでインストールするほうが好きです。

####2．EXEにする
あくしろよ

```C++:AnacondaPronpt
pyinstaller main.py --onefile
```

私「できた(バグ無しとは言ってない)」
友人「やりますねぇ！」
#その他
友人「フォルダ名に"[]"あると対応してくれないんですがそれは」
私「ファ！？」
友人「まあちょっとしかないからここは手動でやるわ」
私「ぐう聖」

暇があったらこの不具合対応版も記事にします
go/analysis.Analyzerをnolintできるようにする

go/analysis.Analyzer を使いこなして行こうと思った時に、
ふと思い立ったので作ってみました。

https://github.com/kyoh86/nolint

```go
func main() {
	multichecker.Main(
		nolint.WrapAll(
			exportloopref.Analyzer,
			bodyclose.Analyzer,
			// ...
		),
	)
}
```

と書くと、 `// nolint` のコメントでDiagnosticsの出力を抑制できます。
Pythonで1から10までの数字の配列のいろいろな作成方法。

Pythonで1から10までの数字の配列を作成する方法を書いていきます。
よろしくおねがします。

まず、基本的な配列の方法。

```
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
```

つぎに、for文を使った方法。

```
a = []
for i in range(1, 11):
  a.append(i)
```

```
a = [0]*10
for i in range(10):
  a[i] = i+1
```

whileを使った方法。

```
a = []
i = 1
while len(a) < 10:
  a.append(i)
  i += 1
```

```
a = [0]*10
i = 0
while not all(a):
  a[i] = i+1
  i += 1
```

最後に、内包表記の方法。

```
a = [i for i in range(1, 11)]
```

以上です。
ありがとうございました。
一つ目のサービスを作ってから、感じたこと/サービスを作りなおした話

# 1カ月前に「みんなでコードレビュー」というサービスを作りました～～

■参考記事
https://qiita.com/shibaHaya/items/734d5e2cd22f7e7d9576

このサービスは、自分のコードをレビューしてもらったり、軽い感じでレビューするサービスを想定して作ってみたものでした:relaxed:

## 経過　:man:
Qiitaに記事を書いて、「LGTM」をたくさんもらえて、それなりの流入がありました。
しかし、日がたつにつれサイトの訪問者はどんどん減っていきました:runner_tone5:

### 原因　:frowning2:

・サービス開始時に、それほど質問の量がないので、見に来る人も少なくなる（なので期間がかかる）
・そもそもこのサービスに人がいないので、コードを載せても答えが返ってこない、使わない
・コードレビューなら、普通Githubなどを使うし、軽い感じでコードレビューしてもらいたいって人があまりいないので、需要がない
・見た目が使いにくい
・そもそも知られてない

まあ、主な点はこんな感じだろう？？と思う。。:ok_woman_tone3:

### 少し対策してみる:dancer_tone4:

なにか苦し紛れな感じで、追加機能をつけてみた。

・通知機能
・アイコン入力機能
・Best Answer
・そもそも機能を改修し、自分のコードを自分でレビューするようなサービスにしてみたりした。

##その時の気持ち
少し使いやすさは改善したように思うが、なんかやっていて意味があるのかな～？:skull:
そもそも、「自分このサービス使わないしな～」とか思ってた。

だいぶモチベーションが下がっていたとき、「Google Adsense」の合格メールが来た:angel:
「まあ、広告はっといたら、1円ぐらいは、かせげるのかな～」と思い、機能開発はストップしていたが、広告だけ貼ってみた。


すると、次の日に「100円」の収益がついていた。
「え、100円も稼げるの？」ってだいぶモチベーションが上がった:heart_eyes_cat:

### 自分が感じたこと
自分は最初、作るのは楽しいし、とりあえず作って、人が集まったら、広告でも貼って、ちょっとは収益になるかな～と思っていた:fist_tone5:

実際作ったまでは、楽しかったが、それ以降の「機能追加・改修」はあんまりモチベーションが続かなかった。

そもそも僕は、月6万ぐらいかせげたら、田舎のおじいちゃん・おばあちゃんの家で暮らして、そこで開発したいな～と思っている。
僕にとっては収益が重要ぽいので、100円稼げたときは、自分が想像するよりもモチベーションが上がった。:hugging:

###結果、自分の性格的に、サービス開発を続ける / このサービス継続して長く公開しとこうと思えるモチベーション優先順位は、

1. 収益
2. サービスの新規開発の楽しさ
3. 収益がない場合でも、自分が使えるもので、あまり大きな改修や追加機能の手間がないもの
（これはサービスの目的をしっかり決めてたら達成できそう）

こんな感じだと思う。　多分:v:

収益があれば、機能改修のモチベーションも湧くが、今の「みんなでコードレビュー」は、自分も使わない(3番も未達成)し、人が全然いないので、1番も達成できなそう。2番はもう使い終わったし。。

1番は、ハードルが高いので、とりあえず3番を達成できそうなサービスに作り替える:thumbsup:

# 自分が開発するときのメモを、webで保存できて後から見直せるサービスに作り替える。

自分が使うサービスを作ろうと思ったので、これにした。

## 機能的には、「みんなでコードレビュー」で実装したものを削っていけばできるから、簡単だった。

・自分のメモを残せるようにする
・ログイン
・お気に入り/いいね
・メモをちょっとカラフル:alarm_clock:に（codeなんかものせられる）
・マイページ

でも、見た目がダサいので、これだと自分もつかわなくなるかもと思ったので、見た目を改善する:night_with_stars:

## デザインテンプレートを参考に使ってみる
自分のデザイン力は、絶望的:thermometer_face:なので、これを利用すれば手っ取り早くかっこよくなる。
調べると、ほんとうにいっぱいあってありがたい。

今回参考にしたサイトは、「https://template-party.com/」

利用規約を読むと、
> テンプレート下部に入っているWeb Design:Template-Party（もしくは「Produced by Girl Staff」か「Web Design:Template-Party＆> Girl-Staff」等の表記である場合あり）の文字とリンクは外さずにご利用下さい。その他は自由にカスタマイズして頂いて構いません


だったので、必要な機能だけ残してカスタマイズさせてもらった。:soccer:
見た目はこんな感じ

![エンジニアメモ - Google Chrome 2020_07_11 13_55_26 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325600/3bdb70a0-cceb-0b79-9b2b-02455e88a8bb.png)

前よりめちゃいい。つぎからこういったサイトを作るときはテンプレートを利用しよ！:guardsman_tone3:

## サイト内の色を決める

僕はとりあえず、オレンジをメインにてきとうに決めていたが、すごく見えづらかった。
調べてみると、世の中には配色デザインを提供してくれるサービスがあるらしい。

そんな中で今回利用したのが、「[PALETTABLE](https://www.palettable.io/)」

何百万人のデザイナーの配色知識を詰めこんだツールで、組み合わせも考えられた色を、好きか嫌いを選ぶだけで直感的に探し出してくれる。

このサービスを使って、5色決めたことにより、見た目がきれいになった。

# 回収したサイト
[エンジニアメモ](https://everyone-code-review.com/)

これなら、自分も使いそう:massage:

#まとめ
今回のことで、自分が何にモチベーションをもっているのか少しわかった。
このサービスは、完了したので、次のサービスを作りたい:man_tone1:

今度は、ドメイン名以外すべて無料で開発しようと思う。
記事を読んでくれて、ありがとうございました:bow_tone2:
heroku login で Error: EISDIR: illegal operation on a directory, read というエラーが出たら

## 📝 概要

- Heroku CLIをインストールしてログインしようとしたら、 `Error: EISDIR: illegal operation on a directory, read` というエラーが出た
- 検索してもあまり記事が出てこなかったので、備忘録として書いておきます

## 💻 環境

- macOS Catalina

## 🤔 問題
### 1. Heroku CLIをインストール
[公式ドキュメント](https://devcenter.heroku.com/articles/heroku-cli#download-and-install)通りに下記のコマンドを実行する。

```
% brew tap heroku/brew && brew install heroku
% heroku --version
heroku/7.42.3 darwin-x64 node-v12.16.2
```

### 2. heroku loginを試す
Heroku CLIインストール後に `heroku login` を試すと、下記のようにエラーが出た。

```
% heroku login --interactive
(node:22431) [EISDIR] Error Plugin: heroku: EISDIR: illegal operation on a directory, read
Error occurred during reading netrc file: /Users/uematsu/.netrc
module: @oclif/config@1.13.2
task: runHook prerun
plugin: heroku
root: /usr/local/Cellar/heroku/7.42.3/libexec
See more details with DEBUG=*
Error: EISDIR: illegal operation on a directory, read
```
（ `% heroku login --interactive` でも同じ結果になった）

## 🌟 解決方法
[公式ドキュメントのLogin issues](https://devcenter.heroku.com/articles/heroku-cli#login-issues)に書いてある通り、下記のコマンドを実行する。

```
% mv ~/.netrc ~/.netrc.backup
```

これで正常にログインできるようになった🎉
【AWS】EC2インスタンスにnode.jsをインストールしてサンプルプログラムを実行

# 環境
Windows 10 Home
Tera Term 4.105（Macの人はTerminal）

# 前提
・Amazon Linux EC2 
　－SSH接続可能
　－アウトバウンドのHTTP接続が可能

# node.jsをインストール
```shell:ec2-linux
$ cd ~
$ wget https://nodejs.org/dist/v12.18.2/node-v12.18.2-linux-x64.tar.xz
```
[公式サイト](https://nodejs.org/ja/download/)から最新バージョンのリンクを取得
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286189/93d30e4d-a654-cdc2-2cc3-5dbdbfba2964.png)


node-v12.18.2-linux-x64.tar.xz

```shell:ec2-linux
$ cd ~
$ wget https://nodejs.org/dist/v12.18.2/node-v12.18.2-linux-x64.tar.xz
```

tarファイルがあることを確認

```shell:ec2-linux
$ ls
$ node-v12.18.2-linux-x64.tar
```

拡張子がxzになっているのでtarに変換

```shell:ec2-linux
$ mv node-v12.18.2-linux-x64.tar.xz node-v12.18.2-linux-x64.tar
```

解凍

```shell:ec2-linux
$ tar xvf node-v12.18.2-linux-x64.tar
```

色々出力されて止まったら解凍完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286189/8b5ae034-a58c-4c6e-34d9-7782bb0c7bc5.png)

実行コマンドがインストールされるnode-v12.18.2-linux-x64/binにパスを設定して移動

```shell:ec2-linux
$ export PATH=$PATH:~/node-v12.18.2-linux-x64/bin
$ cd node-v12.18.2-linux-x64/bin
```

app.jsという[サンプルプログラム](https://nodejs.org/ja/docs/guides/getting-started-guide/)を用意

```javascript:app.js
const http = require('http');

const hostname = 'localhost';
const port = 8080;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
```

実行

```shell:ec2-linux
$ touch app.js
$ vim app.js

~~~~~~サンプルプログラムを貼付け(vimコマンドは各自お調べください)~~~~~~

$ node app.js
Server running at http://localhost:8080/
```
Ruby on Rails アプリケーション新規作成コマンド

# アプリケーション新規作成
アプリケーションを作成するコマンドは
**rails new アプリケーション名**

例：
```rails new myapp```
# オプション指定
アプリケーション新規作成時にオプションを指定することができる。
## Rails バージョン指定
バージョンを指定してアプリケーションを作成する場合は
**rails _バージョン_ new アプリケーション名**

例：
```rails _5.2.3_ new myapp```
## データベース指定
データベースを指定してアプリケーションを作成する場合は
**rails new アプリケーション名 -d データベースの種類**

例：
```rails new myapp -d mysql```

データベースの指定がない場合、データベースはデフォルトのsqliteとなる。
## テストを作成しない
テストを作成せずにアプリケーションを作成する場合は
**rails new アプリケーション名 -T**
## .gitignoreファイルを作成しない
.gitignoreファイルを作成せずにアプリケーションを作成する場合は
**rails new アプリケーション名 -G**
## その他のオプション
上記以外にも様々なオプションが存在する。
次のコマンドでヘルプを表示し、全てのオプションコマンドとその内容を確認できる。
**rails new -h**
PHPで身長を比較する関数を作る



```php
<?php


  function tall($X, $x, $Y, $y) {
        if ($x > $y) {
        return $X;
      } else if ($x < $y) {
        return $Y;
      } else {
        return 'どっちも一緒';
      }
  }

  function answer($name) {
    if ($name === 'どっちも一緒'){
      echo 'どっちも一緒';
    } else {
      echo $name . 'の方が大きい';
    }
  }

  $A = '河合俊一';
  $B = 'ボブサップ';
  $a = 195;
  $b = 196;
   
  $tall = tall($A, $a, $B, $b);
 
  answer($tall);
?>
```

実行結果は

```
ボブサップの方が大きい
```


コード汚なかったり、
もうちょっといい書き方あるかも

ちなみに関数側の引数は仮引数で何を入れてもいいんだけど
実引数の変数名に合わせた方がいいのか、
関数側の値に合わせた変数を命名した方がいいのか...

どっちだろうか...
DDDについてのまとめページ

DDDについて解説したページをまとめています


[開発(ほぼ)未経験がDDD本を読んだので解説する①](https://qiita.com/bouitengineer12/items/29605cebe415e0fa618f)
[開発(ほぼ)未経験がDDD本を読んだので解説する②](https://qiita.com/bouitengineer12/items/3eb46a45e9fa4668fba2)
Amazon Rekognitionによる同一人物検出 

[前回](https://qiita.com/i_12121226/items/50bb4f048d121c893268)に引き続き、Amazon Rekognitonを使って同一人物の検出を行ってみました。

# 概要
Amazon Rekognitionのcompare_facesというAPIを使って同一人物の検出を行います。compare_facesに2つの画像を入力するのですが、1つ目が検出したい人物の映った画像、2つ目が検出対象の画像となります。

# 実行環境

OS：Windows10
言語：Python3.7

# 事前準備

AWS CLI(aws configure)にて、以下の認証情報をセットしておきます。

AWS Access Key ID
AWS Secret Access Key
Default region name
Default output format

# ソースコード(face_compare.py)

```py:face_compare.py
import boto3
import sys
from PIL import Image,ImageDraw

# 引数のチェック
if len(sys.argv) != 3:
	print('2つの画像ファイルを引数に指定してください。')
	exit()

# Rekognitionのクライアントを作成
client = boto3.client('rekognition')

# 画像ファイル2つを引数としてcompare_facesを実行
with open(sys.argv[1],'rb') as source:
	with open(sys.argv[2],'rb') as target:
		response = client.compare_faces(SourceImage={'Bytes':source.read()},TargetImage={'Bytes':target.read()})

# 同一人物が検出されない場合は処理終了
if len(response['FaceMatches'])==0:
	print('同一人物は検出されませんでした。')
else:
	# 2つ目の画像ファイルを元に、矩形セット用の画像ファイルを作成
	img = Image.open(sys.argv[2])
	imgWidth,imgHeight = img.size
	draw = ImageDraw.Draw(img)

	# 検出された顔の数分、矩形セット処理を行う
	for faceMatch in response['FaceMatches']:

		# BoundingBoxから顔の位置・サイズ情報を取得
		box = faceMatch['Face']['BoundingBox']
		left = imgWidth * box['Left']
		top = imgHeight * box['Top']
		width = imgWidth * box['Width']
		height = imgHeight * box['Height']		

		# 矩形の位置・サイズ情報をセット
		points = (
			(left,top),
			(left + width,top + height)
		)

		# 顔を矩形で囲む
		draw.rectangle(points,outline='lime')

		# 画像ファイルを保存
		img.save('detected_' + sys.argv[2]) 	

		# 画像ファイルを表示
		img.show()

```

# 簡単な解説

概略としては以下のような処理を行っています。

①プログラム実行時の引数からRekognitionに入力する2つの画像ファイルを取得する。
②上記①の画像ファイルを引数としてRekognitionのcompare_facesを実行する。
③Rekognitionから返却されるJsonのFaceMatches・BoundingBoxから、認識された顔の位置・サイズ情報を取得する。
④上記③から矩形付きの画像ファイルを作成し、表示する。

# 実行結果

## コマンド

```console
python face_compare.py ichiro1.jpg ichiro2.jpg
```

## 入力画像1(ichiro1.jpg)

![ichiro2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/c8b2d4b2-4727-6339-9edd-3eafb3494369.jpeg)

## 入力画像2(ichiro2.jpg)

![ichiro3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/2776bac6-2737-a841-696e-b5249731fb21.jpeg)

## 出力画像(detected_ichiro2.jpg)

![detected_ichiro3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240454/b42c8449-c48b-7845-0e64-bceb0085c2a7.jpeg)

イチロー選手を検出してくれました。

# まとめ

[前回](https://qiita.com/i_12121226/items/50bb4f048d121c893268)のdetect_facesと同様に、APIを使って簡単に画像認識を行うことができます。compare_facesを使うことで、大量の画像から探したい人物が映っているものを簡単に抽出することができると思います。AWSのサイトによると、日本では新聞社や写真サービスの会社などで使われているようです。
Azure CLI でサブスクリプションを選択する

何回も出てくるのに、手を抜いてちゃんと調べなかったので調べてみる。Azure CLI はよいのだが、問題は、Query で使える [JMESPath](https://jmespath.org/tutorial.html) をちゃんと理解していないことにある。Helpにもサンプルは出てこないので、自分で解決するしかなかったのでそれをメモしておこう。

# Azure CLI で WSL2 からログインする

通常のログインは `az login` でよいのだが、GUI環境ではない場合下記のログイン方法が使える。

```bash
$ az login --use-device
```

他にも GUIが使えないところで使える方法としては、Service Principal を使う方法などが存在するが、簡単な、実験をするなら上記のがお手軽だ。

さて問題は、デフォルトが、自分が意図したサブスクリプションではなく、複数のサブスクリプションがあるケース。

```bash
$ az account show
```

とすると、現在のサブスクリプションが表示される。これを違うサブスクリプションに変えたい場合

```bash
$ az account set -s YOUR_SUBSCRIPTION
```

とやればよい。ところがたくさんサブスクリプションが出てくるときは面倒だ。下記のようなスキーマになっているので、特定の条件に合ったものだけプロジェクションしたい。

```JSON
  {
    "cloudName": "AzureCloud",
    "homeTenantId": "HOME_TENANT_A",
    "id": "SUBSCRIPTION_ID",
    "isDefault": false,
    "managedByTenants": [],
    "name": "EA DEV",
    "state": "Enabled",
    "tenantId": "TENANT_A",
    "user": {
      "name": "YOUR_E_MAIL",
      "type": "user"
    }
  },
  {
    "cloudName": "AzureCloud",
               :
]
```

`USHIO` を含む文字列を検索したい場合は、`contains` 関数が使える。また、ポイントは `--query` の引数は、ダブルクオートでくくる必要がある。ちなみに上記のスキーマだと、プロパティ名の中に配列があるのではなく、一番最初の要素が配列になる。そのようなケースだと次のように書く。二つ目の`.[id, name]` はフィルターした結果をプロジェクションするときのどの項目をピックアップするか？という意味。

```bash
$ az account list --query "[? contains(name, 'USHIO')].[id, name]"
A few accounts are skipped as they don't have 'Enabled' state. Use '--all' to display them.
[
  [
    "MY_SUBSCRIPTION_ID",
    "Test Sub USHIO"
  ]
]
```

ちなみに、フィルターしたくなくて、全件リストしたいときは

```bash
$ az account list --query "[].[id,name]" 
```

わかったらそれで特定のサブスクリプションにしてしまえばいい

```bash
$ az account set -s MY_SUBSCRIPTION
$ az account show
```

これで完成。

# TODO

実はこの方法を使っても、たまに元のサブスクリプションに戻る。さっと探したけど見つからないので同僚に聞いてみよう。月曜日に。

# リソース
それ以外のリソースとしては下記のものが参考になる。

* [JMESPath Tutorial](https://jmespath.org/tutorial.html)
* [JMESPath Specification](https://jmespath.org/specification.html#functions)
* [equivalent to `azure location list` in new CLI](https://github.com/Azure/azure-cli/issues/1520)
* [How to handle JMESPath contains filter on attribute that may be null?](https://stackoverflow.com/questions/55303239/how-to-handle-jmespath-contains-filter-on-attribute-that-may-be-null) -> sample of contains()
Laravel + vueで画像を参照する時の書き方

vueで画像を表示する時はimgタグとbackground-imageを使う場合が考えられる。
ビミョーにやり方が違うので、備忘録として書いておきます。

### imgタグを書く場合
Index.vueなど、vueファイルでimgタグを書く時は以下のように書く。

```vuejs
<img :src="'/images/top-appeal.jpg'" alt="" class="c-img">
```
##### やること
①`src`をv-bindする
②srcのダブルクォーテーション内部にシングルクォーテーションを書く
③vueファイルはトランスパイルされ`public/`がディレクトリとしての起点になるので、そこからの相対パスを入力する。上記サンプルは、↓の画像を参照していることになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420171/eff44d85-676a-cebc-0210-e98b199736da.png)

### background-imageを書く場合

vueファイルのstyleタグ内で`background-image`を書く時は、以下のようにする。

```css
background-image: url('../image/hero-baner.jpeg');
```
##### やること
①画像ファイルを`resources`ディレクトリ(scssやvueを置く場所)内に置く。
②vueで`background-image`を書く時に、`resources`ディレクトリを起点にした相対パスを書く。
↑の場合、↓の画像を参照している。
`npm run watch`などでコンパイルした時に`public/images`に同様の画像をコピーが作られているのがわかるハズ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/420171/c4cc10f8-00e3-c8a3-99ed-ec1f914c686a.png)
CUIによる C# Managed DLL の作成・ Global Assembly Cache へのインストール・利用手順

### 概要

[Visual Studio](https://visualstudio.microsoft.com/) が利用できない環境で C# Managed DLL を [Global Assembly Cache](https://docs.microsoft.com/dotnet/framework/app-domains/gac) (以下「GAC」)にインストールし、
実行ファイルから参照したい機会があったので、手順をまとめておきます。

今時そんな機会はほぼ無いと思われますが、もしかしたらあるところにはあるかも？多分。

---

### 手順

#### 1. C# Managed DLL を作成

概要に記載の通り「 [Visual Studio](https://visualstudio.microsoft.com/) が利用できない環境」想定のため、手動で作成します。
本記事の主目的では無いため、詳細は省略。
拾ってきた C# Managed DLL を GAC にインストール・利用したい場合は、手順ごと省略可能です。

```C#:SampleDll.cs
[assembly: System.Reflection.AssemblyVersion("1.0.0.0")]

public static class SampleDll {
  public static void WriteLine() {
    System.Console.WriteLine("WriteLine from SampleDll.");
  }
}
```

```bat:C#&nbsp;Managed&nbsp;DLL&nbsp;ビルドコマンド
sn -k KeyFile.snk
csc /o+ /t:library  /keyfile:KeyFile.snk SampleDll.cs
```

#### 2. C# Managed DLL の GAC へのインストール

[Gacutil.exe](https://docs.microsoft.com/dotnet/framework/tools/gacutil-exe-gac-tool) を使用することで C# Managed DLL の GAC へのインストールが可能です。

[Microsoft Docs](https://docs.microsoft.com/dotnet/framework/tools/gacutil-exe-gac-tool) には「このツールは、Visual Studio と共に自動的にインストールされます。」と記載があり [Visual Studio](https://visualstudio.microsoft.com/) が必要そうですが、
(恐らく)標準でProgram Filesの「Microsoft SDKs」に入っているため
Visual Studio は必要ありません。
(手元の環境では「C:/Program Files (x86)/Microsoft SDKs/Windows/v10.0A/bin/NETFX 4.8 Tools/gacutil.exe」にありました。)

```bat:インストールコマンド
gacutil /i SampleDll.dll
```

#### 3. GAC の利用

##### ビルド時の利用

GAC へインストールした C# Managed DLL は [.NET Framework
のバージョンにより](https://devlights.hatenablog.com/entry/20120316/p1)「%windir%\Assembly」または「%windir%\Microsoft.NET\Assembly」に格納されています。
しかし、[そのパスを csc.exe で簡単に参照することはできない](https://stackoverflow.com/questions/885041/how-do-you-use-gacd-assemblies-as-references-with-csc-exe)ため、
本記事では「実行時の参照」のみ「 GAC の利用」として扱い、ビルド時の利用方法については扱いません。
CUIによるビルド時に GAC を利用したい場合、素直に [MSBuild](https://docs.microsoft.com/visualstudio/msbuild/msbuild?view=vs-2019) を使用しましょう。
([Microsoft Docs](https://docs.microsoft.com/visualstudio/msbuild/msbuild?view=vs-2019) に記載されている通り [MSBuild](https://docs.microsoft.com/visualstudio/msbuild/msbuild?view=vs-2019) は [Visual Studio](https://visualstudio.microsoft.com/) に依存しません。)
<sub><font size="1" color="#888888">~~( GAC にインストールされたファイルからリンク対象 DLL を検索するスクリプトを作成し、 csc.exe をラップするのも一つの方法です。)~~</font></sub>

```C#:SampleExe.cs
static class SampleExe {
  static void Main () {
    SampleDll.WriteLine();
  }
}
```

```bat:ビルドコマンド
csc /o+ SampleExe.cs /r:SampleDll.dll
```

##### 実行時の利用

[Microsoft Docs の ランタイムがアセンブリを検索する方法](https://docs.microsoft.com/dotnet/framework/deployment/how-the-runtime-locates-assemblies)に記載されている通り、
アプリケーション構成ファイルの作成・編集等、実行時の参照先としての一切の指定は不要で、
その環境におけるあらゆる実行ファイルで(必要に応じて)自動で参照されます。

例として、「[3. GAC の利用](#3-gac-の利用) &gt; [ビルド時の利用](#ビルド時の利用)」で作成した実行ファイル「SampleExe.exe」は、
ビルド時に参照先と指定した同一ディレクトリにある「SampleDll.dll」を削除しても問題なく動作します。
[macOS Catalina] fish-shell でRustupコマンドをインストールする方法

## [macOS Catalina] fish-shell で rustup コマンドをインストールする方法

## 以下のコマンドを実行する

```zsh
curl https://sh.rustup.rs -sSf | sh
```

この場合、以下のようなエラーが出る場合がある。

```zsh
error: could not amend shell profile: '/Users/{ユーザー名}/.profile'
error: caused by: could not write rcfile file: '/Users/{ユーザー名}/.profile'
error: caused by: Permission denied (os error 13)
```

fish-shell からインストールすると設定ファイルの上書きに失敗することがあるらしい・・・

権限系のエラーなので、 `sudo` をつければよいかと考えられるが、それでも上手く行かない。

そこで、 `--no-modify-path` オプションを付けると解決できる。

```zsh
curl https://sh.rustup.rs -sSf | sh -s -- --no-modify-path
```
ReactNavigation V5 で画面遷移をクラスで行う方法

# 環境
ReactNavigation V5

# 目的
## やりたかったこと
- ReactNavigation V5での画面遷移
- App.jsにナビゲーションの設定がしてある
- 各スクリーンは、クラスとして別ファイルに切り出し保持している

### 対応前のコード

```:ディレクトリ構成
App.js
screens
  画面1.js
  画面2.js
```

```javascript:App.js
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import VID0 from './screens/画面１';
import VID1 from './screens/画面２';

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="画面１" component={画面１} />
        <Stack.Screen name="画面２" component={画面２} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

```

```javascript:App:画面１
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';


class 画面１ extends React.Component {

  //スタートボタン押下時
  onStartButtonPress = () => {
    navigation.navigate('VID2'); //＝＞　注１
  }


  render() {
    return (
      <ScrollView
        <Button
        title='次へ'
        onPress={this.onStartButtonPress}
         />
      </ScrollView>
    );
  }
}
export default 画面１

```


# 問題
- 子スクリーンが親のNavigationを取れず、遷移できない
- 注１　の箇所　元々のReact-nativeのサンプルでは、functionであるため引数でnavigationを渡している

> https://reactnavigation.org/docs/navigating

- しかし、今回の場合クラスであるため、引数で渡せない




# 対策
> https://reactnavigation.org/docs/use-navigation/

- useNavigation　ホックを利用すれば、navigationを取ることができる
- 注意点として、このホックを使えるのは、関数内だけであるため、class中では使えない
- 以下のエラーが発生する

```
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
```

- そのため、関数でクラスをラップする

## やること
- `useNavigation` をインポートする
- classを関数でWrapし、関数の方をexport default する
- class のexport defaultを消す
- export defaultする関数を作る
- 関数の中で `useNavigation()` を行い、navigationを取得する
- 関数はpropsを引数にとり、画面のクラスをreturnする。この時propsにnavigationを渡す
- クラスは、遷移イベント時に、this.propsからnavigationを取得する

### 対応後のコード

```javascript:App:画面１
// ============ useNavigationを追加
import { NavigationContainer, useNavigation } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';

//＝＝＝ここでfunctionでクラスをWrap　こっちをexport defaultする

export default function(props) {

//=== useNavigationを取得し、クラスにnavigationを渡す
  const navigation = useNavigation();

  return <画面１ {...props} navigation={navigation} />;
}

//＝＝＝＝＝exportしない
class 画面１ extends React.Component {

  //スタートボタン押下時
  onStartButtonPress = () => {
//=======関数から渡されたpropsのnavigationを取得し、利用する
    const { navigation } = this.props;
    navigation.navigate('VID2');
  }


  render() {
    return (
      <ScrollView
        <Button
        title='次へ'
        onPress={this.onStartButtonPress}
         />
      </ScrollView>
    );
  }
}

```
Confluence(Cloud版)で見出しをわかりやすくする

Confluenceの見出しの見た目を改善したいという話です。
Server版はルックアンドフィールでカスタムcssが適用できたのですがCloud版はできないようでした。

（注意）
これは自分だけしか適用されない方法です。
チームメンバーも同じように見えてほしいため、Theme適用する（作る?)方法も模索中です。
他に良い方法ご存じの方がいたら教えて下さい。

## 適用前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47589/4ecdefad-8a95-236f-e8cb-94f10d2eb925.png)

## 適用後
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47589/bd5fcdff-9f74-791e-3c33-307414f856d6.png)

## 設定方法

~~StylishというChrome拡張をインストール。~~
~~https://chrome.google.com/webstore/detail/stylish-custom-themes-for/fjnbnpbmkenffdnngjfgmeleoegfcffe?hl=ja~~

> 2024/5/30追記（上記のツールは使えなくなったため）
StylusというChrome拡張をインストール
https://chromewebstore.google.com/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne
https://add0n.com/stylus.html


管理画面にて、以下のcssを貼り付けて、

```css
.wiki-content h1 {
    padding: .5em .75em;
    color: #494949;
    background-color: #E0E8F9;
    border-left: 6px solid #1D4A95;
    border-bottom: solid 3px #d7d7d7;
}
.wiki-content h2 {
    padding: .5em .5em;
    color: #494949;
    background: #FFF8CB;
    border-left: solid 5px #F3C759;
}
.wiki-content h3 {
    padding: .25em .5em;
    border-left: 5px solid #446FB7;
    border-bottom: 1px solid #ccc;
}
.wiki-content h4 {
    padding: .3em .6em;
    border-left: 3px solid #F3C759;
    border-bottom: 1px solid #ccc;
}
.wiki-content h5 {
    padding: .2em .5em;
    border-left: 4px solid #CCC;
}
```

以下の正規表現に一致するURLに対して適用するだけです。

```
https://.*\.atlassian\.net/wiki/spaces/.*
```

# 2024/5/30追加： Confluenceの行をもう少し詰めて表示したい場合
  
Confluenceの行をもう少し詰めて画面内に表示される情報量を増やしたい場合は以下のcssも追加してください。


## 適用前

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47589/4d289259-3b03-11f3-44d8-611288d4ef1a.png)

## 適用後

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47589/d0ec9a05-a426-2ab6-5ac0-2517d8884043.png)


## 設定方法

メインコンテンツの行の高さを調整したい場合
```
div[data-testid=pageContentRendererTestId] p {
    line-height: 16px !important;
}
```

メニューバーを縦に詰めて表示する場合
```
a[data-testid=page-tree-item] {
    height: 28px !important;    
}
```


おわり。
【備忘】AI・機械学習・python関連のWebサイト【随時更新】

## 初めに

- pythonやら機械学習の勉強を開始して8カ月程度が経って、素人に毛が生えた程度の私が過去勉強の際に参考になったウェブサイトをご紹介します。
- 網羅性は皆無ですが、自身のブックマークの整理も兼ねて記事にしたいと思います。

## Blogs

- [人工知能ブログ](https://deepage.net/blog/)
DeepAgeが運営しているWebサイト。
基礎的な情報から、Tensorflowの使い方まで色々と載ってます。


- [Let'sプログラミング](https://www.javadrive.jp/)
Buzzword Inc.が運営しているブログ。様々な言語の基本的な内容がきれいに纏まっていて初心者の方（含む自分）にはわかりやすいサイトだと思います。

- [米国データサイエンティストのブログ](http://datawokagaku.com/)
Twitterでも広く情報発信をしてくださっている、`かめ@米国データサイエンティスト`さんのブログ。データサイエンティストになりたい人や、米国で働きたい人にとっては参考になる情報ばかりです。

- [note.nkmk.me](https://note.nkmk.me/)
pythonの勉強をしていてわからないときにググると、必ずと言っていいほど上位に出てくる素晴らしいサイト。基本的なコードの書き方や、「あの操作、どうするんだっけ？」的な状況の時に非常に役に立ちます。
(このBlogに書いてあることを写経するだけでも結構な勉強になるかと思います。)

- [予備校のノリで学ぶ「大学の数学・物理」](https://yobinori.jp/index.html)
いわずと知れた人気教育系YouTuber:たくみさんのHP。機械学習やDeep Learningを勉強する時に必須な線形代数や統計を勉強する際に非常に役に立ちます。
YouTubeのチャンネルはこちら >>> [予備校のノリで学ぶ「大学の数学・物理」](https://www.youtube.com/channel/UCqmWJJolqAgjIdLqK3zD1QQ)

- [u++の備忘録](https://upura.hatenablog.com/archive)
Kagglerで有名なu++さんのブログ。KaggleのSlackとかで質問すると、素早く丁寧に返事をしてくれる素晴らしいお方。kaggleやら競プロ関連の記事が多数ありある程度データ分析やプログラミングに慣れた人には非常に面白い内容かと。

- [戦略コンサルで働くデータサイエンティストのブログ](http://www.ie110704.net/)
タイトルの通りのブログです。様々な分析についてブログに記載していただいており、非常に面白いです。
そして何より、ブログがおしゃれです（笑）

- [CUBE SUGAR CONTAINER](https://blog.amedama.jp/)
個人的興味のある分野を調べていた際に見つけたブログ。（全部読んだわけではないが）ブログの内容はpython関連の記事が多く、またコードもしっかり書いてあり再現性がある良質なブログと思われる。


## データ分析サイト
- [kaggle](https://www.kaggle.com/)
企業や政府などの組織とデータ分析のプロであるデータサイエンティスト/機械学習エンジニアを繋げるプラットフォーム。2017年からGoogleが親会社となって運営している。

- [SIGNATE](https://signate.jp/)
kaggleの日本語版とでもいうのでしょうか。英語が苦手な人はこちらから取り掛かるのがいいかもしれません。

- [Nishika](https://www.nishika.com/)
（最近出来た？）新しいデータ分析コンペプラットフォームのようです。仕組みは上記2つと同じだと思います。


## （個人的）お勧めYouTubeチャンネル
たくさんあるので個々のチャンネルについては書きませんが、動画を見ながら写経するだけでも勉強になります。
YouTubeは外国人のチャンネルが面白いですね（というかレベルが違う）。

- [365 Data Science](https://www.youtube.com/channel/UCEBpSZhI1X8WaP-kY_2LLcg)
- [Computer Science](https://www.youtube.com/channel/UCbmb5IoBtHZTpYZCDBOC1CA)
- [Coding Tech](https://www.youtube.com/channel/UCtxCXg-UvSnTKPOzLH4wJaQ)
- [Data Is Beautiful](https://www.youtube.com/channel/UCkWbqlDAyJh2n8DN5X6NZyg)
- [DeepMind](https://www.youtube.com/channel/UCP7jMXSY2xbc3KCAE0MHQ-A)
- [edureka!](https://www.youtube.com/channel/UCkw4JCwteGrDHIsyIIKo4tQ)
- [SuperDataScience](https://www.youtube.com/c/SuperDataScience/playlists)
- [Corey Schafer](https://www.youtube.com/user/schafer5)　<-- (NEW !!)





まだまだ、素晴らしいサイトは多くありますが、気が向いたときに随時更新していきたいと思います。
[Rails] link_toのリンク先を別タブで表示させたい

Railsでlink_toを使うときに別タブで表示させたいと思い、実装したのでメモとして残しておきます。


# 手順

まず `link_to` で表示させたい文字列とリンク先URLを指定。

```rhtml
<%= link_to "文字列", "リンク先URL" %>
```

別タブで表示させるため、`target: :_blank` を追加

```rhtml
<%= link_to "文字列", "リンク先URL", target: :_blank %>
```

これで別タブで開けるようになる。
しかし、これだとパフォーマンスとセキュリティの面で問題が。。。

- [グーグルのエンジニアが警告、「別タブで開く」リンクは実はヤバいんだって！？【SEO情報まとめ】](https://webtan.impress.co.jp/e/2020/03/13/35510) 
- [実はヤバい？危険な「別タブで開く（target=”_blank”）」](https://wwg.co.jp/blog/3807)

この問題を回避するためには `rel="noopener noreferrer"` をつけるといいみたい。

```rhtml
<%= link_to "文字列", "リンク先URL", target: :_blank, rel: "noopener noreferrer" %>
```

これで安心して別タブを開けます。
2020-07-11 PYNQ > Jupyter NotebookでHDMIモニタにsine curveを描く (pynq.lib.video Module使用)

```txt:動作環境
Windows 10 Pro (v1909) 
PYNQ-Z1 (Digilent) (以下、PYNQと表記)
```

### 関連

- [2020-07-11 PYNQ > HDMI出力で線を引く (pynq.lib.video Moduleのwriteframe()使用 + frameのRGB値を変更)](https://qiita.com/7of9/items/21c528ec9991d2c1f544)

### 概要

- bitstreamはbase.bitを使う
- [pynq.lib.video Module](https://pynq.readthedocs.io/en/v2.5.1/pynq_package/pynq.lib/pynq.lib.video.html)にあるAPIでHDMI入力とHDMI出力を行う
- sine curveを描画する

### 方針

- 描画を消すフレーム blkframeを用意
- sine curveを描画するフレームを用意
- 両社のフレームを交互に表示する

こうすると、アニメーション表示もできるようになる。
今回は静的な表示ではあるが。

### 接続

PCのHDMI出力をPYNQに読み取らせる。

-    A. PCのHDMI端子 <-> PYNQのHDMI IN
-   B. PYNQのHDMI OUT <-> モニタのHDMI

PCからのHDMI出力は今回用意しているが、クロックの設定などすれば不要になると思われる。

### 実装

Jupyter Notebookで以下を順番に実行する。

base.bitのダウンロードとHDMI IN/OUT設定。

```py
from pynq.overlays.base import BaseOverlay
from pynq.lib.video import *

base = BaseOverlay("base.bit")
hdmi_in = base.video.hdmi_in
hdmi_out = base.video.hdmi_out
```

HDMIを動かす。

```py
hdmi_in.configure()
hdmi_out.configure(hdmi_in.mode)

hdmi_in.start()
hdmi_out.start()
```

描画をする。

```py
import time 
import numpy as np
from datetime import datetime
import math

adt = time.time()
print(adt)

blkframe = hdmi_out.newframe()
print(blkframe.shape)  # (720. 1280. 3)
# blkframe = np.zeros(720 * 1280 * 3, dtype = int).reshape(720, 1280, 3)
for row in range(0, 100): 
    for col in range(720):
        blkframe[col][row][0] = 0
        blkframe[col][row][1] = 0
        blkframe[col][row][2] = 0

drawframe = hdmi_out.newframe()
for row in range(0, 100): 
    for col in range(720):
        drawframe[col][row][0] = 0
        drawframe[col][row][1] = 0
        drawframe[col][row][2] = 0

adt = time.time()
print(adt)

for loop in range(10): 
    for ang in range(360):
        x = 720 - ang - 1  # 720: width 
        y = int(math.sin(math.radians(ang)) * 50) + 49
        drawframe[x][y][0] = 0 # blue
        drawframe[x][y][1] = 255 # green
        drawframe[x][y][2] = 0 # red
        # print(x,y)
    if loop % 2 == 1:
        outframe = drawframe
    else:
        outframe = blkframe

    hdmi_out.writeframe(outframe)
    time.sleep(1)
```

数秒ごとに真っ黒の画面とsine curveの描画交互に表示される。

![IMG_1894.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/16c6c3af-df0f-73b5-3982-36575113bb06.jpeg)


### 処理時間

```
1518729496.3631167
(720, 1280, 3)
1518729515.325932
```

初期化処理で20秒近くかかる。numpy ndarrayへの代入が遅いようだ。
Mac で Winsows からやって来た行儀の悪い SJIS ファイルを UTF-8 にする

Mac で Winsows から来た、行儀の悪い `SJIS` ファイルを `UTF-8` にする

```shell

nkf --ic=Shift_JIS --oc=UTF-8 file-encoded-as-sjis.csv
```
【超初心者用】MacでApacheのドキュメントルートを確認する方法（Apacheの設定ファイルを探すというミッション）

「ドキュメントルートってどこだっけ？」となることが多い。確認方法だけわかっていたらいちいち覚えていなくてもいいし、変更することがあれば確認方法を知らないと困ることになるので、メモしておきます。

###ターミナルから確認するためのコマンド
ターミナルの起動を起動して次を入力すると出てきた！


```
$ cd /etc/apache2
$ ls
extra		magic		original	users
httpd.conf	mime.types	other
```
左下に発見しました。


###httpd.confのディレクトリ
`/etc/apache2/`の中にあることがわかりました。
apache2の2はなんなんだろうと思ってしまいます。ターミネーター2の2とは意味が違うんでしょうけど。とりあえず`apache`ではなく`apache2`にあるということが注意点ですな。

###httpd.confを開く
```
$ sudo vi httpd.conf
```

で開きます。

viの操作も全然なれていませんが、頑張って探します。
###DocumentRootの記述を探す
コマンドラインに` /Document`を入力すれば検索もできます。探すだけならこれでいいですね。



```
254 DocumentRoot "/Library/WebServer/Documents"
255 <Directory "/Library/WebServer/Documents">
```

ApacheはMacに標準で入っていたものを使っています。

こういう設定ファイル系がソフトごとにあるけど、結局どこにあるかわからなくなってしまう。これってエンジニアの方は覚えてるんですかね？メモってるんですかね？管理用のなんかがあるんですかね？どうなんでしょう？





###参考にしたサイト
https://qiita.com/s2maeda/items/21d9458e44f86597d1ae
[Git]This repository moved. Please use the new location

GitHubのリポジトリ名を変更後に、pushしたら下記のようなメッセージが出ました。

```terminal:Terminal
$ git push origin master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 294 bytes | 294.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
remote: This repository moved. Please use the new location:
remote:   https://github.com/tetsu-upstr/JSblog.git
To https://github.com/tetsu-upstr/blog.git
   7468063..962d5cd  master -> master
```

リポジトリが動いたから、新しい場所（URL）を設定してねって書いてあります。

```terminal:Terminal
This repository moved. Please use the new location:
```

##解決方法

現在のURL設定を確認します。

```terminal:Terminal
git remote -v
```

下記のように表示されました。

```terminal:Terminal
origin  https://github.com/tetsu-upstr/blog.git (fetch)
origin  https://github.com/tetsu-upstr/blog.git (push)
```

正しいURLを指定します。

```terminal:Terminal
$ git remote set-url origin https://github.com/tetsu-upstr/JSblog.git
```

再度確認してみると、変更されているのがわかります。

```terminal:Terminal
$ git remote -v
origin  https://github.com/tetsu-upstr/JSblog.git (fetch)
origin  https://github.com/tetsu-upstr/JSblog.git (push)
```
AIZU ONLINE JUDGE の問題をSwiftでやってみた(プログラミング入門 文字編)

##トピック #8　文字
[AIZU ONLINE JUDGE](https://onlinejudge.u-aizu.ac.jp/courses/lesson/2/ITP1/8)の問題にSwiftで解答したものになります。競技プログラミングは普段やりませんので、こうしたほうが簡単！などがあればコメントで教えて下さい。
[ブラウザでプログラミング・実行ができる「オンライン実行環境」paiza.IO](https://paiza.io/projects/g8pB6rEnJeIh4YLStunrNg)でテスト出力が行えます

##8_A 大文字と小文字の入れ替え
**問題**
大文字を小文字に、小文字を大文字にして下さい
**解答**

```Swift
let a = readLine()!

func convert(_ text: String) -> String {
var returnText = ""
    for i in text {
        if i.isUppercase {
            returnText.append(i.lowercased())
        } else {
            returnText.append(i.uppercased())
        }
    }
    return returnText
}

print(convert(a))

```

##8_B 数字の和
**問題**
複数のデータセットが入力として与えられます。各データセットは１つの整数 x を含む１行で与えられます。

x は 1000 桁以下の整数です。

x が 0 のとき入力の終わりとします。このデータセットに対する出力を行ってはいけません。

**解答**

```Swift
while(true) {
    let a = readLine()!
    if Int(a) == 0{
        break
    }
    var sum = 0
    let b = Array(a).compactMap({Int(String($0))})
    for i in b {
        sum += i
    }
    print(sum)
}
```

##8_C 文字のカウント
**問題**
与えられた英文に含まれる、各アルファベットの数を数えるプログラムを作成して下さい。 なお、小文字と大文字は区別しません。
**解答**

```Swift
let lowChaArr = (97...122).map{String(Character(UnicodeScalar($0)!))}
var countArr = [Int](repeating: 0, count: 26)

var arrLowNum = 0
let a = readLine()!.lowercased()

for i in a {
    var arrUpNum = 0
    for j in lowChaArr {
        if String(i) == j {
           countArr[arrUpNum] += 1
        }
        arrUpNum += 1
    }
}
for i in 0 ..< 26{
print("\(lowChaArr[i]) : \(countArr[i])")
}
```

##8_D リング
**問題**

リング状の文字列$s$の任意の位置から、時計回りに連続した文字をいくつか選んで文字列$p$が作れるかを判定するプログラムを作成しなさい。

**解答**

```Swift
import Foundation
let ringWords = readLine()!
let searchWord = readLine()!
let ringsWordCount = ringWords.count
let ringsWordIndexStart = ringWords.index(ringWords.startIndex, offsetBy: 0)
let ringsWordIndexHalf = ringWords.index(ringWords.startIndex, offsetBy: ringsWordCount/2)
let ringsWordIndexEnd = ringWords.index(ringWords.startIndex, offsetBy: ringsWordCount)
let frontWords = ringWords[ringsWordIndexStart..<ringsWordIndexHalf]
let backWords = ringWords[ringsWordIndexHalf..<ringsWordIndexEnd]
let convertedRingWords = backWords+frontWords
if ringWords.contains(searchWord) || convertedRingWords.contains(searchWord) {
    print("Yes")
} else {
    print("No")
}
```
Python の仮想環境からの脱出 〜自分の作った仮想環境に閉じ込められかけた話〜

# 前枠
久しぶりに開発しようとMac開いたら，結構前に作った仮想環境に勝手に入ってからしばらく出られなかった．
調べてみると，コマンドが違った模様．

環境

- macOS Catalina 10.15.4
- conda 4.6.11

# どう解決したのか

入ったとき

```bash
(base)$
```

仮想環境から出たい

```bash
(base)$ deactivate
bash: deactivate: command not found
(base)$ source deactivate
bash: deactiate: No such file or directory
```

ダメらしい．

調べてみると同じような症状の人がいた．

- https://teratail.com/questions/214446
- https://devlights.hatenablog.com/entry/2018/02/05/025008

```bash
(base)$ conda deactivate
$
```

脱出完了

# 後枠
仮想環境はまだ慣れない…．
使っていくしかないかな．
新規登録時、ActionMailerでメール送信機能

#概要
ActionMailerを使って新規登録時にwelcomeメールを送ります。

#前提
deviseを用いての、ログイン機能の実装。

#導入手順

###1. ActionMailerを利用する設定
welcomeメールの送信元はGmailを使う記載方法。
`config/environments/development.rb`にてActionMailerの設定方法の記述。

```ruby:development.rb
Rails.application.configure do

#---中略---#

　config.action_mailer.raise_delivery_errors = true
  config.action_mailer.delivery_method = :smtp
  config.action_mailer.smtp_settings = {
    port:                 587,
    address:              'smtp.gmail.com',
    domain:               'gmail.com',
    user_name:            '送信元アドレス',
    password:             'アプリパスワード',
    authentication:       :plain,
    enable_starttls_auto: true
  }
```
アプリパスワードは2段階認証を設定の上16桁のパスワードを発行し、アプリパスワードに記載する。
以下、参考にさせていただきました。
・[Google メールアプリケーション用のパスワードを取得する]
(https://qiita.com/miriwo/items/833d4189140831c5d039)
・[Google 二段階認証設定をONにする方法]
(https://qiita.com/miriwo/items/0331e7241710fb4759fe)

###2. Mailerクラス作成
```terminal
$ rails g mailer UserNotice
```

###3. Mailerクラス編集
```app/mailers/user_notice_mailer.rb
class UserNoticeMailer < ApplicationMailer
  def send_signup_email(user)
    @user = user
    mail to: @user.email, subject: "会員登録が完了しました。"
  end
end
```

###4. メール本文作成
```app/views/user_notice_mailer/send_signup_email.text.erb
ようこそ <%= @user.name %> 様

この度はアカウント登録頂きましてありがとうございます。
```

###5. Userモデル編集
```app/models/user.rb
#---追加---#

  after_create :send_welcome_mail
 
  def send_welcome_mail
    UserNoticeMailer.send_signup_email(self).deliver
  end
```
after_create を使うことで、Userが新規作成された後にメールを送信するためのメソッドを呼び出すことができます。

以上で、welcomeメールが送信されるはずです。
Laravel アプリケーションファイルを$ git clone 後に実施することメモ

# 目的

- LaravelアプリをGitHubからcloneした後に実施しないといけないことを毎回忘れるので自分用にまとめる

# 実施環境

- ハードウェア環境

| 項目 | 情報 |
| --- | --- |
| OS | macOS Catalina(10.15.5) |
| ハードウェア | MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) |
| プロセッサ | 2 GHz クアッドコアIntel Core i5 |
| メモリ | 32 GB 3733 MHz LPDDR4 |
| グラフィックス | Intel Iris Plus Graphics 1536 MB |

- ソフトウェア環境

| 項目 | 情報 | 備考 |
| --- | --- | --- |
| PHP バージョン | 7.4.3 | Homwbrewを用いて導入 |
| Laravel バージョン | 7.0.8 | commposerを用いてこちらの方法で導入→[Mac Laravelの環境構築を行う](https://qiita.com/miriwo/items/adaaaeecae69d6392e08) |
| MySQLバージョン | 8.0.19 for osx10.13 on x86_64 | Homwbrewを用いてこちらの方法で導入→[Mac HomebrewでMySQLをインストールする](https://qiita.com/miriwo/items/ef6a064bc545fbea29d9) |

# することその1 DBの作成

1. 下記コマンドを実行してMySQLにログインする。(MySQLのrootユーザのパスワードを忘れてしまった方はこちら→[Mac ローカル環境の MySQL 8.0 のrootパスワードを忘れた時のリセット方法](https://qiita.com/miriwo/items/1880e9d2ebcfd3c0e60d))

    ```terminal
    $ mysql -u root -p
    ```
1. 下記を実行してDBを作成する。

    ```sql
    create database 作成するDB名
    ```

# することその2 .envファイルの用意

1. cloneしてきたディレクトリで.envファイルの作成をする。
1. cloneしてきた環境の用の.envファイルがGit管理されていない場合は下記のコマンドを実行して.envファイルの例になるファイルから作成する。

    ``terminal
     cp .env.example .env
    ```
1. .envファイル作成後はcloneした環境に合った設定を記載する。(MySQLのユーザ名やパスワード、DB名など)

# することその3 composer install

1. cloneしてきたディレクトリで下記コマンドを実行してcomposer installを実行する。

    ```terminal
    $ composer install
    ```

# することその4 アプリキーの作成

1. cloneしてきたディレクトリで下記コマンドを実行してアプリキーの作成を行う。

    ```terminal
    $ php artisan key:generate
    ```
enumを日本語化して、連動したラジオボタンを生成する方法

# やりたいこと

以下のようなenumをラジオボタンで表現したい。

```
enum loadtype: { normal: 0, trail: 1, beach: 2, track: 3 }
```
かつ、選択肢は日本語で出るようにしたい。

# enumの日本語化について

こちらの記事の内容に従って日本語化します。
https://qiita.com/tanutanu/items/d44a92425188a4489ec6

# 日本語化したenumに連動したラジオボタンを記述

ViewのFormで以下のように記載

```
<%= f.collection_radio_buttons :loadtype, Post.loadtypes_i18n, :first, :last  %>
```

コードの意味は以下の通り

```
<%= f.collection_radio_buttons [column名], [表示対象のコレクション], [value], [textに表示する文字列]  %>
```
gin.Contextを引数にもつ関数をunitテストする

# はじめに
ginのmiddlewareのunitテストをしようと思ったときにちょっとハマったので備忘録としてまとめておきます

# テスト内容
まずはソースツリーから

```
├── main.go
└── middleware
    ├── auth.go
    └── auth_test.go
```

シンプルにGinのクイックスタートにmiddleware.Authorizationを噛ましてるだけです

```go:main.go
package main

import (
	"testing_gin_context/middleware"

	"github.com/gin-gonic/gin"
)

func setupRouter() *gin.Engine {
	r := gin.Default()

    // 認証用のmiddlewareを使用
	r.Use(middleware.Authorization)

	r.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})
	return r
}

func main() {
	r := setupRouter()
	r.Run(":8080")
}

```

contextからヘッダー情報を取得し、トークンが取得できれば認証を行い、その結果をcontextに詰めて返しています。

```go:middleware/auth.go
package middleware

import (
	"github.com/gin-gonic/gin"
)

func Authorization(c *gin.Context) {
	// リクエストヘッダーからaccessTokenを取得
	token := c.GetHeader("Authorization")
	if token == "" {
		c.AbortWithStatusJSON(http.StatusUnauthorized, JSONオブジェクト)
		return
	}

	/*
		　何かしらの認証を行う・・・
	*/

	// 認証の結果をcontextに詰める
	c.Set("userId", "1234567890")
	c.Set("userName", "Taro")
	c.Next()
}
```

```go:middleware/auth_test.go
package middleware

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
)

func TestAuthorization(t *testing.T) {
	// gin contextの生成
	ginContext, _ := gin.CreateTestContext(httptest.NewRecorder())

	// リクエストの生成
	// 今回はmiddlewareのテストのためpathはなんでも可
	req, _ := http.NewRequest("GET", "/", nil)

	// ヘッダー情報の追加
	req.Header.Add("Authorization", "AccessToken")

	// リクエスト情報をコンテキストに入れる
	ginContext.Request = req

	// Authorization関数を実行
	authMiddleware.Authorization(ginContext)


	asserts := assert.New(t)
	asserts.Equal("userId", c.GetString("1234567890"))
	asserts.Equal("userName", c.GetHeader("Taro"))
}
```

# テストのポイント
ポイントとしては

```
// gin contextの生成
ginContext, _ := gin.CreateTestContext(httptest.NewRecorder())
```

```
// リクエスト情報をコンテキストに入れる
ginContext.Request = req
```

の２箇所あたりだと思います。

実はこの`CreateTestContext`どうやら以前は返り値を３つ返していたことがあるらしく、かなりいろんなところで↓のような記述を見たのですが、どうやらバージョンが変わって今の形になったっぽいです（たぶんw）
（参考文献： https://godoc.org/github.com/gin-gonic/gin#CreateTestContext ）

```
c, _, _ := gin.CreateTestContext()
```

またgin.Context自身は

```go
// 公開されているフィールドのみ記述
type Context struct {
	Request   *http.Request
	Writer    ResponseWriter

	Params   Params
	handlers HandlersChain

	Errors errorMsgs

	Accepted []string
}
```

というように定義されていたので今回はRequestフィールドに直接リクエスト内容を入れてあげたような感じです。
（※ginの公式のテストを見てそのやり方を真似しましたw）
https://sourcegraph.com/github.com/gin-gonic/gin@8f3047814e86442c8efbd91ef7007905d47cf6c9/-/blob/context_test.go#L57

#参考文献
- CreateTestContextのgodoc
https://godoc.org/github.com/gin-gonic/gin#CreateTestContext

- ginのテスト
https://sourcegraph.com/github.com/gin-gonic/gin@8f3047814e86442c8efbd91ef7007905d47cf6c9/-/blob/context_test.go#L57
パソコンの正しい捨て方

Windows7 の EOS に伴い、大量のパソコンを処分したので、手順を紹介する。

# まず、法を理解しよう
**資源有効利用促進法**という法律があり、家電製品などはメーカーが責任を持って回収・リサイクルすることが義務づけられている。PCは対象商品に含まれていなかったが、2003年の改正時に追加され、俗にPCリサイクル法とも呼ばれている。
**PCリサイクルマーク**の付いたPCは、メーカーが無料で回収してくれるが、企業・法人で使用する事業用PCは、処分時にリサイクル費用（3,000円）を負担することになっているため、PCリサイクルマークが付いていない。さらに、企業や事業所から排出されたPCは、たとえPCリサイクルマークが付いていても**産業廃棄物**として扱われるため、排出者は廃棄物処理法を順守しなければならない。
![PCリサイクルマーク](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/412ea900-8c17-02a3-8bde-db8ffdbe8ccc.png)
【参考】[産業廃棄物を排出する事業者の方に](https://www.sanpainet.or.jp/service/doc/haisyutsu-pamphlet2.pdf)

# メーカーに処分を依頼しよう
家庭用PCと違い、事業用PCは自治体では引き取ってくれない。
ネットで「パソコン　処分」を検索すると、事業用PCを無料で引き取ってくれるPCリサイクル業者が沢山ヒットする。
なぜ無料かというと、回収したPCをリユース（再利用）またはリサイクル（再資源化）しているからだ。信用できる業者と分かれば任せても良いだろう。
また、PCを手配してくれた出入りのITベンダーや、メーカーの法人担当に相談できるなら、それも良いと思う。

今回はメーカーのリサイクルセンターを利用した。メーカーに直接依頼しても良いが、メーカーリサイクルを代行してくれる[パソコン3R推進協会](https://www.pc3r.jp/)を利用した方が便利だ。

なお、PCがリースの場合、後述の完全消去を行った上で、リース会社に返却する。

# パソコン3R推進協会とは
PCおよびPC用モニタの3R（リデュース、リユース、リサイクル）を促進している一般社団法人。主要なPC事業者の殆どが加盟していて、回収リサイクルを代行してくれる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/f12b4d39-a5cc-eb65-b608-e38f6f85adf9.png)
[事業系PCリサイクル回収申込](https://www.pc3rsupport.jp/pc3r_biz/action/top?m_cd=B99) に沿って進め、集荷を待つだけだ。

# 固定資産の除却
**除却**とは、固定資産を取り壊したり廃棄すること。
除却にかかった費用も固定資産除却損として経費に計上できる。つまり、除却損の金額だけ節税になるというわけだ。

回収後１ヶ月ほどすると、「資産滅却報告書（廃棄証明書）」が発行される。損金経理するためのエビデンスになるので保存しておこう。
![資産滅却報告書](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/e0ec9d03-a735-ae09-d2c9-a9b4f2fcb7d5.png)

# ディスクの完全消去
廃棄・返却する前に、HDD/SDDを完全消去するわけだが、これには２通りの方法がある。

ひとつめは、工場出荷時の状態にリセットした上で、空いたディスク領域を`cipher`コマンドで0埋めする方法。この場合はOSが残るので再利用可能だ。誰かに譲渡するならこれでも良い。

ふたつめは、専用ツールでOSごと削除する方法。本稿で解説する。

なお、プライバシーマークを取得している企業なら、データを消去しましたという記録は必須（フォーマットは任意で決めて良い）。業者に消去を委託した場合はデータ消去証明書で代替できる。

## wipe-out のダウンロード
[wipe-out 公式サイト](http://www.wheel.gr.jp/~dai/software/wipe-out/)よりUSBイメージ版をダウンロードする。

## 起動用USBメモリの作成
ダウンロードしたイメージファイルは、[Win32 Disk Imager](https://ja.osdn.net/projects/sfnet_win32diskimager/) を使ってUSBメモリに書き込む。
いわゆる **Live USB** を作成するので、そのままUSBメモリにコピーしてもダメだ。
![Win32 Disk Imager](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/d8a390cf-8647-6c76-2632-628bdf7ab450.png)

## BIOSブートシーケンスの確認
古いPCでUEFI未対応だったため、BIOS画面から確認または設定する。
USBデバイスがHDD（SSD）より優先されていればそのまま、そうでなければ優先順位を変更する。
![bios.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/066be056-a174-5abc-2c46-ddb649fc4ab4.jpeg)
画面の NEC Mate では USB KEY として表示されている。

## wipe-out の実行
USBメモリを差した状態で電源を入れ、暫くすると wipe-out のメニュー画面になる。
![wipe_out.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/e5e68ad4-2db8-c4d1-9a79-47e6b1e64822.jpeg)
米国防総省の定めている基準（オール0→オール1→ランダム値のデータを書き込み、残留磁気の影響をなくす）にも対応しているが、一般向けには、固定パターンで１回上書き消去を行えば充分、というのが昨今のガイドラインになっている（ディスクが高密度化しているため）。

ここは「0を上書きして、このディスクのデータを消す」で、良いかと思う。
![wipe_out2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/264d9960-deab-ac56-9424-e62a825e7170.jpeg)
ディスク容量にも因るが、小一時間から数時間は待つことになる。
![wipe_out3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236222/64e3d3eb-a4ef-dc84-c3e3-b4d8b7f9c60d.jpeg)
消去が終わったらUSBメモリを抜いて再起動し、OSが起動しないことを確認する。
Cython チュートリアル： shared_ptr の使い方

## 概要
`cython` で`C++`のコードをラップして`python`から呼べるようにする、

というような内容でいろいろ書きましたが、
今回は`cython`での`shared_ptr`の書き方を少し解説します。

少しだけ癖がありますが、慣れてしまえば大丈夫です。


### コードの構成

いつものように、

- `C++ で書かれたクラス`
- `C++ で書かれた関数`

を　`cython`でラップすることで、`python`から呼べるような構成にします。

ここでは例として、

- `C++がTest_CPPというクラスを持っている。`
- `C++のTest_CPPがtest_function というメソッドを持っている`

ということにします。

## 実際のコード

おそらく解説されるよりコードをみた方がわかりやすいと思います。
`Py_Test_CPP`が、`Test_CPP`をラップしているクラスになります。

```test.pyx

from cython.operator cimport dereference as deref
from libcpp.memory cimport shared_ptr, make_shared

cdef class Py_Test_CPP:
    cdef shared_ptr[Test_CPP] ptr

    def __cinit__(self, int arg1):
        self.ptr = make_shared[Test_CPP](arg1)
    
    def test_function(self):
        return deref(self.ptr).test_function()

```

#### 必要なもののインポート
まず、
`dereference`、`shared_ptr, make_shared`を使うのでインポートしています。


#### C++側のオブジェクトの参照のしかた

この際、プロパティとして`C++`側のクラスオブジェクトのポインタを保持している必要がありましたが、
今回はそれを`cdef shared_ptr[Test_CPP] ptr`と書いて`shared_ptr`として保持しています。


#### コンストラクタ
`cython側のコンストラクタ`である`__cinit__`では、
`make_shared` を`self.ptr = make_shared[Test_CPP](arg1)`
のように使うことで、`cython側のself.ptrをシェアドポインタ`にしています。

#### 関数を呼ぶ
`def test_function(self)`のところで、`C++側の関数test_function`をラップしていますが、
その時は`deref(self.ptr)`として、実体にアクセスし、そこから`C++側の関数を呼ぶ`ことでうまくいきます。




### まとめ

`cython`で`shared_ptr`を使う時の書き方について備忘録的にまとめた。


今回はこの辺で。


おわり。
setuptools でサクッと自作モジュールを作る（python）

## 概要
自身で何か開発をしていて、クラスや関数をモジュール化することがあるかもしれません。

`setuptools` を使って`setup.py`を書いてビルドすることによって、

```test.py
import mylib
```

みたいなことができるようになりますが、モジュール化するところで
最低限必要なことを備忘録的にまとめたいと思います。
これを準備すればほんとにすぐにモジュール化できます。

必要最低限なことだけ書くので、細かいところは[公式ドキュメント](https://docs.python.org/ja/3/distutils/setupscript.html)
を参照お願いします。

## 解説

解説は以下の通りです。

- `フォルダとファイル`を用意する。
- `ライブラリの中身`を少し書く。
- `__init__.py`を少し書く。
- `setup.py`を少し書く。
- `python setup.py` のコマンド実行
- オプション（ライブラリが他のライブラリに依存している時）


### 用意するもの

必要なものはこれだけです。

- ライブラリの中身を入れるフォルダ
- 何か関数とかクラスとかを定義したコード(`.py`)
- `setup.py`
- `__init__.py`(アンダースコアは前と後ろ２つずつです)

今回は`mylib`というモジュールを作るとして、以下のように準備しました。

```bash
tree .
```

```bash
setup.py
mylib
|-- __init__.py
|-- mylib.py
```

### ライブラリにしたい関数を書く。

そして、ライブラリの中身になる`mylib.py`に以下のように適当に関数を書きました。

```mylib.py

class Test():
    def __init__(self):
        pass

    def pt():
        print("from class Test")


def test():
    print('hello world')
```


### `__init__.py`  を準備する。

- なんのファイルをインポートするのか
- どの関数をインポートするのか

について数行書くだけです。

```__init__.py
from .mylib import (
    test,
    Test,
)

__version__ = '0.0.0'
```

この場合、
`mylib.py` というファイルから、`test`という関数と
`Test`というクラスをこのライブラリに入れる、というふうに書いているだけです。


### `setup.py` を少し書く。

```setup.py
from setuptools import setup, Extension,find_packages

setup(
    name = 'mylib',
    packages=find_packages(where='mylib'),
    package_dir={'': 'mylib'},
)

```
これで大丈夫です。


### `setup.py`を走らせる。

`python`のライブラリは`シェアドオブジェクトファイル（soファイル）`として
python の環境フォルダに格納されます。場所はhttps://qiita.com/kenmaro/items/d589d32115154dcd26b2この記事を
参照してください。


このフォルダに入れておけば、どこで`python`を実行しても
`import mylib`

とすることができます。そのようにしたい時は、

```
python setup.py install
```

と実行してください。

このフォルダに格納するのではなく、`setup.py`がある階層にsoファイルのフォルダを作りたい時
（このときはこの階層でだけ`import mylib`できます。）
は、

```
python setup.py build_ext --inplace
```

とすることができます。


### オプション（他のライブラリに依存している時）
例えば、自作ライブラリが`tensorflow`を内部で呼んでいるなど、
他のライブラリを使っていることがほとんどだと思います。
その時は、以下の作業が`プラスで`必要です。

- `requirements.txt`を作る。
- `setup.py`を少しだけ変更する。

#### requirements.txt 
`requirements.txt`に

```requirements.txt
tensorflow==2.2
```
というように、必要なライブラリを書いときます。

#### setup.py に追加

```setup.py
from setuptools import setup, Extension,find_packages

def _requires_from_file(filename):
    return open(filename).read().splitlines()


setup(
    name = 'mylib',
    packages=find_packages(where='mylib'),
    package_dir={'': 'mylib'},
    install_requires=_requires_from_file('requirements.txt'),
)

```

このように書きます。
これであとは先ほどと同じように`python setup.py install`などをやってみてください。



### まとめ

今回はできるだけ最低限の手順だけで
pythonの自作モジュールを`setuptools`を使うことで実装すること
を解説してみました。

今回はこの辺で。


おわり。