OmniAICreator/Qiita-1.07M huggingface dataset:
2023/2/1主にITとかセキュリティの記事

コンテナ管理プラットフォーム「Rancher」に深刻な脆弱性
https://www.security-next.com/143222

高度化・巧妙化する企業へのサイバー攻撃　もはや他人事ではない。リスクを知り、今できる備えを(1/30)
https://diamond.jp/articles/-/316109

ランサムウェアに感染したシステムを「身代金を払わずに」15時間で復旧　その“切り札”とは？(1/30)
https://news.yahoo.co.jp/articles/b27a2cf21cdd9d56c8ac459e782236074d7a3f7b?source=rss

米国で病院を標的としたランサムウェア攻撃が増加(1/30)
https://www.carenet.com/news/general/hdn/55752

「ポケモンゲーム」「国税庁です」次々詐称…韓国でハッキング被害 (上)(1/30)
https://www.afpbb.com/articles/-/3448984

「ポケモンゲーム」「国税庁です」次々詐称…韓国でハッキング被害 (下)(1/30)
https://www.afpbb.com/articles/-/3448985

警視庁、サイバー攻撃対応で訓練　身代金ウイルス感染を想定(1/30)
https://www.sanyonews.jp/article/1357101/

中国のハッカー組織、韓国に相次ぐ攻撃、「難度の高い技術ではないが…」(1/30)
https://news.yahoo.co.jp/articles/aa4db0eea35373c193a5360fd0fae8ea9a634d63?source=rss

チェック・ポイント・リサーチ、ロシアのハッカーによるChatGPT悪用の試みを確認(1/30)
https://japan.cnet.com/release/30823753/?ref=rss

ジェネレーティブAIで作られるディープフェイク。ChatGPTなどの｢悪意ある使用｣を防ぐには？(1/30)
https://www.businessinsider.jp/post-264643

2023年、企業ネットワークを進化させる3大要因とは？(1/30)
https://atmarkit.itmedia.co.jp/ait/articles/2301/30/news007.html

【時短タイパ】Windows 11で覚えておきたいショートカットキー11選(1/30)
https://ascii.jp/elem/000/004/122/4122460/?rss

KPMGコンサルティング、「KPMGグローバルテクノロジーレポート2022」（日本語版）を発表(1/30)
https://www.jiji.com/jc/article?k=000000086.000088324&g=prt

恩恵を受けるのは顧客対応部門--先進企業、DXの効果を感じるも人材不足に苦慮(1/30)
https://japan.zdnet.com/article/35199231/

街づくりにおけるWeb3領域の可能性--センサーやブロックチェーン技術が果たす役割(1/30)
https://japan.cnet.com/article/35199190/

日本のIT人材の平均年収の実態、一番稼げる職種は？(1/30)
https://news.mynavi.jp/techplus/article/key-5/

「人を思うは身を思う」みんなの銀行とサイバーリーズンが語る、金融セキュリティの苦労と工夫(1/30)
https://enterprisezine.jp/article/detail/17077

災害・コロナ禍・サイバー攻撃・法令違反……中小企業の約9割が何らかのリスクに恐怖【日本損保協調べ】(1/30)
https://news.yahoo.co.jp/articles/4fb0578f4f508e488669431ab3d84198c428acb9?source=rss

顔認識技術が急速に拡散…「プライバシー侵害」ｖｓ「技術的恩恵」＝韓国(1/30)
https://news.yahoo.co.jp/articles/0eaf4bd4231f1685d2ba25ef94b92be0733f1d76?source=rss

疑われる「C++」の安全性、今後の動きはどうなる(1/30)
https://japan.zdnet.com/article/35199018/

Google Classroom、招待メールを悪用したスパムが問題に(1/30)
https://www.zaikei.co.jp/article/20230130/707502.html

「分散型ID」の実像 アイデンティティー管理を柔軟にする(1/31)
https://xtech.nikkei.com/atcl/nxt/mag/nnw/18/011800154/011800001/

NEWS pickup＆digest（2022/12/9～2023/1/5）
https://xtech.nikkei.com/atcl/nxt/mag/nnw/18/041800011/011800063/

技術の「民主化」で脱・自動車依存(1/31)
https://xtech.nikkei.com/atcl/nxt/mag/nmc/18/00103/00012/

委託事業者経由でランサムウエア被害　外来診療の全面再開に2カ月超(1/31)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600011/012600125/

ショーケースが半年前にソースコード改ざん被害、利用企業のカード流出は10社超に(1/31)
https://xtech.nikkei.com/atcl/nxt/column/18/00598/013000200/

コロナ禍を経てDX全力推進のANA、採用担当が語る航空系IT新卒の醍醐味(1/31)
https://xtech.nikkei.com/atcl/nxt/column/18/02286/012600010/

体制だけでは不十分　成功に「心得」が必須(1/31)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/012400338/012400001/

異質な人材を加えよ　変革始める24の心得(1/31)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/012400338/012400002/

失敗を許容せよ　成果もたらす心得51(1/31)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/012400338/012400003/

年4億日分のデータで健康増進　基幹系刷新の道筋、次期中計で(1/31)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600001/012600128/

深層学習に魅せられ起業　独自のアルゴリズムで勝負(1/31)
https://xtech.nikkei.com/atcl/nxt/mag/nc/18/020600002/012400067/
同じ項目のカウントと、同じ項目の最初と最後のデータをとる2

# はじめに
この記事は、前回の内容をよりシンプルに記述したものになります。

https://qiita.com/tansuke2021/items/fb9cfd0ec1bc70d33ee1

前回はLINQを使用して同じことをしましたが、よりシンプルな書き方を
水科さん（@Mina_Mizushina）が教えてくださいました。
実際に書いて身体で覚えるために記録として残します。

# 結論
https://qiita.com/tansuke2021/items/fb9cfd0ec1bc70d33ee1#comment-f11419c0a5656296f9be

こちらの記事にコメントいただいた内容と同じになりますが、
本記事にも記載します。リンクからコメントまで飛べます。

* 最初の行取得
```
どうぶつひょうdt.Select("動物名 = 'ごりら'").First()
どうぶつひょうdt.Select("動物名 = 'ごりら'").First()("備考").ToString()  // とかで値が取れます

```
* 最後の行取得
```
どうぶつひょうdt.Select("動物名 = 'ごりら'").Last()
どうぶつひょうdt.Select("動物名 = 'ごりら'").Last()("備考").ToString()  // とかで値が取れます
```

* カウント
```
どうぶつひょうdt.Select("動物名 = 'ごりら'").Count
```
# 実際にやってみた
実際にやってみました。
前回の記事をご覧の方には一部くどい説明となりますが、お付き合いください。

こんな表を用意しました。
この中から、
* 最初のゴリラのデータと備考欄の内容
* 最後のゴリラのデータと備考欄の内容
* ゴリラが全部で何頭いるか
を抽出したいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1846796/2b19dda0-acba-0cf3-86a9-1a968d214cfc.png)


## 実装
前回とほとんど見た目は変わっていませんが、こんな実装をしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1846796/579dd9f5-3a91-2e63-83eb-f8cda3aebda6.png)



## うごかした結果
ちゃんと最初と最後のデータ、
それからゴリラが何頭いるかと、それぞれの備考欄の内容が出力できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1846796/a48b1aa8-3a51-af86-55bd-d32f041ebc54.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1846796/be5c3590-1406-1b73-4907-2b9b65506017.png)


# 参考文献
前回の記事の備考でDataRowとDataTableについて少しだけ触れましたが、
それについても水科さん（@Mina_Mizushina）の記事が非常にわかりやすく書いてくださっていたので、参考文献として記載します。

https://qiita.com/Mina_Mizushina/items/ec5140e9230bc52355ef

# おわりに
今回、いただいたコメントのおかげで自分の中での実装の幅が広がったのを感じました。
水科さん（@Mina_Mizushina）、教えてくださりどうもありがとうございました。
コードは最上級のドキュメントになり得るか

# はじめに

https://qiita.com/saetegaljewp/items/a15d769f6553f4ed75d3

という記事を投稿したところ、多くの方からコメントいただきました。
上記事は私のマネジメント失敗談を書いたものですが、私と同じように設計ドキュメントを正とする考えもあれば、コメントやドキュメントよりもコード！という考えもあるとわかりました。

そこで、**果たしてコードが最上級のドキュメントになりえるのか**を考察したいと思います。

## ディスクレーマー

本記事はコメントしてくださった方を批判するものではありません。
誤解のなきようお願いいたします。

# 最上級とは

ここで最上級とは**ドキュメントに取って代わることができる**つまりコードだけでプロダクト活動に包含される概念を示すことができる、という意味とします。

つまり、コードさえあればドキュメント無しでプロダクト活動（プロジェクト）は成立するということです。

# ドキュメントとは

ここでドキュメントとは**DDDの文脈におけるドキュメント**を指すことにします。

* ドキュメントはコードや会話での表現を補うものである
* コードでうまくやれていることをドキュメントでやるべきではない
* ドキュメントは活動に組み込まれていなければならず、最新の状態になる
* 等など

またドキュメントの一部になり得るコメントも、リーダブルコードやプリンシパルオブプログラミングにあるように、**意味のあるコメントを指す**ことにします。

* 自分の考えを記録する
* コードの欠陥を記録する
* 他者への注意や警告
* なぜこのようなコードになっているか（Why）を記録する
* 等など

# 仮定：コードだけがある状態

コードだけが存在する場合を考えてみます。

コードだけが存在し且つコードがプロダクト活動の全てを表現可能なのであれば、確かにドキュメントやコメントは不要です。
例えば、小規模プロダクトや個人開発等でしょうか。

では、それ以外のあらゆる場面においてコードが全てを表現できるのでしょうか。

# コードが全てを表現できる とは

繰り返しになりますが、ドキュメントやコメントの内容がコードに表出するということです。

例えば、会話の中で発せられた微妙なニュアンスであったり、自分の考え、コードの欠陥、注意、Whyです。
そのようなコードを書くとすれば以下のようになります。
言語はGoです（言語は特に問題ではありません）。

```golang
package main

func doSomethingWithQuickSortAlgorithmBecauseXXX() {
    // Do something with the quick sort algorithm
    // because ...
}
```

上記のコードはXXXという理由によってクイックソートを用いて何らかの処理を行う関数を示しています。
つまりコードはWhyを示しているのです。

# このコードは最上級のドキュメントになり得るか

私はこのような関数（関数名に背景を含む）をあまり見たことがありません。書くとしても `func doSomething` でしょう。
そしてなぜそうしたかはコメントに残すでしょう。
複雑な背景がある場合やステークホルダーが多い場合はドキュメントという表現手法を用いるのが適切で、なぜ適切なのかというと**あらゆるステークホルダーが理解可能な自然言語で表現でき、文字以外での表現も可能だから**です。

```golang
package main

// doSomething returns ... 
// This function uses the quick sort algorithm
// because ...
func doSomething() {
    // ...
}
```

上記の例は突拍子もない例ですが、実際は関数名を簡潔に示すことが多いと思います。

またこのコードは**活動の一部しか表現できていません**。活動とは絶え間なく続くプロセスであり、プログラミング言語という一種のフィルタを通した時点で情報はいくらか欠落してしまいます（このコードからプロダクト活動が透けて見えますか？）。

つまり**コードは最上級のドキュメントになり得るごく稀なケースを除いて、通常はコードだけで表現せず、ドキュメントやコメントといった適切な情報伝達手段を用いる**というのが個人的見解です。

# まとめ

当然と言えば当然ですが、状況（プロダクト規模等）によってコードがドキュメント並の表現力を持つ場合もありますが、それ以外の表現手段（ここで言うドキュメントやコメント）を全く用いなくてよいかというとそういうわけではありません。

**誰に対して何を伝えるか**を念頭に、どのような手段であればそれが上手く伝わるかを考えることが大切だと思います。

# 最後に

皆さんのご意見お待ちしています。
【Python】サイコロクラスはどのように実装するのが最適か


読者のあなたならどのような実装を行うかイメージしながら読んでみてほしい。

**条件**
『サイコロを回すクラス』から利用されることを念頭に置く。
このサイコロクラスは、サイコロを渡されると、ランダムに値を選択する処理になっている。

**題**
サイコロクラスはどのように実装を拡張するのが良いか
6面,8面サイコロ...というように作成していくとする。


```python:サイコロを回すクラスの実装例(サイコロクラスは仕様のみ定義)
import random
import abc


class Dice(abc.ABC):
    """
    サイコロの仕様
    """

    __slots__ = ()  # インスタンスの属性追加を不可にする。

    @property
    @abc.abstractmethod
    def numbers(self) -> set:
        ...

class DiceRoller:
    """
    サイコロを回す役割
    ランダムに出目を選択する。
    """
    def __init__(self, dice: Dice) -> None:
        self._dice: Dice = dice

    def roll_dice(self) -> int:
        """
        ランダムに選択する。
        """
        return random.choice(list(self._dice.numbers))

```

# 実装例1 面の数ごとに、愚直に具象クラスを作成する。
抽象クラスの仕様を満たしたサイコロクラスを実装。共通化は行われていない。
この場合、抽象クラスはJavaで言うインターフェイスに近い。

```python
class SixSidedDice(Dice):
    """
    6面ダイス
    """
    __slots__ = "__numbers"

    def __init__(self) -> None:
        self.__numbers = {1, 2, 3, 4, 5, 6}

    @property
    def numbers(self) -> set:
        return self.__numbers


class EightSidedDice(Dice):
    """
    8面ダイス
    """
    __slots__ = "__numbers"

    def __init__(self) -> None:
        self.__numbers = {1, 2, 3, 4, 5, 6, 7, 8}

    @property
    def numbers(self) -> set:
        return self.__numbers
```

# 実装例2 面の数ごとに、テンプレートパターンで共通化しつつ,具象クラスを作成する。
今回の場合、プロパティの実装なんて変わることがないと判断し、
抽象クラスにプロパティの実装をする場合 

```py
class Dice(abc.ABC):
    """
    サイコロの仕様
    """

    __slots__ = "_numbers"  # インスタンスの属性追加を不可にする。

    def __init__(self) -> None:
        self._numbers = None

    @property
    def numbers(self) -> set:
        if self._numbers is None:
            raise NotImplementedError('NotImplementedError')
        return self._numbers


class SixSidedDice(Dice):
    """
    6面ダイス
    """
    __slots__ = "_numbers"

    def __init__(self) -> None:
        super().__init__()
        self._numbers = {1, 2, 3, 4, 5, 6}


class EightSidedDice(Dice):
    """
    8面ダイス
    """
    __slots__ = "_numbers"

    def __init__(self) -> None:
        super().__init__()
        self._numbers = {1, 2, 3, 4, 5, 6, 7, 8}
```

# 実装例3 抽象クラスさえ不要と判断し、コンストラクタで切り替える手法を取る。
サイコロの面の数を変えた程度でクラスを別々に作ること自体が愚かだと
抽象クラスをやめて、インスタンス生成時にセットすればよいと判断した場合

```py
class Dice:
    """
    サイコロの仕様
    """

    __slots__ = "_numbers"  # インスタンスの属性追加を不可にする。

    def __init__(self, numbers: set) -> None:
        self._numbers = numbers

    @property
    def numbers(self) -> set:
        return self._numbers
```

# 筆者の見解
サイコロの面の数の仕様程度で、具象クラスに分けるのは、少しやりすぎかもしれない。今回の場合であれば3を筆者は選ぶ。
ただし、抽象クラス(インターフェイス)で仕様を定めて、具象クラスに実装処理を記載するコーディング手法が強力であることには変わりはない

自身が執筆している
https://zenn.dev/timoneko/books/78460a539d033f
OOPの解説のために、少し仰々しく実装した実装例1を載せているが....
DockerでRedmineを起動した話

# 概要
表題のよりDockerを利用してRedmineをRaspberrypi上で起動できたので知見を共有します。
先見の方の記事がたくさんあるのですが、結構古い記事が多くてそのままだとだいたいエラーとなったので記事にしておきます。

- 追記
Docker駆動のサーバーについてはGitHubに集約しました。
[SustainableEnv](https://github.com/koiusa/SustainableEnv/tree/recommend)

# GitHub Project
https://github.com/koiusa/DockerWithRaspberryPi

# Dockerについて
とりあえず、ちょっとつまったのはaptでとれるものはバージョンが古いようで、docker-composeでredmineのimageをbuildしたときにエラーになったことです。
Dockerの公式リポジトリから最新を取得することで解決しました。
```
 sudo curl -fsSL https://get.docker.com -o get-docker.sh
 sudo sh get-docker.sh
```

ユーザーにdockerグループを追加します
この設定を実行しないとdockerコマンド使用時にsudo記述がいるので何か不都合があるかと思います。
```
 sudo usermod -aG docker $USER
```

# Docker-Composeについて
docker-compose.ymlに使用したいイメージと各種設定を記述します。

こちらもpipで取得できるものはバージョンが古いので、公式のリポジトリから取得しています。
今回の動作環境ではuname -mで取得できるアーキテクチャ名が
armv7lとなるので、最後の一文字を削って無理やりリポジトリのファイル名に合わせています。
もしかしたら、ほかにいい方法あるかもしれません。
```
pi@raspberrypi:~ $ uname -m
armv7l
```

```
function install_docker-compose(){
 VERSION=$(get_setting "docker_compose_env_version")
 VERSION=$(echo $VERSION | sed 's/"//g')
 DISTRIBUTION=$(uname -s)-$(uname -m)
 DISTRIBUTION=${DISTRIBUTION/%?/}
 DOCKER_COMPOSE_DIR=/usr/local/bin/docker-compose

 #Docker-Compose Insrall
 sudo curl -L "https://github.com/docker/compose/releases/download/${VERSION}/docker-compose-${DISTRIBUTION}" -o ${DOCKER_COMPOSE_DIR}
 chmod +x ${DOCKER_COMPOSE_DIR}
}
```

# Redmineについて
参考にしたサイトがPostgreSQLをつかっていたのでそのまま使ってますが、redmineはmysql系の実例の方が多いようです。
イメージファイルは公式からの取得となっています。
自分のプロジェクトでは指定していませんが、バージョン指定したほうが無難化と思います。

https://docs.docker.jp/compose/compose-file/compose-versioning.html

バージョン指定は上記リンクのドキュメントで対応表が記載されています。
```
version: '3.8'
services:
  redmine:
    image: redmine
    container_name: redmine
    ports:
      - 8080:3000
    volumes:
      - ./files:/usr/src/redmine/files
      - ./log:/usr/src/redmine/log
      - ./plugins:/usr/src/redmine/plugins
      - ./public/themes:/usr/src/redmine/public/themes
    environment:
      REDMINE_DB_POSTGRES: redmine-db
      REDMINE_DB_DATABASE: redmine
      REDMINE_DB_USERNAME: redmine
      REDMINE_DB_PASSWORD: redmine
      REDMINE_DB_ENCODING: utf8
    depends_on:
      - redmine-db
    restart: always

  redmine-db:
    image: postgres
    restart: always
    environment:
      POSTGRES_DB: redmine
      POSTGRES_USER: redmine
      POSTGRES_PASSWORD: redmine
    volumes:
      - ./dbdata/:/var/lib/postgresql/data
```

おそらく、こんな感じです
```
image: redmine:[バージョン]
```

# あとがき
この記事もそのうち過去の遺物になってしまうとおもいますが、、、出来ればメンテしていきたいところです
動作環境はRaspberryPiですが、
たぶん、linux debian系ならうごくんちゃうかなとおもいます。
未確認ですが、、、
【Azure】クラウドサービスの種類まとめてみた


## クラウドサービスの種類

クラウドサービスは大きく、IaaS(Infrastructure as a Service)、PaaS(Platform as a Service)、SaaS(Software as a Service)の3種類に分類されます。

これらのサービスには共同責任モデルが適用されます(責任共有モデルは、[サービスとしてのプラットフォームについて説明する](https://learn.microsoft.com/ja-jp/training/modules/describe-cloud-service-types/3-describe-platform-service)の図を参考)。
共有責任モデルとは、クラウドサービス提供者(Azureなど)とクラウドサービスを使用する顧客との間のセキュリティ責任の分割を指します。
どのクラウドサービスにも共通して、プロバイダーは物理ホスト、物理ネットワーク、データセンターを保護する責任があり、顧客はクラウド上のデータ、デバイス、アカウントを保護する必要があります。
しかし、認証やクラウド上のアプリケーション、ネットワーク制御、OSに関しては、クラウドサービスの選択によって責任の対象が変更します。

### IaaS(Infrastructure as a Service)
IaaS(Infrastructure as a Service)は、仮想マシン、ストレージ、ネットワークなどの仮想化されたコンピューティング リソースを提供するクラウドサービスを指します。
顧客は、クラウド上の認証、アプリケーション、ネットワーク制御、OSを管理する必要が出てきます。
IaaSを選択することによるメリットとデメリットには下記のようなものがあります。

#### メリット
- 顧客が独自のOS、ミドルウェア、およびアプリケーションをインストールできるため、やれることの自由度が高い
- 顧客が必要に応じてリソースを簡単に追加または削除できるため、柔軟性とスケーラビリティが担保される
- 従量課金制であるため、初期コストを抑えられる

#### デメリット
- 管理する対象が多いため、技術的専門知識が必要になる
- 保守やアップグレード等の作業を顧客自身で行う必要があるため手間がかかる

AzureでのIaaSのサービスは行かようなものがあります。

|  リソース  |  説明  |
| ---- | ---- |
|  Azure Virtual Machines  |  クラウド上で仮想マシンのデプロイ及び管理をできるようにするサービス  |
|  Azure Storage  |  スケーラブルで耐久性があり、可用性の高いストレージサービス  |
|  Azure Virtual Network  |  クラウド上にプライベートネットワークを作成できるサービス  |
|  Azure Load Balancer  |  トラフィックを複数の仮想マシンに負荷分散し、高可用性を担保するサービス  |


### PaaS(Platform as a Service)
PaaS(Platform as a Service)は、顧客がインフラストラクチャの管理をしなくても、アプリケーションを開発、実行、および管理するためのプラットフォームを提供するクラウドサービスを指します。
顧客は、クラウド上の認証、アプリケーション、ネットワーク制御の一部を管理する必要があります。
PaaSを選択することによるメリットとデメリットには下記のようなものがあります。

#### メリット
- インフラストラクチャの管理を考えずに、アプリケーションの開発、展開、および管理を行える
- プロバイダーがインフラストラクチャの保守とアップグレードを担当するため、運用コストを削減できる

#### デメリット
- 基盤となるインフラストラクチャをあまり制御できないため、高度なカスタマイズができない
- スケーリングとリソース割り当てが限定される可能性がある

AzureでのPaaSのサービスは行かようなものがあります。

|  リソース  |  説明  |
| ---- | ---- |
|  Azure App Service  |  Webやネイティブアプリケーションを構築、デプロイするためのフルマネージドプラットフォームサービス  |
|  Azure Functions  |  イベントドリブンなアプリケーションを構築および実行するためのサーバーレスプラットフォームサービス  |
|  Azure Container Instances  |  クラウド上で作成したコンテナを実行できるサービス  |
|  Azure Database for PostgreSQL  |  Azure上でPostgreSQLを提供するフルマネージドデータベース  |


### SaaS(Software as a Service)
SaaS(Software as a Service)は、プロバイダーが提供するソフトウェアをインターネット経由で利用できるクラウドサービスを指します。
顧客は、クラウド上の認証のみ一部管理する必要があります。
SaaSを選択することによるメリットとデメリットには下記のようなものがあります。

#### メリット
- インターネット環境とWebブラウザのみで簡単にアプリケーションにアクセスできる
- セットアップ、メンテナンス、アップグレードのすべてがプロバイダーに任せられる
- 使用分のみの支払いとなるため、最も費用対効果が高い可能性がある

#### デメリット
- インフラストラクチャ、アプリケーション、およびデータに対する制御の自由度が最も低い
- カスタマイズおよび他のシステムとの統合が制限される可能性が高い
- プロバイダーのサービス停止に伴って、顧客側のサービスも同時に停止してしまう

MicrosoftでのIaaSのサービスは行かようなものがあります。

|  リソース  |  説明  |
| ---- | ---- |
|  Microsoft 365  |  Microsoftのさまざまなサービス(Office365など)をパッケージ化したクラウドサービス  |
|  Dynamics 365  |  CRMサービス  |
|  Power BI  |  ビジネスインテリジェンスとデータの視覚化サービス  |
|  Azure DevOps  |  ソフトウェア開発および配信プラットフォーム  |
streamlitでexcel形式のファイルをダウンロードする方法

# はじめに
Pythonのみで記述できるお手軽webフレームワークであるstreamlitはHTMLやCSSの勉強の必要がなくちょっとしたものを作るには非常に便利です。しかしその反面で標準搭載されている機能も少なく、場合によっては自作する必要があります。
今回、結果を出力する際にexcel形式(.xlsx)を利用しようとしたのですが、少々つまずいたので備忘録として残しておきます。

2023/02/15追記
[@SaitoTsutomo](https://qiita.com/SaitoTsutomu)さんより良い方法を教えていただきました。
記事の下に追記したのでそちらをご参照ください。
ありがとうございます。

# 内容

``` py
from io import BytesIO
import pandas as pd
import streamlit as st

def df_to_xlsx(df):
    byte_xlsx = BytesIO()
    writer_xlsx = pd.ExcelWriter(byte_xlsx, engine="xlsxwriter")
    df.to_excel(writer_xlsx, index=False, sheet_name="Sheet1")
    ##-----必要に応じてexcelのフォーマット等を設定-----##
    workbook = writer_xlsx.book
    worksheet = writer_xlsx.sheets["Sheet1"]
    format1 = workbook.add_format({"num_format": "0.00"})
    worksheet.set_column("A:A", None, format1)
    writer_xlsx.save()
    ##---------------------------------------------##
    workbook = writer_xlsx.book
    out_xlsx = byte_xlsx.getvalue()
    return out_xlsx


df_test = pd.read_excel("test.xlsx")
xlsx_test = df_to_xlsx(df_test)

st.download_button(label="Download", data=xlsx_test, file_name="_test.xlsx")
```
上記のコードを使うことでDataFrameをcsvではなくxlsxの形式でダウンロードすることが可能になります。
xlsxのエンジンとして```xlsxwriter```を利用するのでこちらのライブラリのインストールを忘れないでください。
xlsxを用いる利点としては
・日本語が入っていても文字化けしない
・画像やグラフの挿入ができる
・非プログラマーにとってはcsvより使いやすい
といった点が挙げられます。

特に日本語を含むcsvを扱いたい場合、デフォルトの```st.download_button```では文字化けしてしまいます。
そのため別途ダウンロードリンクを作成する必要があり厄介です。
ダウンロードリンクの例も示しておきます([こちら](https://www.cloudnotes.tech/entry/streamlit_csv_download)から流用)。
```py:csv用のダウンロードリンクの例
import base64
import streamlit as st

csv = df.to_csv(index=False) 
b64 = base64.b64encode(csv.encode('utf-8-sig')).decode()
href = f'<a href="data:application/octet-stream;base64,{b64}" download="result_utf-8-sig.csv">Download Link</a>'
st.markdown(f"CSVファイルのダウンロード(utf-8 BOM):  {href}", unsafe_allow_html=True)
```

またアプリ画面の見栄えも標準搭載されている```st.download_button```の方が綺麗だと思います(好みですが...)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/449785/4c276209-75d3-6822-357d-d9cc79242ffa.png)

# 最後に
streamlitでDataFrameをxlsx形式で出力する方法について記しました。
webフレームワークとしてはほかにFastAPIやDjangoなどもあるので、必要に応じて使い分けたいと思います。

# 2023/02/15追記
[@SaitoTsutomo](https://qiita.com/SaitoTsutomu)さんに以下の方法を教えていただきました。
```python
from io import BytesIO
import pandas as pd
import streamlit as st

df = pd.DataFrame(["テスト"], columns=["列A"])

df.to_excel(buf := BytesIO(), index=False)
st.download_button(
    "Download",
    buf.getvalue(),
    "sample.xlsx",
    "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
)
```
こちらの方がコンパクトに書けるので良いと思います。
【備忘録】VPNとプロキシは何が違うのか？

# はじめに
　最近、IT系の漫画にハマっていて、「海外のVPNを経由する」、「プロキシサーバを経由する」という文言を見た際に、VPNとプロキシサーバって役割似てないか？と思ったので、さらっと違いを調べてみました。
　本記事では、違いに焦点を当てているので、それぞれの詳細は引用元を参照していただければ幸いです。

# 本題
　さて、ここからVPNとプロキシサーバの違いを解説していきます。
### VPNは通信を暗号化する
　これが最も大きな違いです。アクセス元のIPアドレスを隠すのは両者に共通していますが、VPNは通信そのものが暗号化されているため、通信内容が盗み見られる可能性は比較的低いです。雑に言えば、プロキシサーバはIPアドレスを偽装するだけということになります。

### VPNはログを残さない
　プロキシサーバにはVPNに比べ、無料のサービスが多く存在しています。そのため、利益を上げるには、サーバ内のログを情報として第三者に販売する、あるいは政府に提供するということが行われています。一方でVPNはそもそもログが残りません。その分有料なサービスが多いですが、安全性を考慮すればVPNを選ぶのが無難でしょう。

### VPNの方がより広い範囲をカバーしている
　VPNの場合、OSレベルで通信を保護します。つまり一度接続してしまえば、それ以降のインターネット上の活動は、ブラウジングもバックグラウンドで動いているアプリも全て暗号化されたトンネルの中で行われます。
　一方プロキシサーバの場合、アプリケーションレベルの保護になるので、プロキシを指定したアプリあるいはwebサイト上でのやりとりのみ、限定的に保護します。

# まとめ
　VPNとプロキシサーバはアクセス元のIPアドレスを隠すという点においては、似たような役割をしますが、暗号化やログを残さないといった点で、安全性の面ではVPNに軍配が上がります。
　基本的に有料のVPNサービスを利用すれば、無料のプロキシサーバを使う必要はありませんが、一時的にIPアドレスを変えたい場合等は無料のプロキシサーバを使うのも考慮しても良いかもしれません。

# 終わりに
　自分のアウトプット用に記事にしてみましたが、訂正等あればコメントいただけると幸いです。

以下、引用元
[What's the Difference Between a Proxy and a VPN?](https://www.security.org/vpn/proxy-vs-vpn/#:~:text=Key%20Differences&text=VPNs%20hide%20not%20only%20your,t%20encrypt%20your%20online%20activities.)
[Proxy vs VPN: what are the main differences?](https://nordvpn.com/ja/blog/vpn-vs-proxy/)
[Proxy vs. VPN: 4 differences you should know](https://us.norton.com/blog/privacy/proxy-vs-vpn)
日本語化　i18n

# １．はじめに
- 日本語化対応の手順をメモ

# ２．日本語化実装
### ２−１．i18nのプラグインを確認
```
$ bundle list
```
- 一覧を確認して`i18n (1.12.0)`などの記載があればOK
- ない場合は、`Gemfile`に下記を記載
```:Gemfile
gem 'rails-i18n'
```
その後
```
$ bundle install
```

### ２−２．config/application.rbに下記を追記
```:config/application.rb
config.i18n.default_locale = :ja
```

### ２−３．翻訳用ファイルをgitHubからダウンロード
https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml

ダウンロード後`./config/locale/`の配下に入れる。

### ２−４．日本語訳をja.ymlに追加する
```:./config/locale/ja.yml
ja:
  activerecord:
    attributes:
      user:
        name: '名前'
      post:
        location: '旅行先'
        text: '感想'
```

# ３.deviceの日本語化実装

### ３−１．device翻訳用ファイルをgitHubからダウンロード

[devise.ja.yml](https://gist.github.com/yhara/606476)

ダウンロード後`./config/locale/`の配下に入れる。

# ４．おわりに
以上で日本語化の実装ができます
キャッシュフロー

# キャッシュフローとは？
実際のお金の流れを意味する。
その中にキャッシュイン、キャッシュアウトがある。
### キャッシュイン
一定期間内に企業に流入するお金のこと
### キャッシュアウト
一定期間内に企業から流出するお金のこと

# キャッシュフロー計算書(C/F)
経営成績を現金や預金の増減で明らかにする書類のこと。
その中には3つの種類のキャッシュフローがある。

### 営業活動によるキャッシュフロー
```仕入れや生産、販売などの日常的な事業活動によって発生```するお金の流れ

### 投資活動によるキャッシュフロー
```設備投資や有価証券に伴う```お金の流れのこと

### 財務活動によるキャッシュフロー
```借入や増資などに伴う```お金の流れのこと

# 投資の採算性の判断
キャッシュフローに基づく投資回収期間や```正味現在価値(NPV: Net Present Value)```によって判断する。

# 感想
これも覚えいておこう。
しかし大事なのは問題文を読んで理解することができれば解ける。
「Glitch Sync From GitHub」を使ってみた（mainにPushでGlitchに自動更新する方法）

Node.jsを使ってWebアプリを作成しており、デプロイ先に [Glitch](https://glitch.com/) を活用しています。本記事では、GitHubの更新が即座にGlitchに反映されるようにする方法をまとめます。

## 内容


- GitHubにSecretを登録する
- GitHub Actionsを活用する

### 必要な PROJECT_ID と AUTH_TOKEN を取得し、GitHub に Secret を登録する

https://github.com/kanadgupta/glitch-sync#inputs

にある通りです。

まずはGlitch にアクセスし、「Ctrl＋Shift＋I」で開発者ツールを有効にしてください。

その後、「Network」タブを開いてください
※「Preserve log」と「Disable cache」はチェックを入れなくても大丈夫です
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1630025/3fd1a7e9-e3e2-c53a-799b-fabdd067c95f.png)


下にある「Tools > Import/Export」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1630025/6f8fcbfe-329f-7b25-4f4d-ff6061d066e9.png)

「Import from GitHub」を選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1630025/3acaab9f-c323-1dd1-323c-fc34f0d1966b.png)


ここに「username/repository:path」形式で入力して「OK」を押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1630025/0a9a63cb-c437-d0af-f95f-41a9a544a7a5.png)

「githubImport?projectId=XXX&repo=XXX」となっている部分を探す
- projectId = PROJECT_ID
- authorization = AUTH_TOKEN


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1630025/f1450877-74f1-fd23-91f8-9b0572b228bb.png)

それらを GitHub の Secret に登録する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1630025/6b7b128c-e0f8-69b1-8905-27341a0834c8.png)



### GitHub Actions Workflow

あとは以下のコードを書くだけで、mainブランチにPushした時に自動でGlitchも更新されるようになる

``` yaml:.github/workflows/deploy-to-glitch.yaml
name: Deploy To Glitch
on:
  push:
    branches:
      - main
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy To Glitch
        uses: kanadgupta/glitch-sync@main
        with:
          project-id: '${{ secrets.PROJECT_ID }}'
          auth-token: '${{ secrets.AUTH_TOKEN }}'
```

## 参考資料

https://github.com/kanadgupta/glitch-sync

https://docs.github.com/en/actions/security-guides/encrypted-secrets

※以下を参考にしましたが、その通りやっても出来ないので注意が必要です。

https://neos21.net/blog/2020/08/23-01.html
BIND 9のゾーンファイルとリソースレコードの主なディレクティブ

●BIND 9のゾーンファイルの主なディレクティブ
ディレクティブ　書式
・$ORIGIN　＄ORIGIN　ドメイン名　補完するドメイン名、ホスト名のみ（ピリオドで終わってない）で記述された名前に追加される。
・$TTL　$TTL　秒　デフォルトTTLを0-2147483647の範囲で指定する。

●リソースレコードに使用する主なリソースタイプ
・SOA(Start of Authority)　ゾーンに関する情報を記述する
・NS　ゾーンの情報を管理するサーバ名を記述する
・MX　ゾーンで管理するドメインあてメールの宛先メールサーバ名を記述する。優先度は0-65535の範囲で指定し、優先的に使ってほしいサーバに対して低い値を設定する。
・A｜AAAA　ゾーン内の名前に対応するIPアドレスを記述する。異なる名前を同じIPアドレスに割り当てることも、同じ名前を異なるIPアドレスに割り当てることも可能。
・CNAME　ある名前に対する別名を記述する。
・PTR　IPアドレスに対応する名前(FQDN)を記述する。主に逆引きゾーンの定義に使用する。

※ゾーンファイルの記述には以下の省略表記ができる。
・名前が「.（ピリオド）」で終わらないものはFQDNではないとみなし、「$ORIGIN」またはゾーンのドメイン名が自動で追加される
・「＠」はゾーンの管理するドメイン名が指定されたとみなす（SOA、NS、MXレコードで使用）
※上記の「＠」の扱いのため、SOAレコードに記述する管理者のメールアドレスは「＠」をピリオドに置き換えて記述
初めてのKubernetes

■はじめに
•Kubernetesとは、製品名が長く先頭のkと末尾のsの間に8文字あるため「k8s」と略す。
•Kubernetes自身にはコンテナを実行する機能はありません。

◼︎Kubernetesの基本機能
1.コンテナ実行環境をマニフェスト(テキストファイル)で管理可能
2.コンテナのスケジューリング/オートヒーリング
　→どのサーバでコンテナを起動するかの制御(=スケジューリング)
　→ノードやコンテナ、アプリ故障時にコンテナを再起動や再作成して自動復旧
3.コンテナのリソース管理
　→CPUやメモリ使用率に応じてコンテナのスケールイン/アウトが可能(手動も自動も可)
4.ロードバランシングとサービスディスカバリ
　→コンテナへの通信をロードバランシング可能
　→どのコンテナがどこで動いているかを知るサービスディスカバリ機能
5.基盤リソースの抽象化
　→コンテナ間ネットワークの仮想化
　→コンテナのボリューム(永続化領域)の抽象化

Kubernetesは管理下の複数のサーバをクラスタリング(まとめて管理)します。
〇Master…主にAPIを提供したり、サーバやコンテナを管理するコンポーネントが起動。
　　　　 本資料では「Master」または「Master Node」と呼びます。
〇Node…主にユーザが作成したコンテナが起動。
　　　 「Worker」または「Worker Node」と呼ぶ。
■Kubernetes上のコンテナのネットワーク
Kubernetes上のコンテナ間のNWは仮想化されており、コンテナが起動しているサーバに関係なく、論理的にコンテナ同士は直接通信可能なネットワークに接続する。(＝オーバーレイNW)
コンテナにはオーバーレイNW上のプライベートIPが付与され、通常では外部のサーバから直接アクセスできません。



～k8sのコマンドを打ってみよう～(´ω｀)カタカタ
Kubernetesの操作には「kubectl」コマンドを使用。（言い方は、キューブコントロール）
✌コマンド実行は大きく２つ✌
①kubectlコマンドの引数を駆使して操作する方法。主に簡単な操作に使用。
②kubectlにマニフェストと呼ばれるテキストファイルを読み込ませて実行する方法。
 複雑なコンテナやネットワークの設定をKubernetesに投入する場合に使用。

■Kubernetesの状態確認
→下記コマンドでKubernetesが正常に動作していることを確認する。

# kubectl get node -o wide
☝-o wide はノードのIPやカーネル等の詳細情報を表示するためのオプションである。

■Hello World -Apacheの起動してみよう
→Kubernetes上で、Apacheコンテナ(Webサーバ)を起動しアクセスしてみましょう。
 マニフェストを投入するには「kubectlapply-fファイル名」を使用。
# kubectl apply -f/root/k8s/hello-world/hello-world.yaml
-f…filenameの略。

■Apacheコンテナの起動を確認
→コンテナができていることを確認する。
# kubectl get pod
NAME                  READY   STATUS    RESTARTS   AGE

# kubectl get service
NAME            EXTERNAL-IP    PORT(S)        AGE
kubernetes      10.46.0.7      493/TCP        apache
apache 　　　   172.19.255.2   80:31419/TCP   4m54s# 
apacheのアクセスIPアドレス、PORTは80となる。

# kubectl get pod -o wide
→-owideオプションを付けるとより詳細な情報が表示される。

#kubectl get service -o yaml
→マニュフェストファイルを全て表示。

#kubectl get service apache-o yaml
→hello-worldで投入した「apache」という設定だけを対象に表示することもできる。

#kubectldescribe pod apache～
→「kubectldescribe～」で各設定の詳細情報や状態を確認することができる。

＜削除のコマンド＞
kubectl delete -f　場所
※設定を全て削除することができる。
全て削除できているか確認する。
Terraformを開始するシンプルな手順メモ

# はじめに
今回は、Terraformを開始する手順をシンプルに確認していきます。

:::note info
手順のメモのため、ベストプラクティスや仔細は、公式ドキュメント等をご確認よろしくお願いいたします。
:::

https://developer.hashicorp.com/terraform/tutorials/aws-get-started?ajs_aid=faebd7bf-0c00-48ad-98d7-6cb0dafaee34&product_intent=terraform

# 手順
実際に実行する流れを記載します。


1. tfenvをインストール
1. プロジェクトフォルダを作成
1. 使用バージョンを決めてインストール
1. 使用バージョンを指定
1. 初期化
1. Terraformファイルの作成
1. コードの記述
1. リソース作成前に確認
1. リソースをデプロイする
1. リソースを削除する

:::note info
AWSであれば、AWSアカウント及び認証情報の設定は必要です。
[こちら](https://qiita.com/kosments/items/ca7cd0041dcf37327731)の手順を参考にしてください。
:::

https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-quickstart.html

## tfenvをインストール

```
$ brew install tfenv
```
https://github.com/tfutils/tfenv

## プロジェクトフォルダを作成

```
$ touch Terraformtest
$ cd Terraformtest
$ code .
```
codeコマンドは、エディタにVScodeを使っている場合に使用できる便利な機能。

https://code.visualstudio.com/docs/setup/mac

## 使用バージョンを決めてインストール

```
$ tfenv list-remote
$ tfenv install 0.13.6 
```
M1macはエラーが出るので以下を入力
```
$ TFENV_ARCH=amd64 tfenv install 0.13.6
```

## 使用バージョンを指定

```
$ tfenv use 0.13.6
$ terraform --version
```

## Terraformファイルの作成、必要なコードの記述

```
touch hogehoge.tf
```

上記ファイルに、試しに以下を記載

```
# providerを指定
provider "aws" {
  region = "eu-west-1"
}

# 作成するVPCを定義
resource "aws_vpc" "this" {
  cidr_block = "10.0.0.0/16"
  tags = {
    "Name" = "sample_vpc"
  }
}
```

## 初期化

```
$ terraform init
```
初期化が走り、指定したproviderに応じた必要なファイルが生成される。

https://developer.hashicorp.com/terraform/cli/commands/init

## リソース作成前に確認

```
$ terraform plan
```
リソースを実際に作成(terraform apply)する前に、実行した場合の作成内容を確認できる。

https://developer.hashicorp.com/terraform/cli/commands/plan

## リソースをデプロイする

```
$　terraform apply
```
実際に定義したリソースが作成されます。
AWSのマネジメントコンソールを開き、リソースが作成されていることを確認します。

https://developer.hashicorp.com/terraform/cli/commands/apply

## リソースを削除する

```
$ terraform destroy
```
作成したリソースが綺麗に削除されます。

https://developer.hashicorp.com/terraform/cli/commands/destroy

# おわりに
モジュール、Code Structureといった考え方にも取り組んでいきたいです！
Puppeteerで自動入力を図ろうとしたときのTips

## はじめに

郵便番号, 都道府県, 住所, 建物名, 氏名, フリガナ, メールアドレス, 生年月日...など一通りのユーザー情報を入力するフォームの動作確認で、大量の `input` を何度も入力するシーンが出てきます。

手入力で対処しようとすると **動作確認NGを繰り返すたびに心が折れます。**
そこで、Puppeteerのみを含めたNode.jsの簡単なプロジェクトを立ち上げて、自動入力するツールを作り突破しようとしました。

同様なソリューションとしてCypressも考えられますが、Puppeteerは自身とローカルにインストールしたChromeで完結するためこちらを採用しました。

この記事ではフォームの自動入力でPuppeteerを用いた際に役立ったTipsを紹介します。
自動入力の対象はjQuery + JavaScriptのプロジェクトを想定しています。


## インストール・実行

```console
$ npm install puppeteer-core
```

https://github.com/YusukeIwaki/ggrks-puppeteer-js などを参考にプロジェクトに合ったPuppeteerのテストケースを作成し、次のコマンドで自動入力を実行します。これでローカルにインストールされたChromeを使うことができます。
(macOSの場合。WSL2では追加の設定が必要になります)

```
$ node puppeteer-test.js
```

## input type=number に入力する

テキストと同様に `page.type` を使いますが、 **引数はStringしか受け付けません。**

```js:type=numberを入力する例
await page.type('input', '333');
```

> ~/work/puppeteer/node_modules/puppeteer-core/lib/cjs/puppeteer/common/Input.js:222
>         for (const char of text) {
>                            ^
> 
> TypeError: text is not iterable

## セレクタにdata属性を指定する

jQueryと違って **クオーテーションで囲まないと認識しません。**

> Error: Evaluation failed: DOMException: Failed to execute 'querySelector' on 'Document': 'input[data-sample=some]' is not a valid selector.

```js:data属性を指定した例
await page.type('input[data-sample="some"]', 'hoge');
```

## jQueryで生成したモーダル内のinputに値を入力する

`waitForSelector` では反応しないので、モーダル表示のイベントを発火させてから `waitFor` とかで2秒ほど待って入力する必要があります。

## 入力しても onChange イベントが発火しない

`page.type`, `page.$eval` の両方をそのまま使っても発火しないので、eval内で明示的にeventを呼びます。

```js:changeイベントを呼び出す例
await page.$eval('input#id-selector', (element, count) => { 
    element.value = 2;
    const event = new Event('change');
    element.dispatchEvent(event);
}, count);
```

https://stackoverflow.com/questions/51143819/puppeteer-invoking-onchange-event-handler-not-working

## input type=dateに入力する

キーボードの矢印キーで年月日を移動させ、数値を入力する発想で対処します。

```js
const cssid = "input#testdate";

const y = "2019";
const m = "05";
const d = "06";

// まず年をtypeして…
await page.type(cssid, y);
// ここがキモ。右矢印押すことで、月の欄に移動します。
await page.keyboard.press('ArrowRight');
// 次は月
await page.type(cssid, m);
// 日の欄に移動します。
await page.keyboard.press('ArrowRight');
// 最後に日
await page.type(cssid, d);
```

https://qiita.com/cranpun/items/da3cfec169be4cb301fa


## evalに変数を突っ込みたい

$evalの中には外部からの変数は認識してくれません。

```js:誤
const count = 0;
await page.$eval('.selector', element => element.value=count);
```

> ~/work/puppeteer/node_modules/puppeteer-core/lib/cjs/puppeteer/common/ExecutionContext.js:221
>             throw new Error('Evaluation failed: ' + helper_js_1.helper.getExceptionMessage(exceptionDetails));
>                   ^
> 
> Error: Evaluation failed: ReferenceError: count is not defined

アロー関数でvalueを入れてから、 $evalの末尾に変数を指定します。

```js:正
await page.$eval('.selector', (element, value) => { element.value = value }, count);
```

https://stackoverflow.com/questions/50657830/passing-a-thunk-to-puppeteers-eval
部分文字列による InfluxDB Flux クエリグループ

列名のプレフィックスでグループ化しようとしているので、次のことを試すことができます。

1. プレフィックスに基づいて新しい列を作成します
2. 新しい列でグループ化

サンプルコードは次のとおりです：

from(bucket: v.bucket)
   |> range(start: v.timeRangeStart, stop:v.timeRangeStop)
   |> filter(fn: (r) => r._measurement == "Processors")
   |> map(fn: (r) => ({r with newProc: strings.substring(v: r._value, start: 0, end: 7)}))
   |> group(columns: ["newProc"])
   |> aggregateWindow(every: v.windowPeriod, fn: mean)  

注意: |> aggregateWindow(column: "_value", every: v.windowPeriod, fn: mean) この方法で列を指定できます。これを試す前に、この行をコメントアウトして、集計前の結果、特に列名を確認してください。

map 関数と部分文字列関数、aggregateWindow 関数の詳細を参照してください。
Notion APIのタイトル取得の癖を解消する

こんにちは。
Notionの案件でNotionのタイトル周りでの癖に悩まされた話と、解決策を書かせていただきます。

## Notion APIでページ一覧を取得

Notion APIでは以下のようにしてページ一覧を取得できます。
取得できるのは、インテグレーションをコネクトしたページ、およびインテグレーションをコネクトしたデータベースにぶら下がっているページです。
実際に使用したコードを適当にバラしただけですので、見にくい上に実際に動くかどうか分かりませんが、ご愛嬌で笑ってご容赦いただければと思います。

```javascript
 const token="";
 const NOTION_VERSION="";

 _post(endPoint, payload) {
    const options = {
      method: 'post',
      headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer ' + token,
          'Notion-Version': NOTION_VERSION,},
    }

    if (payload)
      options.payload = JSON.stringify(payload);

    const resp = UrlFetchApp.fetch(endPoint, options)
    const result = JSON.parse(resp.getContentText());
    return result;
  }

  search(filter = null, start_cursor = undefined) {
    if (!filter) filter = {};
    filter.start_cursor = start_cursor; //undefinedはAPIが許容する

    let result = this._post(`https://api.notion.com/v1/search`, filter);

    //続きがあれば取得
    if (result.has_more) {
      return [...result.results, ...this.search(filter, result.next_cursor)];
    }

    return result.results;
  }
```


## Notion APIの癖

上記のようにしてページ一覧を取得できるのはいいのですが、ここから難がありました。
タイトルが、、、

Notion APIのドキュメントより引用です。propertiesに関する記述です。

>If parent.type is "page_id" or "workspace", then the only valid key is title.
>If parent.type is "database_id", then the keys and values of this field are determined by the >properties of the database this page belongs to.
https://developers.notion.com/reference/page

これによると、親のタイプがpage_idもしくはworkspaceの場合のみ、確実にtitle属性がつくそうです。
しかし親のタイプがdatabase_idなら、そのデータベースに依存するとあります。

始め私のテスト環境では、親がデータベースの場合取得したページオブジェクトの「名前」属性で取れてたんですね。ですがそれが問題で、たまたま私のデータベースのタイトル部分が「名前」になっていただけで、そうでない場合もあるわけです。

そこで解決策として、propertiesオブジェクト全てを走査し、type属性がtitleのものを抽出する必要があるわけです。具体的なコードは以下になります。


```javascript
  /**
   * アイテムからタイトルを取得
   */
  function getTitle(item) {
      if (item.object == "database")
        return item.title[0].plain_text;
      for (let prp of Object.values(item.properties)) {
        if (prp.type == "title") {
          return prp.title[0].plain_text;
        }
      }
  }
```

うーむ、なんと。初めからtitle属性付けておいてくれたら良いのにと思います。
ちなみにtitleがなぜ配列になっているのか、いまだに理解できていません。
一つのページまたはデータベースで、複数のタイトルが付く事ってあるのでしょうか。
詳しい方、コメントで教えていただけましたらこれ幸いです。
jackc/pgxでクエリー実行時にconn busyと出る場合

クエリーを複数回実行する場合は、一度も `Scan()` せず次のクエリーを実行しようとすると下記のエラーが出ます。

> conn busy

```go:誤
err = conn.QueryRow(context.Background(), "INSERT INTO users(screen_name) VALUES ('go');")
err = conn.QueryRow(context.Background(), "INSERT INTO users(screen_name) VALUES ('mod');") // error!
rows, err := conn.Query(context.Background(), "INSERT INTO users(screen_name) VALUES ('tidy');") // error!
```

次のクエリー実行前に必ず `Scan()` するか、 `INSERT`, `UPDATE` など値を取得する必要がないクエリーには `Query()` (`QueryRow()`) の代わりに `Exec()` を使います。

```go:Exec()を使った例
cmd, err := conn.Exec(context.Background(), "INSERT INTO users(screen_name) VALUES ('tidy');")
```

(単体テストなどで)クエリー実行後のエラー処理をすり抜けて次のクエリーが実行されてしまった場合もこのエラーが出ます。

conn busyと出た場合の対処方法として「`conn.Release()` が実行されているか確認する」というのもありましたが、慣れていないのでハマりました。

## 参考

"conn busy" error, help please [SOLVED] ;) · Issue #1124 · jackc/pgx · GitHub
https://github.com/jackc/pgx/issues/1124#issuecomment-990527007
Apacheが実行されていることを監視（CSH)

# 概要
今回はCSHでApacheが起動されているかどうか監視するスクリプトを作成したいと思います。

# 事前作業
以下のパッケージをRASPBERRY（LINUX)にインストールが必要です。

■パッケージ
・cshインストールコマンド
sudo apt-get install csh -y

・APACHEインストールコマンド
sudo apt install apache2 -y

■Apacheの起動コマンド
以下のコマンドで起動および停止ができます。
sudo apachectl stop
sudo apachectl start
sudo apachectl restart
sudo systemctl start apache2
sudo systemctl stop apache2
sudo systemctl restart apache2

■Apacheのプロセスを確認コマンド
ps aux|grep apache|grep root
※grep apacheまたはgrep httpdで確認します。

# CSHソース
下記のソースをrun.shに保存します。

~~~bash
#!/bin/csh -f
# APACHEの起動されている内容をPSコマンドから確認します。
set PSRUN=`ps -ef | grep apache|grep root > ./PS`

# PS結果からAPACHEの文字列を取得するための処理です。
set PSX = `cat ./PS | awk 'BEGIN{FS="sbin/"}{print $2}'|awk 'BEGIN{FS=" -k"}{print $1}'`

# PSの内容を出力します。起動されていない場合は、非表示になります。
cat ./PS

# 作業ファイルを削除します。
rm -rf ./PS

# apache2の文字列の比較です。OSによってhttpdの場合もあります。
if ( "$PSX" == "apache2" ) then
echo "APACHEが起動されている。"
endif

if ( "$PSX" == "" ) then
echo "APACHEが起動されていない。"
sudo systemctl start apache2
endif
~~~~

# 実行結果
上記のCSHを以下のコマンドから実行します。

~~~bash
# `Apacheが軌道中の場合、表示されるメッセージ
pi@raspberrypi:~/work/php $ csh run.sh
root      2700     1  0 23:30 ?        00:00:00 /usr/sbin/apache2 -k start
APACHEが起動されている。

# Apacheの停止コマンドを実行
pi@raspberrypi:~/work/php $ sudo systemctl stop apache2

# Apacheが起動されていない
pi@raspberrypi:~/work/php $ ps aux |grep apache
pi        3002  0.0  0.1   5972   568 pts/1    S+   23:43   0:00 grep --color=auto apache

# Apacheが起動されていない場合、表示されるメッセージ
pi@raspberrypi:~/work/php $ csh run.sh
APACHEが起動されていない。
※この後で「sudo systemctl start apache2」コマンドで起動される

# Apacheの起動されていることを確認コマンド
pi@raspberrypi:~/work/php $ ps aux|grep apache
root      3025  0.5  3.4 192112 15128 ?        Ss   23:46   0:00 /usr/sbin/apache2 -k start
www-data  3029  0.0  1.0 192144  4680 ?        S    23:46   0:00 /usr/sbin/apache2 -k start
www-data  3030  0.0  1.0 192144  4680 ?        S    23:46   0:00 /usr/sbin/apache2 -k start
www-data  3031  0.0  1.0 192144  4680 ?        S    23:46   0:00 /usr/sbin/apache2 -k start
www-data  3032  0.0  1.0 192144  4680 ?        S    23:46   0:00 /usr/sbin/apache2 -k start
www-data  3033  0.0  1.0 192144  4680 ?        S    23:46   0:00 /usr/sbin/apache2 -k start
pi        3035  0.0  0.1   5972   592 pts/1    S+   23:46   0:00 grep --color=auto apache
~~~

# 終わりに
今回Apacheが起動されているか監視するスクリプトを作成した理由としてはApacheが停止されている場合、自動で起動するためのものです。
もしApacheの設定ファイルの問題がある場合は、正常に起動されない場合もあります。
下記のスクリプトですが、変数に設定してそのまま利用すればよいですが、
コンソールで上手く動作していなかったので、ファイル出力にしました。
set PSRUN=`ps -ef | grep apache|grep root > ./PS`
変数に設定したときのエラーメッセージは次のメッセージが表示され処理が中断されました。
^^;;


~~~bash
pi@raspberrypi:~/work/php $ csh run.sh
echo: No match.
~~~


以上です。　最後まで読んで頂いてありがとうございます。
【Linux】古くなったディレクトリやファイルを削除する方法

## ファイルの削除
以下のように`find`コマンドを使用して一定の期間が経過したファイルを削除します。

```zsh
find /path/to/dir -name '*.log' -mtime +30 -delete
```

上記を実行すると対象ディレクトリの`.log`ファイルのうち最終変更日から30日経過しているファイルを削除します。

`-mtime`の部分を変更することで期間の起算日を変更できます。
使用可能なオプションは以下のとおりです。


| オプション|説明|
|:-----------:|:------------|
| -mmin|最終変更日時から（分）|
| -mtime|最終変更日時から（日）|
| -amin|最終アクセス日時から（分）|
| -atime|最終アクセス日時から（日）|
| -cmin|データとステータスの最終変更日時から（分）|
| -ctime|データとステータスの最終変更日時から（日）|


また数字の前の`+`の部分には`-`を指定したり指定しないことができます。

| オプション|説明|
|:-----------:|:------------|
| + |過去から指定した数字までに|
| - |現在から指定した数字までに|
| 指定なし |指定した数字前に|

- 30日前までに変更された

```zsh
-mtime +30
```

- 現在から30分前までに変更された

```zsh
-mmin -30
```

- 30日前にアクセスされた
```zsh
-atime 30
```

## ディレクトリの削除
上記の方法では空ではないディレクトリを削除することができません。
中身のあるディレクトリを削除するには以下のように実行します。

```zsh
find /path/to/dir -type d -mtime +30 | xargs rm -fr
```

上記を実行すると最終変更日から30日経過しているディレクトリを削除できます。
PowerShell で音を出したい（ビープ音・WAV ファイル・TTS）

長時間動作するような PowerShell スクリプトを書く必要がある場合、終了時に音による通知がほしくなります。本記事では、PowerShell 標準の機能で利用できる 3 種類のアプローチをご紹介します。

## ビープ音を鳴らす

まずは古式ゆかしいビープ音です。[Console クラス](https://learn.microsoft.com/ja-jp/dotnet/api/system.console?view=net-7.0)の [Beep メソッド](https://learn.microsoft.com/ja-jp/dotnet/api/system.console.beep?view=net-7.0)を利用します。

```powershell
# デフォルト設定でビープ音を鳴らす
[Console]::Beep()

# 指定の周波数、再生時間でビープ音鳴らす
[Console]::Beep(524, 1000)
```

## WAV ファイルを再生する

せっかくなのでお好みの音楽で通知してほしい場合もあるでしょう。そんな時は [SoundPlayer クラス](https://learn.microsoft.com/ja-jp/dotnet/api/system.media.soundplayer?view=dotnet-plat-ext-7.0)を利用します。

```powershell
# 同期的に再生
(New-Object Media.SoundPlayer <WAV ファイルへのパス>).PlaySync()
```

非同期的に再生・停止したい場合は `SoundPlayer` クラスのインスタンスを変数に格納しておき、`SoundPlayer#Stop()` で停止します。

```powershell
# 非同期的に再生
$sp = New-Object Media.SoundPlayer <WAV ファイルへのパス>
$sp.Play()

# 停止
$sp.Stop()
```

## TTS で日本語を喋らせる

ビープ音や音楽ファイルでなく、任意のメッセージを発話させる（Text-to-Speech）こともできます。PowerShell から簡単に呼び出せる TTS には 2 種類あります。

[Microsoft Speech API 5.4 (COM) の ISpVoice::Speak](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee125024(v=vs.85))：

```powershell
(New-Object -ComObject SAPI.SpVoice).Speak("喋らせたい内容")
```

[.NET Framework 3.0 - 4.8.1 の SpeechSynthesizer クラス](https://learn.microsoft.com/ja-jp/dotnet/api/system.speech.synthesis.speechsynthesizer)：

```powershell
# 同期的に再生
(New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak("喋らせたい内容")

# 非同期的に再生
$ss = New-Object System.Speech.Synthesis.SpeechSynthesizer
$ss.SpeakAsync("looooooooooooooooooooooooooong text")

# 停止
$ss.SpeakAsyncCancelAll()
```

## 参考リンク

* [Console.Beep メソッド](https://learn.microsoft.com/ja-jp/dotnet/api/system.console.beep?view=net-7.0)
* [PowerShellでビープ音の「ドレミの歌」を奏でてみよう](https://tech.guitarrapc.com/entry/2013/02/05/000226)
* [SoundPlayer クラス](https://learn.microsoft.com/ja-jp/dotnet/api/system.media.soundplayer?view=dotnet-plat-ext-7.0)
* [ISpVoice::Speak](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee125024(v=vs.85))
* [Windows 10は好きな文章を合成音声で簡単に喋らせることができる](https://ascii.jp/elem/000/004/055/4055975/)
* [SpeechSynthesizer クラス](https://learn.microsoft.com/ja-jp/dotnet/api/system.speech.synthesis.speechsynthesizer)
DDD初心者でも仲良くなりたい ~学ぶメリット - エンティティ編~

## はじめに
ドメイン駆動設計入門を読んでみて、DDDを学ぶメリットやそれぞれのパターンについてまとめていく。
## ドメインオブジェクトを定義するメリット
「コードのドキュメント性が高まり、ドメインにける変更をコードに伝えやすくする」
ソフトウェアを作る製造過程よりも、その後の保守開発に効果を発揮する。
## ざっくり噛み砕いてみる
Q.開発者がプロジェクトに途中で参画したり、引き継いだ時は全く知識がない状態でどのようにソフトウェアの用件を知るのか。

=>多くの場合は仕様書などのドキュメントを頼りにする
=>しかし、そのドキュメントはコードと直接紐づくわけではないため、ドキュメントが更新されていなくてもソフトウェアには問題を引き起こさない。
=>度々ドキュメントが役に立たないことがある。
=>開発者は結局のところコードを手がかりに仕様を知ることになる。
=>ドメイン駆動設計ではドメインオブジェクトとして実装することで、「無口なコード」になることを防ぐ。

★振る舞いを持ったオブジェクトはそのソフトウェアがどのドメイン知識に関心があり、それをどのように識別しているかを明確にする。
これが後続の開発者がドメイン知識を理解する有効な手段となっていく。


## ドメインオブジェクトのエンティティについて
### エンティティの性質
①可変である
②同じ属性であっても区別される
③同一属性により区別される
#### ①可変である
エンティティは値の交換による変更は行わない。エンティティの属性を変更したい時は、その振る舞いを通じて変更することになる。

そもそも振る舞いって？...
=>淡白にsetterなどで属性の値を交換を行うのではなく、メソッド名で何を変更できる振る舞いなのかを明記する。
※ エンティティは全ての値を可変にする必要はない。むしろ可能な限り普遍にしておく方がベター。

ユーザーエンティティの氏名の属性を変更したい時は氏名を変更するためのメソッド（updateNameメソッド）を作成して変更できるようにする。

#### ②同じ属性であっても区別される、③同一性をもつ

人間には名前や身長体重などの属性がある。それが一つ変わっただけで、その人は違う人に変わってしまうのか。もちろん変わらない。
=>同一性を担保するものが存在するということ。

これをシステムに置き換えると、新規ユーザー登録した人のユーザー情報を変更を行い、氏名を変更すると別のユーザーになってしまうのか。
=>多くのシステムではIDなどで一意に決まるため、ユーザー自体が変更されるわけではない。

この時、ユーザー情報は同一性によって識別されている状態という。
エンティティ同士を区別するために識別子が利用され、値オブジェクトのように等価性で判断されない。

### エンティティのライフサイクル
何を値オブジェクトとして、エンティティとするかの判断基準
=>エンティティはライフサイクルが存在し、連続性が存在する。

ユーザーは新規作成されてから生を受け、時間が流れてそのユーザー登録が利用者にとって不要となったときに死を迎える。これはユーザーはライフサイクルを持ち連続性を持つということ。このためユーザーはエンティティとして定義すべき。



>参考：ドメイン駆動設計入門 ボトムアップでわかる！ドメイン駆動設計の基本
【React】React学習ログ　１本目 【ゼロから始める】


Railsで簡単アプリケーションが作れるようになった次のステップとしてReactを学ぶことにしました。
理由は・・・「なんかおしゃれなフロント作れそうじゃん？」みたいな先入観からです（笑）
備忘録として学習ログを残していきます。

### そもそもJavaScriptって何？
そこからです。そこからなんです。Reactの理解の前にJavaScriptが何なのかしっかり押さえていくことが大事かなと思ったのでまとめていきます。
・ブラウザ上で動く唯一のプログラミング言語
・ブラウザ上で動く言語→開発環境の構築が不要→初学者が取り掛かりやすい。
・技術の移り変わりが早い。
・どのWebシステムでも使われている（はず）。
・バックエンド開発もできる。

### モダンJavaScriptって？
・仮想DOMを用いるライブラリ（例）React,Vue,Angularなど・・・。
・npm/yarnなどのパッケージマネージャーを使用。
・webpack等のモジュールバンドラーを使用。
・Babel等のトランスパイラを使用

※仮想DOMについて
従来のDOM操作・・・コードが複雑になる。画面を表示する速度が遅くなる。
仮想DOM・・・JS上で仮想DOMを操作し、差分を出してからDOMに反映。

※パッケージマネージャー
かつてのJS開発、１つのJSファイルにすべての処理を記述。コードが複雑化。
パッケージマネージャー
→公開されているパッケージをコマンド一つで使える。つまり便利で簡単。
※モジュールバンドラー
複数のファイルを１つにまとめるためのもの
※トランスパイラ
新しいJavaScriptの記法を古い記法に変換してくれる。

今日はここまで！React本編への道はまだまだ長そうだ・・・。
bzip2、xzコマンドは「ディレクトリ内のファイルをすべて圧縮(-rオプション)」をできるんだっけ？

LinuC対策勉強の際、bzip2、xzコマンドに-r（ディレクトリ内のファイルをすべて圧縮）の記載がなかったので、コマンドとして使用できるのかを検証してみる。
（マニュアル見たほうが解決は速いのだが、、）

ついでに圧縮について、業務にてテストデータを準備するとき、そこそこ使用する頻度があったので、備忘録的にまとめたほうが後々便利かと思いアウトプット。
まず、gzip bzip2 xz の基本的な操作コマンドをオプション一覧でまとめる。


:::note info
余談ですが、-rオプションは指定したディレクトリごと圧縮するのではなく、ディレクトリ内のファイルをすべて圧縮するだけです。
ディレクトリを圧縮するにはtarコマンドを使用し、アーカイブを作成したあとにgzipコマンドなどで圧縮する必要があります。
:::

# ファイルの圧縮、解凍コマンドのオプション一覧

| オプションの説明\コマンド | gzip | bzip2 | xz |
|:-|:-|:-|:-|
| 圧縮ファイルを展開   | -d, --decompress | -d, --decompress | -d, --decompress |
|標準出力へ出力  | -c,--stdout | -c,--stdout | -c,--stdout |
|圧縮・解凍元ファイルを残す|-k, --keep|-k, --keep|-k, --keep |
|ディレクトリ内のファイルをすべて圧縮|-r, --recursive|(使用不可)|(使用不可)|

基本的な書式は下記の通り( {コマンド} はzip、gzip、bzip2、xz )
```txt:書式
$ {コマンド} [オプション] [ファイル名]
```

# 検証
上記の「ファイルの圧縮、解凍コマンドのオプション一覧」に記載したが、
結論を言えばbzip2、xzコマンドに-r(ディレクトリ内のファイルをすべて圧縮)は使用できなかった。

検証は以下の通り


```txt:検証ディレクトリ構造
/bash_test
|--test.txt
|--test2.txt
|--testdir
|  |--test2
|  |--test2.txt
|  |  |--test1.txt
```
ディレクトリ構造の表示は下記を参考

https://qiita.com/yone098@github/items/bba8a42de6b06e40983b

## bzip2 →　NG
成功せず。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3119139/a46392f9-a23c-47ed-26cb-10c46f64d1c0.png">

## xz →　NG
成功せず。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3119139/eb7a1352-e6c5-b693-9eb9-6bc4a803cb64.png">

## gzip →　OK
(当然であるが)成功。
<img width="600" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3119139/4ad3b8b5-ff23-0fa6-99fe-34524675da33.png">

実行後ディレクトリ
```txt:実行後ディレクトリ構造（-kオプションを使用し、元ファイルが残るように実施）
/bash_test
|--test.txt
|--test.txt.gz
|--test2.txt
|--test2.txt.gz
|--testdir
|  |--test2
|  |--test2.txt
|  |--test2.txt.gz
|  |  |--test1.txt
|  |  |--test1.txt.gz
```

# 結論
gzipでは使用できる-rは、bzip2、xzコマンドに使えなかった。
めったにないとは思うが、例えば、圧縮したテストファイルをディレクトリに分けて準備するときや、ディレクトリに分かれた圧縮データを解凍する時などには注意をしたい。


# 参考

https://tech.amefure.com/web-linux-gzip
Livebook 0.8.1 でカメラ撮影が簡単にできる

## はじめに

2023/01/19 に Livebook の 0.8.1 がリリースされました

https://github.com/livebook-dev/livebook/releases/tag/v0.8.1

追加機能の一つに、カメラからの画像取得があるので紹介します

Livebook 公式はこちら

https://livebook.dev/

## セットアップ

Livebook で Kino と StbImage （画像表示用）をインストールします

表示用のモジュールは他のもの（evisionなど）でもOKです

```elixir
Mix.install([
  {:kino, "~> 0.8"},
  {:stb_image, "~> 0.6"}
])
```

## 画像入力の作成

画像入力を作成します

```elixir
image_input = Kino.Input.image("CAMERA")
```

![camera.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/fa46da45-4d6e-b8b8-0ea5-3d7509aabeca.png)

Open camera のボタンが表示されます

## 注意点

Chrome で http://0.0.0.0:8080 など、ローカルで Livebook を開いている場合、 Open camera のボタンをクリックしても何も動きません

ブラウザのコンソールログを見ると、以下のようなエラーが出ています

```
Uncaught TypeError: Cannot read properties of undefined (reading 'getUserMedia')
    at Xe.renderCameraList (app.js:2:3588375)
    at HTMLButtonElement.<anonymous> (app.js:2:3586544)
```

Chrome は HTTP 通信時にカメラ入力を許さないようになっています

なので、 ngrok を使ってローカルの Livebook と HTTPS 通信します

https://ngrok.com/

ngrok の使い方については以下 @mininobu さんの記事を参照してください

https://qiita.com/mininobu/items/b45dbc70faedf30f484e

ngrok を実行して、下に表示される HTTPS の URL を開き、 Livebook の認証トークン（Livebook 起動時に表示されている `[Livebook] Application running at http://0.0.0.0:8080/?token=<ここの値>`）を入力してください

当然、上の HTTP の方にアクセスしてもカメラは使えません

```bash
$ ngrok http 8080
...
Forwarding                    http://xxxx-xxx-xxx-xxx-xxx.ngrok.io -> http://localhost:8080
Forwarding                    https://xxxx-xxx-xxx-xxx-xxx.ngrok.io -> http://localhost:8080
```

この状態であれば先に進めます

## カメラの許可

Open camera ボタンをクリックすると、ブラウザの右上にカメラの使用許可を求めるダイアログが表示されます（ブラウザによって挙動は異なります）

![allow_camera.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/7c9c446b-e092-c0aa-8ea7-75a7f9a97989.png)

「許可する」や「Allow」などのボタンをクリックしてください

2回目以降はダイアログは表示されません

## カメラ撮影

カメラの選択肢が表示されるので、使いたいカメラを選択します

![select_camera.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/a0cf4c84-7eb8-1f73-eb88-7d6cd616ba1c.png)

そうするとカメラ映像が表示され、ボタンが Take photo と Cancel になります

![take_photo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/e578e485-c657-51fd-8e85-15f06658a8f8.png)

Take photo をクリックすると、そのときの映像が静止画になります

## 画像の表示

`Kino.Input.read()` で画像を読み込みます

StbImage の場合は以下のようにして画像を表示します

```elixir
image_input
|> Kino.Input.read()
|> then(&StbImage.new(&1.data, {&1.height, &1.width, 3}))
```

![oni.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/4ab980a1-100e-9984-4762-2c8437b26ad7.png)

## まとめ

簡単にカメラ映像を使えるようになったので、色々なことに応用できそうですね