OmniAICreator/Qiita-1.07M huggingface dataset:
python datetime練習

[PythonでUNIX時間（エポック秒）と日時datetimeを相互変換 \| note\.nkmk\.me](https://note.nkmk.me/python-unix-time-datetime/)
[Pythonのdatetimeで日付や時間と文字列を変換（strftime, strptime） \| note\.nkmk\.me](https://note.nkmk.me/python-datetime-usage/)

```py:dt_example.py
import datetime

def main():
    # 本日を取得
    dt_today = datetime.datetime.today()
    print(dt_today.strftime('%Y-%m-%d %H:%M:%S'))
    # unixtime
    unixtime = dt_today.timestamp()
    print(unixtime)
    # unixtime -> datetime object
    dt_obj_fromunix = datetime.datetime.fromtimestamp(unixtime)
    print(dt_obj_fromunix.strftime('%Y-%m-%d %H:%M:%S'))
    # datestr -> datetime object
    dt_str = '2020/12/01 12:00:00'
    dt_obj = datetime.datetime.strptime(dt_str, '%Y/%m/%d %H:%M:%S')
    print(dt_obj.strftime('%Y-%m-%d %H:%M:%S'))


if __name__ == '__main__':
    main()
```

```
2020-12-12 16:04:38
1607756678.533729
2020-12-12 16:04:38
2020-12-01 12:00:00
```
vue3をvue-i18nを使って多言語化対応してみる

vue3に移行している際に多言語化対応について調べたのでメモ

参考(https://vue-i18n-next.intlify.dev)

#環境
Vue3(cliで環境作成)
Typescript


#多言語化プラグイン　vue-i18nを使う

インストール

```
# npm install vue-i18n@next
```


```main.ts
import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'

const messages = {
    en: {
      message: {
        hello: 'hello world'
      }
    },
    ja: {
      message: {
        hello: 'こんにちは、世界'
      }
    }
  }


const i18n = createI18n({
    locale: 'ja', // set locale
    messages, // set locale messages)
})

const app = createApp({
  // something vue options here ...
})

app.use(i18n)
app.mount('#app')
```

```App.vue
<template>
  <div>
    {{$t("message.hello")}}
  </div>
</template>
```

多言語化しなくても、メッセージを切り分けることで後々メンテナンスが楽になると思います。
githubでcloneできなかったときの解決法

cloneしようとしたら

~~~
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
~~~

と出たので下記コマンドで設定ファイルを編集して

~~~
vim ~/.ssh/config
~~~

Host のあとにgitbubだけじゃなくて、github.comと書いたら解決した

~~~
Host github github.com
 HostName github.com
 User git
 IdentityFile ~/.ssh/id_rsa_github
~~~

(参考)標準出力をクリップボードにコピーするにはpbcopyを使う

~~~
cat ~/.ssh/config | pbcopy
~~~
【2日目】Rust入門 ~構想について~

# 【2日目】Rust入門 ~構想について~

今日は、本当に軽く方針を決めます。
公式にweb上でrustを試せるサイトがあるのですが、簡単に文法や型の調査をしました。
https://play.rust-lang.org/

```rust
fn min(x:i32,y:i32) {
    if x > y {
        return println!("{}",y);
    } else {
        return println!("{}",x);
    }
    
}

fn plus(x:i32,y:i32) -> i32 {
    return x + y;
}
```
引数のx,yはi32という型（符号付き32ビット整数,要するにintです)で型定義しています。
普段、javascriptなどの型定義が曖昧な高級言語しか使ってなかったので、上の関数の戻り値の型にi32をつけてしまうという初歩ミスを犯したりなど自分は間抜けプログラマーだと痛感しました。
これ以外にも、traitや所有権などあまり見慣れない概念もあり少しずつ理解していこうと思います。

## 方針
とりあえず、webアプリを作る上で言語を統一したいので、rustを使い慣れない自分でも簡単に作れる様,フレームワークにActix-webかrocketを使いたいと思います。Actix-webは、非同期処理にも対応しているとのこと。
作るアプリについては、とりあえず性能が測りやすい様なものを想定しています。例えば、電卓を作ったりしている方もいたのですが、オリジナルのアプリを模索したい...　この案については、引き続き考査していきます。
今日は、記事にする程の事でもありませんでしたが、軽く書いてみました。
余談ですが、rust今のところ本当に需要が少ないですね。世の中的に、WebAssemblyの活路が見出されていない事も要因なのでしょうか。
本当にとりあえずで始めたので、途中で打ち切りも考えています。

## 追記
憶測で語ってる部分や、間違いがあるかもしれないので詳しくRust,WebAssemblyを知りたい方は、他の記事をお使いください
Rust入門についての参考: http://www.tohoho-web.com/ex/rust.html
WebAssemblyについての参考: https://ukyo.github.io/wasm-usui-book/webroot/what-is-webassembly.html
【備忘録】初心者が覚えるべきUnixコマンド

 
# プログラミング初心者が覚えるべきunixコマンド集
## 覚えないと序盤で詰むコマンド
+ cd => ディレクトリを移動するコマンド
+ ls => ディレクトリ配下のファイルを一覧表示するコマンド

　使用頻度がとても多く覚えないと序盤で詰む（が覚えやすい）

## 徐々に使えるようになるべきと思ったコマンド
+ mkdir	=> フォルダを作成するコマンド
+ pwd => 現在のディレクトリの絶対パスを表示するコマンド
+ cd => ディレクトリを移動するコマンド
+ rm => ファイルやフォルダを削除するコマンド
+ cp => ファイルやフォルダをコピーするコマンド
+ touch	=> ファイルの最終更新日を変更するコマンド。ファイルが存在しない場合は新規作成する。

 ここら辺のコマンドも重要度高いですね。徐々に使いこなして行きたいです。
Gatsbyでフォントをセルフホスティングする方法

# はじめに
GatsbyでWebフォントをセルフホスティングする方法を紹介します。
セルフホスティングをすると、Google Fontsなどのサービスからフォントを取得する場合と比べて、パフォーマンスが向上するというメリットがあります。

# フォントのセルフホスティングとは
セルフホスティングとは、**自前のサーバに置いたフォントファイルをwebページのフォントとして使用すること**です。
Google Fontsなどのサービスがもつサーバからフォントを取得する方法とは異なります。

# セルフホスティングするメリット
セルフホスティングをするメリットは、Webページのパフォーマンスが向上することです。
サービスがもつサーバからWEBフォントを取得する場合は、そのサーバにHTTPリクエストをする必要があり、日本語などの文字の種類が多いフォントの場合は、大きなボトルネックになってしまいます。
しかしながら、セルフホスティングをすると、上記のフォント取得による遅延を軽減することができます。

# 使用するパッケージ
[Fontsource](https://github.com/fontsource/fontsource
)というモノレポから好きなフォントを選び、インストールします。
自前でフォントファイルを、デュレクトリに配置するという方法もありますが、設定が面倒臭いのでライブラリに頼ることとします。

# 実装方法
Google Fontsにも存在する「Noto Sans JP」をインストールすることを例に実装方法を説明します。
## パッケージのインストール
まずパッケージをインストールします。

```shell
yarn add fontsource-open-sans
```

パッケージマネージャーにnpmを使用している場合は下記の通りです。

```shell

npm install fontsource-open-sans
```

## `Layout.js`にimportする
`Layout.js`に下記のようにimportします。

```js
import 'fontsource-noto-sans-jp';
```

上記では、デフォルトのフォントウェイトに、全てのスタイル(Boldなど)が含まれる状態でimportしてしまうため、下記のように使用するフォントウェイトとスタイルを指定すると、ペイロードサイズを削減できます。

```js
import "fontsource-noto-sans-jp/700.css"
import "fontsource-noto-sans-jp/900-normal.css"
```

あとは、CSSで`font-family`を指定してあげれば、使用ができるようになります。

# まとめ
[Fontsource](https://github.com/fontsource/fontsource
)というライブラリを使用して、GatsbyでWebフォントをセルフホスティングする方法を紹介しました。
参考になれば幸いです！
Anaconda3 インストール時につまづいた点

Pythonで機械学習を勉強しよう、まずはanacondaのインストールから！
Anaconda3のインストールが終わり、Anaconda Promptでライブラリを追加しようとしたところ以下のような点でつまづいてしまった。

#conda installを実行するとエラーが出る。
Pythonをバージョン指定でダウンロードしようと以下のコマンドを実行。

```py
conda install python=3.7.6
```

実行するとネットワーク系のエラーが出力されました。
インストール時にPathを通していないからかな？と思い、以下を実行。
（Windows10）
コントロールパネル→システムとセキュリティ→システム
→システムの詳細設定→環境変数
→ユーザー環境変数の欄からPathを選択。
C:¥インストール先\Anaconda3
を追加。

それでもエラーがなくならず。どうやらAnaconda3フォルダの直下に.condarcというファイルを作成しないといけないらしい。

```py
proxy_servers:
    http: http://ID:password@IP:ポート番号
    https: https://ID:password@IP:ポート番号
```

ここでのID、passwordというのは認証が必要なネットワークでのユーザーID、passwordのことである。
大学や会社でネットワーク接続に認証が必要な人はこれを書かないといけないらしい。

参考文献：
https://ja.stackoverflow.com/questions/54019/%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7%E8%A8%AD%E5%AE%9A%E3%81%8C-%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%82%92%E4%BD%BF%E3%81%86-%E3%81%8C%E3%82%AA%E3%83%B3%E3%81%AE%E5%A0%B4%E5%90%88%E3%81%AEconda%E3%81%AB%E3%82%88%E3%82%8B%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E6%8E%A5%E7%B6%9A%E8%A8%AD%E5%AE%9A%E3%82%92%E6%95%99%E3%81%88%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84

https://qiita.com/miyamoto_works/items/4676a7ee350210624ed9


#pip使用時SSLに弾かれる
conda install 同様にSSLにはじかれるので上記のように実行。

```py
pip install --proxy=http://ID:password@IP:ポート番号 インストールするもの
```
自分はopencvをインストールしたかったので以下を実行。

```py
pip install --proxy=http://ID:password@IP:ポート番号 opencv-python
```

参考文献：
https://ja.stackoverflow.com/questions/43671/pip-install-%E3%81%A7-newconnectionerror-failed-to-establish-a-new-connection-%E5%90%8D%E5%89%8D%E8%A7%A3%E6%B1%BA%E3%81%AB

#仮想環境を作る
Pythonをバージョン指定で入れようとしtが、既存のバージョンとコンフリクトを起こしたので仮想環境を作成した。

参考文献：
https://qiita.com/ozaki_physics/items/985188feb92570e5b82d
[Python]DFS（深さ優先探索） Lake Counting (POJ No.2386)

#[2386 Lake Counting（蟻本）](https://wikiwiki.jp/pku/2386%20Lake%20Counting)

#DFS（深さ優先探索）
**DFSは、初期状態から遷移を繰り返すことで辿り着けるすべての状態を生成します。したがって、すべての状態に対して操作を施したり、全状態を列挙したりできます。**
#解法
適当な W から始め、そこからつながっている部分を . に置き換える操作を繰り返します。1回のDFSで、始めの W とつながっている W はすべて . に置き換わるので、W がなくなるまでに何回DFSを行ったかが答えです。DFSは各地に対して高々1回しか呼ばれないので、計算量は$O(N\times M)$となります。

```python:サンプルコード
from itertools import product

n, m = map(int, input().split())
lake = [input() for _ in range(n)]
# 辞書型で各位置の状態を入力
lakedict = {(i, j):c for i, row in enumerate(lake) for j, c in enumerate(row)}

# 水溜りを乾燥地にする再帰関数
def dfs(i, j):  # (i, j)は現在位置
    # 現在地が W でなければ終了。範囲外のgetは None 
    if lakedict.get((i, j), '.') != 'W':
        return
    # 現在地 W は . におきかえる
    else:
        lakedict[i, j] = '.'
    # 隣接地に再帰dfs
    for di, dj in product((-1, 0, 1), repeat=2):
        dfs(i+di, j+dj)

n = 0  # dfsを呼ぶ回数
for i, j in lakedict:
    if lakedict[i, j] == 'W':
        n += 1
        dfs(i, j)

print(n)
```
[React-native + expo]エラー監視ツールSentryを導入してみた

最近の勉強で学んだ事を、ノート代わりにまとめていきます。
主に自分の学習の流れを振り返りで残す形なので色々、省いてます。
エンジニアの諸先輩方からアドバイスやご指摘を頂けたらありがたいです！

# はじめに
アプリを運用していくにあたって、エラーの監視をどのツールで行うか検討した結果
ドキュメント(Expo, sentry)の豊富さだったり、React Native のサポートだったり、そもそもの使いやすさの面 + 価格が 5000/month が free ということで、相性の良さそうな Sentry を選びました。

# Sentryとは？
フロントエラーの監視ツール。
公式サイトはこちら
https://sentry.io/welcome/

今回はreact-native側で起きるエラーをSentryで監視します。

# 導入フロー
Sentryのアカウント設定などはこの記事を参考に進めました！
[Expo(React Native)でSentryを利用してみる](https://qiita.com/zaburo/items/abb1734bba1bb6f7db03/)

Sentryの設定が完了したらExpoのドキュメントを参考に `App.tsx`と`app.json`に必要なcodeを追記すれば完了です。
[Using Sentry](https://docs.expo.io/guides/using-sentry/?redirected/)

動作確認の為、ボタンを押しSentryでエラー確認できたらOKです。

``` App.tsx
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import * as Sentry from 'sentry-expo';

//Sentry初期化
Sentry.init({
  dsn: 'https://04770839cdf74bfca91ea8xxxxxxxxxx@sentry.xxxxxxxxxx',
  enableInExpoDevelopment: true,
  debug: true
});

export default class App extends React.Component {
  render() {
    return (
      <View style={{ flex: 1, justifyContent: 'center' }}>
        <Text>App</Text>
        <Button
          title="throw error"
          onPress={() => {
            throw new Error('test error'); //button pushでエラー発生させる
          }}
        />
      </View>
    );
  }
}

export default App;
```

![スクリーンショット 2020-12-12 15.11.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394657/acc7a08c-ba64-5d95-5477-1db6614ef76d.png)

# React-nativeのエラーを検知するようにcodeを追記
Sentryを導入して試しにReact-native側のエラーを検知できるかテストしましたが
ドキュメントに書いてある基本設定だけではreact-nativeのエラーを検知することができませんでした。

###React-nativeでの設定を追加

`@sentry/react-native`を `yarn add @sentry/react-native`コマンドで追加し
最上位の App.tsx で Comonent を `SentryNative.ErrorBoundary`でwrapします。
これで子 Component 以下で発生するエラーを補足することができます。


```App.tsx
import React from 'react';
import { StyleSheet, Text, View, Button } from 'react-native';
import * as Sentry from 'sentry-expo';
+ import * as SentryNative from '@sentry/react-native';

//Sentry初期化
Sentry.init({
  dsn: 'https://04770839cdf74bfca91ea8xxxxxxxxxx@sentry.xxxxxxxxxx',
  enableInExpoDevelopment: true,
  debug: true
});

export default class App extends React.Component {
  render() {
    return (
     + <SentryNative.ErrorBoundary showDialog>
         <View style={{ flex: 1, justifyContent: 'center' }}>
          <Text>App</Text>
          <Button
            title="throw error"
            onPress={() => {
              throw new Error('test error'); //button pushでエラー発生させる
            }}
          />
         </View>
     + </SentryNative.ErrorBoundary>
    );
  }
}

export default App;
```

これで、React-nativeで発生したエラーを補足してエラーを表示することが出来るようになります。

#参考記事
・[僕の考える最強のReact Native開発ライブラリ等（2020年11月末版）](https://zenn.dev/hal1986/articles/e7ed1579bd7ff73ee0ec/)
・[react + react native(Expo)でエラーハンドリングどうやる？](https://chick-tack.blog/posts/2019/react-react-native-expo-error-handling//)
・[FlutterアプリでSentryとFirebase Crashlyticsにエラーレポートを送りアプリの品質改善に役立てる](https://wapa5pow.com/flutter-report-errors/#sentry%E3%81%AE%E5%B0%8E%E5%85%A5/)
【JS】通貨記号＆カンマ付き文字列をNumberに変換する

`replace`  で変換してNumberに変換します。

```js

> Number("¥1,000,000".replace(/[¥,]/g, ''))
  => 1000000
```

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/replace
【簡単に図解】AWSのEBSとAMIとスナップショットを自分なりにまとめてみた

# 背景
* 最近AWSの勉強を開始
* AMIやスナップショットなどの用語の意味があやふやになってきたため、自分なりにまとめてみました

## EBSとは
* EC2インスタンスの外付け仮想ディスク
* HDDやSSDなどを選択可能
* ボリュームといっているのは、(おそらく)これのこと

## AMIとは
* EC2インスタンス＋EBSスナップショットのバックアップ
* EBSスナップショットはS3に保存される
* これがあれば、EC2の複製が可能
![スクリーンショット 2020-12-12 15.13.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882682/3d4182ad-a934-6fcc-c9cf-90efb1aba9c2.png)

## スナップショットとは
* 正確にはEBSスナップショットのこと
* 言葉の通り、EBSのスナップショットを作成する
* 保存場所はAMI作成時と同様に、S3に保存される
* インスタンスに関しては、何も情報を保存しない
![スクリーンショット 2020-12-12 15.13.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/882682/a53383ad-9ee7-2d54-1924-823d5cf7f66e.png)
deviseのcontroller#createをいじったけどエラーでた時のメモ

```ruby:app/contoller/customers/registrations_controller.rb
class Customers::RegistrationsController < Devise::RegistrationsController
  before_action :configure_sign_up_params, only: [:create]

...
  def create
    super
    @customer.company_id = params[:company_id]
    binding.pry
    if @customer.save
      CustomerStatus.create(paid: false, room_plus: false, dozen_sessions: false, numbers_of_contractnt: 0, customer_id: @customer.id)
    end
  end
...
  protected

  def configure_sign_up_params
    devise_parameter_sanitizer.permit(:sign_up, keys: [:company_id])
  end
```

カスタマーをずっと保存できなかった。
![スクリーンショット 2020-12-12 15.14.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239663/e32c74a2-d3ca-e8ca-7e95-62a516ef1e01.png)


原因はモデルのcustomer.rbのここでした
![スクリーンショット 2020-12-12 15.16.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239663/ce23b7b9-e191-c2a1-1070-0fed75c11eaa.png)

deviseはbelongs_to をしたら新規登録でうまくいかなくなるみたいなので、気をつけましょうというメモです。

##　解決法
####7行目の部分です
![スクリーンショット 2020-12-12 15.17.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239663/efaeb9c4-2c8d-32ec-704b-fd5c04b60d45.png)

以上！
近年徐々に浸透しつつあるノーコードについて

##ノーコードとは
開発する上で行うことといえば**プログラミング**ですね。
ノーコードの場合このプログラミングの際に用いる**ソースコード**を使用する必要がなくなり、直感的に**パーツをドラッグ＆ドロップ**して開発していくことが可能となっています。
これは特定のプラットフォーム上で開発していく必要があるのですが、プログラミング知識がない方でも形にしていくことは可能です。

##ノーコードの市場
現在、世界全体でノーコード開発者が80万人以上いると言われてます。
親しいところでいうとローコード開発があげられ、合わせると市場には260万人近くの開発者がおり、これは世界の開発者人工の10%にも及びます。
民間の調査会社によると、2018年には78憶ドルであった世界のRAD市場が、2023年には462憶ドルに成長するという予測が立てられており、今後数年での市場規模拡大が見込まれている状況です。
スピーディーな対応が求められている現在のビジネスシーンにおいて、ノーコードは今や見逃せないトレンドとなっており、プログラマーやIT技術者不足を解消できる切り口にもなりうるのではないかと思われます。

##ノーコードでなにができるの？
現段階ではノーコードで何を作るかというより、何をノーコードに置き換えられるのかという考えの方が多いように感じます。
制限があるなかで、どの程度の実現が必要なのか要件毎に切り出し、部分的にノーコードで構築するといった使われ方や、プロトタイプとして構築するなど、【**開発スピード＋低コストの実現** 】に向けた目的へ導入されるケースが多いように感じます。

実際、3～6カ月の開発期間を要する案件に対してノーコード開発ツールを活用した場合、たった2週間前後でアプリが完成するといったケースもあるように、開発スピードに関しては段違いに早いです。

一方で、ノーコード開発のデメリットもあり、開発ツールの操作方法に習熟する必要があることや、新たなキャッチアップは必要であり、構築以前の工程に関しても従来どおり要件定義や設計をを行う必要があるということです。

そういった中ノーコード作られた[モバイルオーダーアプリのSmartDish](https://prtimes.jp/main/html/rd/p/000000004.000064504.html)が脚光を浴びるなど、海外含めノーコードによるサービスは続々と登場しています。
このサービスはノーコードツールである[Adalo](https://www.adalo.com/)を使用して作られております。

また、ノーコードによるスタートアップも登場しており、2019年に著しくその動きが見られました。
Bubbleが630万ドルの資金調達を発表し、googleにより2020年1月に買収されたAppSheetは1500万ドルを調達するなど大型の資金調達も相次いでおり、ラウンド毎の調達企業数ではSeedステージが最も多いことから、今後も市場は盛り上がっていくことが予想されます。

##エンジニア不要論
ノーコード化が進み、複雑なものへ触れることなく誰でも開発ができるようになった時、エンジニアは不要になるのではないかと耳にしますが、実際エンジニアの需要がなくなるということはないと思われます。
このノーコードツールも裏ではシステムが組まれており、それらを構築するエンジニアや全てをノーコードで敢えて完結させる必要もないので、共存して共に良いところを組み合わせていくというところが落とし所になるでしょう。

##まとめ
ノーコードについて市場感や何に使うのかをまとめましたが、ツールの種類や使ってみて実際どうなのかという部分についてもまとめたいと思っています。

AdaloとBubbleを使ってみているのですが、構築が楽なのでプロトタイプとして使われるというのは割としっくりしました。
個人的には英語のみなので苦労しますがBubble が好きで、自由度が高く非常に使いやすいと感じてます。
ここについてはまた別途まとめます。
【Rais】N + 1問題への対応

# 概要
- gemをダウンロード
- 問題発生時にポップアップで知らせてくれる
- includesで問題を解決していく


# gem
- bullet
- https://github.com/flyerhzm/bullet

# ポップアップ通知
- 開発環境で問題が発生しているページにアクセスすると、ポップアップで知らせてくれる

# 問題の解消
- ポップアップ通知が出なくなるまで、問題へ対処
EKSとGKEを両方触ってみて思ったこと

この記事は「[イエソド アウトプット筋 トレーニング Advent Calendar 2020](https://adventar.org/calendars/5888)」12日目の記事です。
前職でEKS,現職でGKEを触ったので感じた違いとかを書いていこうかなと思います。

# はじめに
EKSとGKEを両方触って感じたことは、Kubernetesのマネージドサービスとしては**GKEの方が優秀**だと思いました。
そう感じた理由を一つずつ書いていこうと思います。(※個人的感想です)

# ノード管理について
まずEKSはKubernetesの1.14バージョン以前まではEKSのマネージドノードグループがありませんでした。
そのためEC2のAutoScalingグループに指定のタグをつけてどのクラスターのノードグループなのかという識別をさせる必要がありました。
こういった点から正直EKSを利用する際は、関連するコンポーネントの管理も含めIaC化をしないと管理が厳しいのではないかと感じました。(タグが一文字typoしただけでも動かなくなるとかあるので。。)
ちなみに私はterraformのEKSモジュールを使用して管理をしていました。
1.14以降はマネージドノードグループができたので、そういった事をする必要は基本なくなりました。
しかし現状EKSのマネージドノードグループはスポットインスタンスに対応していないため、スポットインスタンスをクラスター内に組み込みたい場合は前述の方法でAutoScalingグループを自分で作成して管理をするか、EKSのマネージドノードグループを設定した後に起動テンプレートのスポット購入オプションの%を変更するなどしないといけません。
その点GKEはコンソール上から簡単にノードプールを追加して、プリエンティブルノードへの変更もできるのでノードの管理が楽です。

# ポッドのスケーリングとクラスターのスケーリングについて
EKSでHPA/VPAを利用するにはmetrics serverを自分でインストールをする手間が入ります。
GKEには標準でインストールされているためHPA/VPAのマニフェストを適用するだけで設定が完了します。
また、ノードのスケーリングについてもEKSではCluster Autoscalerを自分でインストールする必要がありますがGKEは標準でインストールされているのでコンソール上から有効にして数を指定するだけで設定が完了します。

EKSではこれらのコンポーネントのインストールに加えてIAMロールで権限を設定してあげる必要があるなど結構めんどくさいです。

# ログについて
EKSではPodのログをCloudWatchなどのログビューアーに転送するためには、何かしらのログ転送デーモンをノード上に展開をする必要があります。
私はfluentd-cloudwatchをDaemonsetとして利用してCloudWatchに転送をしていました。
その点GKEでは特に設定をする事なくStackdriverにログが転送されてくれるため非常に楽です。
また、ログビューアーといった観点で可読性や検索性についてCloudWatchと比較してStack driverの方が優れていると感じますし、EKSを使用するにしてもログの転送先は多少手間をとってでも　Stackdriverを使用した方がいいと思います。

# アクセス管理について
社内ツールや開発環境など社外から見られたくないけど、インターネットに公開していて欲しいリソースは何かしらの認証をかけるのが一般的です。
BASIC認証やIPホワイトリストベースの認証など方法は色々ありますが、GKEではIdentity Aware Proxyという機能を利用すれば特定のサービスに対してGoogle認証をかけることが可能になります。
これによって認証管理の手間を削減することができる上、セキュリティリスクの低減に繋がります。
またGKEはWorkload Identityを利用することでKubernetesのサービスアカウントとGCPのサービスアカウントを簡単に紐づけることができるため、GCP上の別サービスへのアクセスコントロールを容易に行うことができます。

# 全体的に
EKSはKubernetesでやりたい事を実現するために複数のサービスを横断してそれぞれにタグ付けをしたり権限を設定する必要があり、管理が非常に煩雑になります。
またmetrics serverやCluster Autoscalerやログデーモンなど自分たちでマネージしなければいけないコンポーネントの数が増えるため管理工数が高くなります。
複数のサービスを横断することもあって、Webコンソール上でも欲しい情報に辿り着くまでの画面の遷移数がGKEと比較して多く手間がかかっていた印象があります。

その点GKEはGKEを立ち上げれば、全部入りといった印象でKubernetes内のアプリケーションやネットワークの管理に集中をすることができるので、個人的にはGKE最高！！って感じです。

雑な記事ではありますが、Kubernetesのマネージドサービスを比較検討されている方や、既にEKSやGKEを利用している方の参考に少しでもなれば嬉しいです。

来週にもまた何かネタを書くのでよろしくお願いします。
【UiPath】Advent of UiPath 2020 #2（正規表現で文字列を抽出）

この記事は、UiPath Forum で行われている [**Advent of UiPath 2020**](https://forum.uipath.com/t/advent-of-uipath-2020/273299) の [Advent Challenge #2](https://forum.uipath.com/t/advent-challenge-2-everyone-welcome-to-join-the-event/273936) の紹介記事です。

:christmas_tree: 過去の投稿

- [Advent of UiPath 2020 #1（APIからデータを取得）](https://qiita.com/miya_g/items/4021ba90aab84984cb3c)

# “Advent of UiPath 2020” とは何か？

- 英語版のフォーラムで投稿されています。
- RPA challenge です（＝お題を出て、みんなでRPA化してみる）
- 誰でも回答できます（フォーラムのアカウントがあれば）
- 3日おきに出題され、計8回（xmasイブまで）あります
- 回答して上位に選ばれれば、フォーラム上でスターがもらえます。
- フォーラムのスタッフが開催しているっぽいです。

# チャレンジ2：正規表現で文字列を抽出

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/b1efc6a7-334d-a887-2fd3-67ad2cf12ae1.png)
サンタにあなたの助けが必要です！サンタは現在、タイのバンコクで立ち往生しています。サンタは仕事で忙しかったため、トナカイにも休息を取らせていましたが、トナカイたちはどこかに行ってしまいました。でもサンタが彼らの名前を呼ぶと、トナカイたちは戻ってきます。
サンタは疲れていてトナカイの名前を思い出せません。。。でも幸運にも「Regex101」のWebサイトにトナカイの名前を保存していました。取り出すには正規表現で名前を引っ張り出す必要があります。

### 初心者用チャレンジ

正規表現を使用して、初心者リスト（https://regex101.com/r/AQJgbd/1 ）からトナカイの名前を取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/044cee29-feca-0c0c-c9f1-a9abb57adb3a.png)

### 上級者向けチャレンジ

正規表現を使用して、上級者用リスト（https://regex101.com/r/AQJgbd/2 ）からトナカイの名前を取得します。

でも待ってください！サンタのトナカイのリストは本当に散らかっています。。。
エルフはファイルで遊んでいて、中にクリスマスの画像を描いています。
エルフがコンピューターに雪玉を投げて、ファイルが破損し、全体にランダムな文字が含まれてしまっています。

でもヒントもあります。サンタはテキスト内のパターンについて教えてくれました。サンタは、
　各トナカイの名前には「トナカイ：」という接頭辞が付いている
　ちょうど8つのトナカイがいた
と言っていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/46dba610-6925-e50a-9e70-792992903ea9.png)


## 感想

今回は「正規表現で文字列を抽出する」というお題でした。

初心者用チャレンジは、シンプルな
`[A-Z][a-z][\w]+`
という正規表現でした。

上級者向けチャレンジは、「トナカイ：」という接頭辞が付いているヒントから
`(?<=Reindeer:\s)([A-Z][a-z][\w]+)`
という正規表現が使われています

「<=」が「XXXという文字で始まって」という意味です。

実際のフロー開発でも、正規表現はよく使います。
正規表現は奥が深く難しいので理解するのが難しいですが、簡単なパターンならサラっと書けるレベルになると
開発が早くなると思います。

どこかのサイトから持ってきて「コピペ」するにしても、内容を理解していないと後で思わぬバグになる可能性がありますので注意が必要かと思います（経験談）

回答で面白かったのは以下でした。


■ セレニウムでサイトをスクレイピングして、POwershellの正規表現で抽出する（もはやUiPathではない）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/6f27aea7-c1f0-22b0-a3f4-adc39bfecaab.png)

■ 1回目のチャレンジに出てきた「[ザ・ロック] (https://ja.wikipedia.org/wiki/%E3%83%89%E3%82%A6%E3%82%A7%E3%82%A4%E3%83%B3%E3%83%BB%E3%82%B8%E3%83%A7%E3%83%B3%E3%82%BD%E3%83%B3)」がまた、twilioでSMSを送ると言っています（笑）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154139/1cedc9ec-75ec-d15f-3180-5fa7f7f05163.png)

クリエイティブですね。
リモートワーク中、家族に画面を覗かれて変な空気になったので、Chrome拡張機能を開発している

[DMMアドベントカレンダー](https://qiita.com/advent-calendar/2020/dmm)18 日目の記事です。
昨日は、@matsuda-hiroki さんの「[なにもしてないのにEC2インスタンスが壊れた](https://qiita.com/matsuda-hiroki/items/522a8004be246711a52b)」でした。

## 奥さん「何見てるの？」

こんにちは。DMM.com の@tmitsuoka0423 です。

今年 4 月に電子書籍事業部にジョインし、ずっと自宅でリモートワークをしています。
電子書籍事業部では、一般向け電子書籍サービスである`DMM電子書籍`と、成人向け電子書籍サービス`FANZA電子書籍`の両方の開発を行っています。

そんなある日、悲劇は突然起こりました。

アダルトコミック画面の機能を開発していた夕方のこと。
奥さんが仕事から帰ってくるなり、私のディスプレイを見て言いました。**「何見てるの？」**

## リモートワーク中はディスプレイの覗き込みに注意

私は真剣に仕事をしていただけですが、家族から見るとそうは見えないみたいです。
（仕事内容は事前に伝えているのに）。
お子さんのいる方・二世帯住宅にお住まいの方はもっと注意が必要になるのではないでしょうか。

## Chrome拡張機能「tile」を開発中

そんなリモートワーカーのために、Chrome 拡張機能を開発しています。

![スクリーンショット_2020_12_15_12_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/16300d73-b4d0-d629-b656-52262aece1be.png)
HTML 上の画像にぼかしを入れてくれます。

完成次第、公開します！

## 使い方イメージ

（1）インストール後、Chrome 右上のパズルマークをクリック、tile をクリックします。

![2020-12-12_15h28_31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/301678bf-16ac-580a-07b8-99870e013b5e.png)

（2）ぼかすを選択します。

![2020-12-12_15h31_30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/0cbf48e3-9be3-c7f5-0465-65746b2dcbf1.png)

（3）ページに表示されている画像にぼかしが入ります。

![スクリーンショット_2020_12_15_12_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/16300d73-b4d0-d629-b656-52262aece1be.png)

## 内部処理の概要

内部で行っていることはとてもシンプルです。
全ての`imgタグ`を取得し、CSS 関数の[blur](https://developer.mozilla.org/ja/docs/Web/CSS/filter-function/blur())を利用してぼかしをかけています。

```javascript
const imgList = document.getElementsByTagName("img");
for (let i = 0; i < imgList.length; i++) {
  imgList[i].style.cssText = "filter: blur(8px);";
}
```

上記のコードを利用して、拡張機能をインストールせずに、`Chrome DevTools`の`Console`タブから実行することも可能です。

![スクリーンショット_2020_12_15_12_59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/90087/ad936dcf-e373-4431-8612-4fba722798ae.png)

## まとめ

開発中の Chrome 拡張機能「tile」の紹介しました。
リモートワークをより働きやすくするためのアプリを今後も開発していきます。
HOYAのVoiceTextを使ってDiscordのテキストを読み上げるBotを作ってみた

# はじめに
このBotを作った経緯としては、夜遅くなどどうしてもしゃべれない時間帯があるため、そうした時間帯でもVCで参加する方法が必要になり、このBotを作ってみました。
どうやら、既に喋太郎というBotがあるらしいですが、このBotを最初に作った時（2018年2月ぐらい）にはなかったのでご容赦ください。
似たようなことをしたい人の参考になれば良いなと思います。

# ソースコード
GitHubにあります。
[noriokun4649/Discord-TTS-Voice-channel-Bot](https://github.com/noriokun4649/Discord-TTS-Voice-channel-Bot)
利用してるライブラリなどの依存関係はpackage.jsonに書いてありますが下記の通りです。

* Node.js v12.19.0
* @discordjs/opus : 0.3.2
* config : 3.3.2
* config-reloadable : 1.0.8
* discord.js : 12.4.0
* ffmpeg-static : 4.2.7
* voice-text : 0.1.2


# 開発してみての感想
VoiceTextのライブラリや、Discordのライブラリが用意されていたため、簡単に開発することができました。
躓いた点としては、VoiceTextのライブラリから返ってくるbufferをDiscord.jsのstreamに渡す方法に躓きました。
このBotを作った当初(2018年2月ごろ)の時点では、一度wavファイルに保存して、保存後wavファイルを読み取りDiscord.jsに渡すという糞みたいな処理をしてましたが、現在は直接bufferからstreamに変換して渡しています。

また、DiscordのAPI自体の仕様で、Discordのテキストチャネルで送信されるサーバ独自の絵文字やメンションなどが変わった形で送られてくるため、こうした絵文字やメンションの処理も少しだけ戸惑いましたが、最終的には正規表現で対応しました。

# 使い方
Node.jsで動くので通常どおりnpmのコマンドで使えます。

## 機能や設定、コマンドなど
機能はそんなに多くありませんが下記のような機能があります。

* コマンドの接頭語の変更機能
* エラー時の自動再起動する機能
* 読み上げ音声の変更機能
    * 音声の種類変更
    * 読み上げ音声の速度の変更
    * 読み上げ音声の高さの変更
* ブラックリスト
    * コマンドの接頭語別に読み上げ禁止にする機能
    * ユーザー別に読み上げ禁止にする機能
    * すべてのBotを読み上げ禁止にする機能

読み上げ音声の変更機能以外は、Configファイルで設定可能です。

### Configファイルについて
configフォルダ内のdefault.jsonがコンフィグファイルです。

```json
{
  "Api": {
    "discordToken": "",
    "voiceTextApiKey": ""
  },
  "Prefix": "/",
  "AutoRestart": true,
  "ReadMe": false,
  "Defalut": {
    "apiType": 1,
    "voiceType": "hikari"
  },
  "BlackLists": {
    "memberIds": [
      "381054450451742720"
    ],
    "prefixes": [
      "!",
      "/"
    ],
    "bots": true
  }
}
```

| 項目 | 内容・説明  |
| ------------ | ------------ |
| discordToken  |  Discordのトークンを記入 |
| voiceTextApiKey | VoiceTextのAPIキーを記入  |
| Prefix  |  コマンドの接頭語を決めます |
| AutoRestart  |  予期せぬエラー時に自動でボイスチャンネルへ再接続すかどうか |
| ReadMe  |  このBotが送るメッセージを読み上げるかどうか |
| apiType  |  デフォルトのAPIを指定 (利用できるAPIが1になってしまったので無意味)|
| voiceType  |  デフォルトのボイスを指定 |
| memberIds  |  読み上げから除外するユーザーのユーザーID |
| prefixes  |  読み上げから除外する接頭語  |
| bots  |  Botを読み上げから除外する  |

### Botのコマンドについて

| 接頭語+α | 内容・説明  |
| ------------ | ------------ |
| join  |  ボイスチャンネルにBotを呼びます |
| reconnect  |  ボイスチャンネルへ再接続します |
| kill  |  ボイスチャンネルから切断します |
| mode  |  読み上げに利用するTTSのAPIを変更します |
| type  |  APIで利用可能な音声タイプを一覧表示します |
| voice  |  音声タイプを変更します |
| speed  |  音声の速度を変更します(0～200の数値) |
| pitch  |  音声の高さを変更します(0～200の数値) |
| reload  |  コンフィグを再読み込みします |

接頭語は、Configファイルで指定した接頭語を使います。

# ToDo
一応まだ、開発のモチベーションがあるので今後の予定としては、ユーザーごとに読み上げ音声の種類を設定できるようにしたいと考えています。
聞き専の人が複数居ても、読み上げ音声の種類で人を区別できるようになるので利便性が上がると思います。

# 最後に
2018年2月頃に書いたプログラムをリファクタリングや機能の改善、バグ修正などしましたが、最終的には実用に足りる実装ができたと思います。
まだまだ、至らぬ点がありそうですが今後も開発出来たら良いなと思います。
最後まで見て頂きありがとうございます。

# P.S.
書くの忘れていましたが、一応Dockerで動かせるようにDockerファイルとdocker-composeファイルも作ってあります。(別リポジトリですが)

[noriokun4649/DiscordTTSBot-Docker-compose-setup](https://github.com/noriokun4649/DiscordTTSBot-Docker-compose-setup)
# 更にP.S.
DockerfileをGitHubのPackageにアップしたので下記のコマンドで使えるようになります。

```shell
docker pull ghcr.io/noriokun4649/discord-tts-bot:1.0.4
```
【初心者】PHPでの配列の並び替え

# 配列の並び替えについて
今日は配列の並び替え（ソート）について覚えておきたい。
結論としては…

```
sort(配列名);
```

だった。
これを利用すると、アルファベット順、数字は小さい順に並び替えが可能だった。
平仮名の場合も五十音順になるようだ。

以前、バブルソートのコードの書き方を覚えておかなければ。と思っていたのだけれど…

きっとどこかで役に立つかもしれない。

今ある知識でもなんとかできるのか考えることは面白い。
それに加えて色々なことを知ることで、できることの幅が広がっていくことは本当に面白い。
node.jsのエラー解決方法　"gyp ERR! find VS You need to install the latest version of Visual Studio"

#node.jsを使用するときのエラーの対処法

普段、Vue.JsのフレームワークであるNuxt.jsを使用して主に開発業務を行っているのですが、
node.jsでのエラーにはかなり悩まされました。
ちなみに私のOSはwindowsです。macユーザーLinuxの方はご参考程度に。

yarn devでプロジェクトは起動できるからといっかと無視できない下記のエラー

```:Nuxt
gyp ERR! find VS You need to install the latest version of Visual Studio
gyp ERR! find VS including the "Desktop development with C++" workload.
gyp ERR! find VS For more information consult the documentation at:
gyp ERR! find VS https://github.com/nodejs/node-gyp#on-windows
```

このエラーが出ている状態では、ライブラリが正常に入らなかったり、
yarnをしてもyarn.lockが入らなかったり、非常に厄介。

解決方法はググってみるといろいろ出ています。
オーソドックスなのが

##PowerShellの管理権限から"widows-build-tools"を入れる。

```:PoweShell
npm install --global --production windows-build-tools
```

node.js自体がそもそも、C言語やPythonで作られているようなので、python2.7やらCをインストールして
パスを通す必要があるようです。
上述のコマンドを入れるとすべて自動で入れてくれるという優れもの！

しかし、今回はこれではエラーが出たままでした(´;ω;｀)


##Visual StudioのC++ Build Toolsを入れてパスを通す。

参考にしたのはこちらのサイト。↓
![参考サイト(1)](https://github.com/nodejs/node-gyp)

Visual StudioをインストールしてC++ Build Toolsというものをいれます。
VS Codeとは別ものですので注意！（私も知りませんでした、、、、）
windowsならデフォルトでVisual Studio Installer というものが入っていると思いますので、
それを使ってインストールすると下記のスクショのようになります。インストールされている状態です。
インストールするときは、C++ Build Toolsも一緒にインストールしてあげてください。（あとから変更もできるので忘れても大丈夫！）

![sample1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630652/5ace1d5f-603a-fabb-e663-4d9eb834a2c1.png)


変更ボタンを押して、C++ Build Toolsがインストールされているかどうか確認してみましょう。
![sample2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630652/69721430-8ddf-5b20-defc-a9404cfef0a3.png)

問題なくインストールできています。
これでC++がインストールできたのか、、、、
もう少しです笑

今度は"C:\Users\your user name\"のディレクトリ配下にbinding.gypという
ファイルを作成し下記のコードを入れます。

```:Python
{
    "targets": [{
        "target_name": "binding",
        "sources": [ "binding.vcxproj" ]
    }]
}
```
参考サイトの"sources"と若干違うのは、VS C++ Build Toolsにパスを通してあげる形にするのがミソ！！

そのあとはコマンドプロンプトを起動し下記のコマンドを打っていきます。

```
$ node-gyp configure
↓
$ node-gyp configure --msvs_version=2019※すでに2019年版をインストールしていたため、2019指定です。
↓
$ node-gyp build
```

##エラーは頑張って対処するしかない。
エンジニアになって半年近くたちますが、いまだに辛いのが今回のようなエラーが起こった時。
でも最初は、俺嫌われてんのかと思いました。センスないんかなって(笑)
でも実は、みんな困ってる！(笑)
しっかりエラーを読み解いていくと私のような素人に毛が生えたようなエンジニアでもなんとか対処できました。
こういうチュートリアルが意外となかったので、
誰かの役に立てばと思い載せさせていただきました。
駆け出しエンジニアの皆さん！あきらめず一緒に頑張りましょう★
AWS用語集第二回(VPC, サブネット)

# はじめに
<a href="https://qiita.com/shuntagami23/items/0357677d7216efcaca3b">第一回</a>の記事ではデータセンターの単位を`AZ（アベイラリティゾーン）`といい、`AZ`を束ねて`リージョン`という内容でした。`リージョン`には`VPC`という仮想ネットワークを立てることができて、例えば`ap-northeast-1 (東京リージョン)`を選択すると`ap-northeast-1a`、`ap-northeast-1c`、`ap-northeast-1d`の3つ全てのリージョン（データセンター）を使用することが可能になるという話までしました。（画像のイメージ）今回はその`VPC`とそれを分割した`サブネット`について深堀していきます！

![スクリーンショット 2020-12-12 13.32.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694973/536b20a7-2dba-3c1f-d025-0d8b0ba95e3f.png)



# VPCとは
AWS内部のネットワーク環境のことです。AWSアカウントを作成するとデフォルトでひとつVPCが付与されます。`デフォルトVPC`以外にも自分専用のセキュリティ空間として`VPC`を作成することができます。その空間に入ってくる通信を許可するのか、拒否するのかといったセキュリティ面のサービスも含まれます。つまり、`VPC`はAWSサービスを使ってネットワークを作る上での土台になる部分です。

# サブネットとは
`VPC`を小さく分けたネットワークが`サブネット`です。これはセキュリティレベルを高めるためです。`サブネット`の中に`サーバー`を立て、`サーバー`がルールに基づいて外部と通信することでアプリケーションとしての機能を果たせる訳です。`サブネット`は`AZ`を跨ぐことができないので一つの`AZ`を選択して作成します。一つの`AZ`に複数の`サブネット`を配置することは可能です。また、`サブネット`から`インターネットゲートウェイ`へのルーティングの有無で`パブリックサブネット`, `プライベートサブネット`と呼び方が変わりますが今回は深堀はしません。以下の図の様なイメージです。
![スクリーンショット 2020-12-12 13.38.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694973/7d9ba9fc-9564-245b-5855-0ac4eb5cfa05.png)

# IPアドレス
`VPC`を使用する際は`IPアドレス`の範囲を指定する必要があります。この`IPアドレス`はサーバーやクライアント、ネットワーク機器を接続するのに十分な数を確保する必要があります。`VPC`のアドレスレンジ帯は`/16`〜`/28`の範囲内と決められています。`/16`や`/28`というのは`サイダー表記`という`IPアドレス`の範囲を示す方法で、詳しい説明は割愛しますが、例えば`/24`で区切った場合は`32ビット`の`IPアドレス`のうちの`24ビット`をネットワークアドレス部に割り振り、残りの`8ビット`をホスト部に割り当てることができるので、2**8=`256`個の`IPアドレス`を確保できることになります。（正確にはAWSの予約分が5つあるので251個となります）

# 具体例を見てみよう
`VPC`の`IPアドレス`を`/21`という大きいアドレスの範囲で確保して、それを`/24`の`サブネット`で分けていくということを考えます。このとき`VPC`で確保できる`IPアドレス`は2**11=`2048`個となり、一つの`サブネット`に割り振れる数は`256`個となります。つまりこの場合最大8個の`サブネット`が作成できるということになります。以下の図では8個のサブネットのうちの6個を使い、残り2つを予備として残しているといった設計になります。
![スクリーンショット 2020-12-12 14.48.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694973/f37f650f-86dc-a393-0026-427cfb7119a5.png)

# まとめ
今回は`VPC`と`サブネット`の構成について見てきました。次回、この`VPC`がインターネットとどのように接続するのか、およびそのための構成部品について説明していきたいと思います！
最初で最後にflashのアニメを作ってみた


Flushのサポートがついに終了するということで
人生初めてフラッシュのアニメを作ってみました。


Adobe Animateを使ったので楽でしたが
かなり雑な仕上がりになってしましました

![スクリーンショット 2020-12-12 14-59-27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68981/3821a1a2-81a5-8fd6-7518-ef9269a30594.png)


[雑なフラッシュアニメ](https://fir-intro-1006c.web.app/)


結局[ホームランダービー](https://kids.disney.co.jp/game/3018.html)はクリアできなかったなあ

ありがとうflush
テキストエリアのカーソル位置に画像を挿入したい

# はじめに
現在、社内向け（想定）の質問アプリケーションの開発を個人的に行っています。
[開発詳細](https://qiita.com/sasao3/items/5311d442699dba198bb0)

こちらのアプリケーションでは、マークダウン記法を用いることができます。
Qiitaのように、非同期で複数画像を投稿できるよう実装をしたのですが現状テキストエリアの文末に画像を挿入するような仕様になっています。
しかし、実際に使ってみて文章の途中に画像を挿入したいシーンもあり使いにくさを感じました。

そこで今回は、テキストエリアのカーソル位置を取得しそこに画像を挿入するような処理を実装したので学習メモとして残します。

#　流れ
流れとしては
１．対象の要素（今回はテキストエリア）の取得
２．テキストエリアの文字列を`substring()`でカーソル位置前後の２つの文字列に分割する（カーソル位置より前の文章とカーソル位置より後の文章）
３．あとは、上記の文字列の間に挿入したいもの（今回は画像URL）をいれる

# 実装
コードはこのようになりました。

```js
let image = "![画像](" + response + ")";

let target = document.getElementById('markdown_editor_textarea');

let beforeCursor = target.value.substring(0, target.selectionStart);
let afterCursor = target.value.substring(target.selectionStart);
let newText = beforeCursor + image + afterCursor;

target.value = newText; 
```

---

ひとつひとつ解説。

```js
let image = "![画像](" + response + ")";
```
こちらは、今回カーソル位置に挿入したい文字列になります。
今回、非同期処理で画像URLをサーバーから受け取っています。そのためこのresponseはAWS S3で保存した画像URLとなります。テキストエリアの横にプレビューがありそこで画像プレビューを表示させるためマークダウンの形式にしています。

---

```js
let target = document.getElementById('markdown_editor_textarea');
```
getElementByIdで対象のテキストエリアの要素を取得。
ここで、`getElementById`でできるなら`getElementsByClassName`でも出来るのかなと思いgetElementsByClassNameでやろうとしたらうまくいきませんでした（知識不足）。

てっきりgetElementsByClassNameはgetElementByIdのクラス版かと思っていましたが、返り値がそもそも違いました。

***◯getElementById***
対象要素が存在する場合はElementオブジェクトを返し、ない場合はnullを返す。
参考：[特定のID名から要素を取得](https://shanabrian.com/web/javascript/getelementbyid.php)
※Elementオブジェクトとは、HTML及びXHTMLの要素を表現するオブジェクト

***◯getElementsByClassName***
対象要素が存在する場合は、１個以上のHTMLCollectionを返し、内場合は0個のHTMLCollectionを返す。
参考：[特定のclass名（class属性値）から要素を取得](https://shanabrian.com/web/javascript/getelementsbyclassname.php)

getElementsByClassNameは名前がElements複数形である通り、配列のように複数返す。
確かにclassはidと違い同一ページに同じ属性値を使用できるため複数返すようになっているのかなと。

---

```js
let beforeCursor = target.value.substring(0, target.selectionStart);
let afterCursor = target.value.substring(target.selectionStart);
```

ここでは、テキストエリア内の文字列をカーソルの位置で分割している。
文字列の分割は`substring()`で行っている。
第一引数に分割の開始位置、第二引数に終了位置（指定しなければ文字列の最後まで）を指定できる。

※終了位置は含まれないという点に注意
例）

```js
const str = "hoge";
console.log(str.substring(0,2)); //出力：ho
```

`selectionStart`でテキストエリアのカーソル位置を取得しています。
selectionStartは、選択範囲の開始位置のオフセット（つまりテキストエリアの開始位置からカーソルまでの距離）を返す。
これでカーソル位置の前後で分割が出来ます。

---

```js
let newText = beforeCursor + image + afterCursor;
target.value = newText; 
```

分割した文字列の間に、挿入したい文字列をいれる。それをテキストエリアに上書きしてあげればOK。

# まとめ
今回は、テキストエリアのカーソル位置に画像を挿入するにあたり学習したことをまとめました。
まだJavaScript部分では知識が圧倒的に足りていないので少しずつ理解を深めていきたいと思います。
Qiita organizationページの更新をSlackで通知させる【bot作成編】

# やりたいこと
「[Qiita organizationページの更新をSlackで通知させる](https://qiita.com/Hiroki1928/items/dfc7d5782875de83189d)」で紹介した通り、SlackのRSSアプリがイケていなかったので、Qiita organazationからSlackへ投稿したい情報をパース / 投稿するbotを作る。

# 仕様
bot仕様を掻い摘んで説明します。

- RSSフィールド取得先URLは以下の通りとする  
  `http://qiita.com/organizations/<organization名>/activities.atom`
  - 前回の投稿通り、organazationページからatomを取得します
- 1時間毎にatomをパースし、メッセージを投稿する
  - atomの確認頻度は1時間毎とします
  - 1時間の内、複数の投稿があった場合は記事毎にメッセージを投稿するようにします
  - よって、**直近1時間の投稿**をお知らせする形になります

# 実装
Slack botでメッセージを投稿する方法はいくらでも情報があるので、atomのパースに焦点を当てて紹介していきます。

```
import feedparser
from datetime import datetime as dt
import datetime
import logging

def get_atom():
    msg_arr = [] // botで投稿する文章は配列に格納し、1行ずつSlackへ投稿します
    d_atom = feedparser.parse('http://qiita.com/organizations/<organization名>/activities.atom')

    if len(d_atom['entries']) == 0:
        msg_arr.append("ERROR:atom is invalid")
        logging.error('atom is invalid')
        return msg_arr
    
    // 1時間前と現在の時刻をdatetimeで取得します
    from_time = dt.now() - datetime.timedelta(hours=1)
    to_time = dt.now()
    
    // ここからatomを1行ずつ確認していきます
    i = 0
    while i < len(d_atom):
        tdatetime = dt.strptime(d_atom['entries'][i]['published'], '%Y-%m-%dT%H:%M:%SZ')
        // 投稿された時間が1時間以内でない場合、以降の行も同様になるので、ループから抜けます
        if not from_time <= tdatetime <= to_time:
            break
        
        // テキストに挿入する情報を取得します
        author = d_atom['entries'][i]['author']
        published = d_atom['entries'][i]['published']
        link = d_atom['entries'][i]['link']
        title = d_atom['entries'][i]['title']

        // formatを使うのをサボったのですが、formatを使用した方が良いですね…
        msg = tdatetime.strftime("%Y-%m-%d %H:%M") + "に" + author + "さんの記事が投稿されました。\r\nタイトル：<" + link + "|" + title + ">"
        msg_arr.append(msg)
        i += 1

    return msg_arr
```

# 課題点
実際に動作確認をしてみたところ、**アドベンドカレンダーの公開を検知できない**という課題が見つかったので、今後対処してきたいです。

アドベンドカレンダーは7:00に公開されますが、どうやらatomのpublishedは**限定公開した時間**になってしまうようです。（で、合っていると思う…）
改良案として、**更新時間を変数に格納しておき、1時間おきに差分があった場合は更新を通知する**、という方法を考えています。
”データ”を軸としたキャリアへ進み始めた1年を振り返り

事業企画職からエンジニアに転職し1年が経ちました。
2020年は今後のキャリアについて改めて考え、模索する一年でしたが、[データラーニングギルド](https://data-learning.com/guild)、[データラーニングスクール](https://school.data-learning.com/)に参加させていただく中で、ある程度方向性を見いだせたかなと感じているので、自身での振り返りも兼ねてまとめていきます。

#自己紹介・経歴
理系の大学院（非情報系）を修了後、事業企画を3年、エンジニアを1年ほど経験しています。
####自動車部品メーカーにて新規事業企画・マーケティング（3年）
- 主に事業計画の作成、M&A、投資先の管理等の業務に従事
- 社内、社外調整に奔走・・・
- 事業企画をする中で、表面的な提案しかできないことに課題を感じ、「技術的知見を持って事業企画をしたい」と考え、エンジニアへの転職を決意

####SIerでの開発業務（現職）
- 主にCIツールを用いた自動評価環境構築業務に従事
- 最近はチームマネジメント、顧客折衝の割合が多め

#キャリアについての模索
今年のはじめ（1～3月頃）は、エンジニアに転職し、スキルがついていく実感がある中で、もともとの目標である「事業企画」の方向に進むためにどのように学び、活動していけばよいか悩んでいました。

####[データラーニングギルド](https://data-learning.com/guild)への参加（4月）
その中で、たまたまTwitterにて[データラーニングギルド](https://data-learning.com/guild)の案内が目に入り、参加。
そして、ギルド内で[データラーニングスクール](https://school.data-learning.com/)の案内をいただき、「機械学習について学べる + キャリアについて相談ができる」と考え、スクールにも2期生として参加させていただきました。

####[データラーニングスクール](https://school.data-learning.com/)（5～7月）を通して学んだこと
スクールでは、データサイエンスの基礎の部分を広く学べたことはもちろん、メンタリングをしていただく中で、「どのようにファーストキャリアを得るか、そのためにどのようなポートフォリオを作成すべきか」等、悩んでいた内容への（一旦の）答えを出すことができました。
一人で考えていてはその答えにたどり着かなかったので、本当に参加してよかったです。

####ポートフォリオの作成（8月～）
8月からは、ポートフォリオとして、データパイプライン、ダッシュボードの作成に取り組んでいます。
内容については次項にて。

#ポートフォリオ作成について
上述の通り、現在ポートフォリオの作成中です。
構成についてはある程度固まっており、それぞれのサービス等について学びながらコーディングをしています。

####やりたいこと
- Qiitam Twitterの各APIを用いて記事情報を取得
- Twitterでの拡散数と記事のlike数の相関を可視化
- その他、Qiitaの記事についての分析、可視化
- できるだけ低コストで実現

####フロー
AWSの各サービスを用いることをベースとしていますが、コスト面を考え、タスク管理（Airflow）はローカルPCのdocker内で構築しています。

![tet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/880652/127e00c2-5cf0-6fb3-6f85-b2d1d2c01325.png)

####作成の上で大事にしていること
- 自分がほしいと思えるものにする（課題設定）
- 応用ができるように基礎的な知識を疎かにしない

AWSやAirflowについての知識がほぼない中でのスタートでした。
上記のスキルはエンジニアとしてのスキルアップにおいても非常に重要であり、実装を急がずに基礎を学ぶことを意識しています。

#今後のキャリアについて
今年の経験から、「データサイエンス×事業企画」がキャリアの目標となりました。

最終的にはPMのような立ち振る舞いができる人材を目指していますが、自身の経歴と親和性の高い"データエンジニアリング力"を伸ばすことを目標とし、データエンジニアとしての転職を考えています。
現職での業務＋独学を通してデータエンジニアリング力を伸ばしながら、転職活動を行っていく予定です。（21年4月～？）

#おわりに

2020年はキャリアについて考え、一歩を踏み出す一年にすることができました。
「データサイエンス×事業企画」がキャリアの目標になりましたが、まだまだ抽象的ですので、来年以降その部分を具体化していきたいです。

ご覧いただきありがとうございました。
FlutterのThemeを触ってみる

FlutterのThemeはアプリケーションの色や見た目を提供するものです。
子孫WidgetはThemeのThemeDataを使うことでその見た目を反映させたWidgetをビルドすることができます。

# DemoアプリのThemeを触ってみる
Flutter DemoアプリのThemeDataを少し変えてみます。

- `MaterialApp`の`theme`の`primarySwatch`をamberに追加
- `MaterialApp`の`theme`に`buttonColor`を追加
- `_MyHomePageState`の`Text`のtextThemeを`headline1`に変更

## 実際のコード
```dart
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        // primarySwatchをamberに変更
        primarySwatch: Colors.amber,
        visualDensity: VisualDensity.adaptivePlatformDensity,
        buttonColor: Colors.lightBlue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}
```

```dart
// _MyHomePageState 抜粋
body: Center(
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
    children: <Widget>[
      Text(
        'You have pushed the button this many times:',
      ),
      Text(
        '$_counter',
        style: Theme.of(context).textTheme.headline1,
      ),
      RaisedButton(
        child: Text('Button'),
        onPressed: () {},
      ),
    ],
  ),
),
```

## 見た目の変更

- primaryカラーがamberになりました
- Buttonの色がlightBlueとして設定されました
- countを表す文字が大きくなりました

<img width=400, src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779879/bb64deed-bfe9-4d28-34ef-103fc4ece6b7.png">


# ダークテーマの使用

`MaterialApp`の`theme`をdarkにするとダークモードがデフォルトになります。
OS設定のダークモード時に適用する場合は、`MaterialApp`の`darkTheme`属性にThemeDataを設定します。

例えば以下のように記述すると、通常テーマ(ライトテーマ)とダークモードが逆になります。

```dart
// StatelessWidget
theme: ThemeData.dark(),
darkTheme:ThemeData.light(),
```


## ダークテーマが反映された例

<img width=400, src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/779879/4088357a-72ed-bbf8-4d25-34f456985192.png">


# 公式ドキュメント

[公式ドキュメント](https://api.flutter.dev/flutter/material/ThemeData-class.html)にThemeDataのプロパティが記載されいます。かなり細かく設定できるようです。
これを活用することで、Widgetツリーの複数箇所に見た目の設定コードが分散するという煩雑さを避けることができると思います。
Nokogiri でクラス名を変更する

The 自分用メモ🔖

### やりたいこと
`h1`タグの中にある`span`タグのクラス名を既存のものからすべて`number`に変更したい！

### やったこと
`Nokogiri`を使ってギコします！

```ruby

require 'nokogiri'

# 👇ここは環境に合わせて上手く読み込ませてください
html = Nokogiri::HTML(File.read("ファイル名.html"))

html.search('h1 span').each do |span|
  span['class'] = 'number'
end
```

### 参考にしたもの
👇 痒いところに手が届きそうなサンプルコードがシンプルにまとまっていて、大変重宝しました✨

- [RubyでWebスクレイピング](https://yhara.jp/RubyScraping)
【JS】初期値を設定した要素数n個のArrayを生成する

`fill` を使うとよいです。
例えば、要素数12で全て`0`を設定したArrayを生成するには以下のようにします。

```js

> Array(12).fill(0)
  => [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
```

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
Active Adminの検索条件にenum値が反映されない時の対処方法

Active Adminの検索条件にenum値が反映されない現象が発生する。

## 現象詳細
enum値は以下の通り。

```
enum status: { not_started: 0, in_progress: 1, done: 9 }
```
Active Admin内のfilter設定は以下の通り。(enum値の日本語化対応も込み)

```
filter :status, as: :select, collection:Inquiry.statuses_i18n.invert
```

この時に検索条件にenum値の選択項目が表示されるものの、絞り込みが動作しない現象が発生する。
![スクリーンショット 2020-12-12 14.15.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680628/c2ad79b9-9c47-e0bf-0707-10167c2c4088.png)

## 原因
activeadminの検索にはransack gemを使用してが、ransackがうまくenumに対応できていないことが原因。
以下のように変更すると正しく動作する。

```
filter :status, as: :select, collection:Inquiry.statuses_i18n.invert.map{ |k, v| [k, Inquiry.statuses[v]]}
```

## 原因詳細

```
filter :status, as: :select, collection:Inquiry.statuses_i18n.invert
```

上記フィルターを指定した際に、サーバへ送付されるパラメータは以下のようになる。

```
{"q"=>{"status_eq"=>"in_progress"}, "commit"=>"絞り込む", "order"=>"id_desc"}
```
これを受けて、本来は、in_prgressに対応した1で検索をしてほしいが、実際のSQLを見ると以下のように'status'=0となっている。

```
SELECT COUNT(*) FROM (SELECT 1 AS one FROM `inquiries` WHERE `inquiries`.`status` = 0 LIMIT 30 OFFSET 0) subquery_for_count
```

つまりransack側でin_progress → 1という変換を期待してたがenumの値をとってくれず、
数値にカラムに文字列を指定したので、0が指定された。

## 解決方法
filterに指定したcollection:以降の部分は、コンソールで叩くと以下のようになる。

```
>Inquiry.statuses_i18n.invert

{"未着手"=>"not_started", "進行中"=>"in_progress", "完了"=>"done"}
```

collectionではkeyが表示内容、valueがサーバへ送信される値なので、ハッシュを以下のような形にできれば、ransackで正しく検索できる様になる。

```
{"未着手"=>0, "進行中"=>1, "完了"=>9}
```

そのため、collectionに読み込ませる値を以下のようにすると、ransackでも読み取れる値にできる（arrayでもcollectionは問題なく読み込んでくれる模様）

```
Inquiry.statuses_i18n.invert.map{ |key, value| [key, Inquiry.statuses[value]]}
> [["未着手", 0], ["進行中", 1], ["完了", 9]]
```

参照：RansackはRailsのenumに対応していないっぽい
https://www.tom08.net/entry/2016/12/05/121746
.zshrcを晒す

この記事は、[上智大学エレクトロニクス研究部Advent Calendar](https://qiita.com/advent-calendar/2020/sophia_ele_lab)第12日目の記事です。

## tl;dr

この記事に書いてある内容は、GitHubでも公開しています。以下のコマンドでインストールできるので、長くて読めないよ！という方はこちらを試してみてください。

```bash
cd ~/Documents
git clone git@github.com:Tsutomu-Ikeda/settings
cd settings/unix/setup
make install
```

## なぜシェルに `zsh` を使うか

開発をしていて触れる時間が長いもので一二を争うものといえばシェルです。 `bash` や `fish`、 `xonsh` など様々なシェルに触れてきましたが、僕が一番オススメするのは `zsh` です。なぜなら、一番カスタマイズが行いやすく、有志によるツールの開発も活発で、かつMacOSに標準でインストールされているからです。

## `.zshrc` の中身

`zsh` でカスタマイズしている中でおすすめの設定をいくつか紹介します。全体を見たい方は [GitHubの .zshrc](https://github.com/Tsutomu-Ikeda/settings/blob/master/unix/home/.zshrc) をご覧ください。

### setopt関連

```zsh
setopt auto_param_slash  # ディレクトリ名を補完したとき自動的にスラッシュを付ける
setopt mark_dirs
setopt list_types
setopt auto_menu
setopt auto_param_keys
setopt interactive_comments
setopt magic_equal_subst
setopt print_eight_bit  # 日本語を表示できるようにする
setopt hist_ignore_space  # コマンド先頭が空白文字の場合履歴から除外する
setopt hist_reduce_blanks  # 連続した空白文字をひとつにする
setopt hist_ignore_dups  # 同じコマンドが連続したとき、一回しか出力しないようにする
setopt hist_find_no_dups  # インクリメンタルサーチで同じコマンドが結果に出ないようにする
setopt complete_in_word  # カーソルの位置が単語の途中でも単語全体で補完を行うようにする
setopt always_last_prompt  # 補完候補をその場で表示し、選択し終わったら画面から消すようにする
setopt nonomatch  # 補完失敗時のエラーを出さないようにする
```

### alias関連

```zsh
alias rebirth='echo Reincarnate zsh... ; rm -rfv ~/.antigen/ ; [ ! -d ~/.antigen ] && mkdir -p ~/.antigen && curl -L git.io/antigen > ~/.antigen/antigen.zsh; source ~/.zshrc'
alias nconsole='tmux new-s -s Console'
# 設定を反映するためのエイリアス
alias restart=". ~/.zshrc"
# 長いコマンドの省略
alias docker-compose-dev='docker-compose -f docker-compose.dev.yml'
# 日本語のファイル名でも表示できるようにする
alias tree="tree -N"
# 色付き出力に対応する
alias less="less -R"
# ls関連、h: ファイルサイズをKB, GB表示する。glsはGNU版のlsをMacにインストールした場合
alias ls='gls --color'
alias ll='gls -lh --color'
alias la='gls -lah --color'
# ファイル削除をゴミ箱に移動に変える。 `brew install macos-trash` が必要
alias rm="trash"
```

### antigenモジュール関連

`fish` の使い心地の良さを再現するために色々入れています。

```
zsh-users/zsh-syntax-highlighting  # コマンドの打ち間違いを分かりやすくする(存在しないコマンドが赤くなる)
zsh-users/zsh-autosuggestions
zsh-users/zsh-completions
zsh-users/zsh-history-substring-search  # インクリメンタルサーチをコマンド入力後上矢印を押すことでできるようにする
```

### キーバインド関連

```
bindkey -e
bindkey "^?"    backward-delete-char
bindkey "^U"    backward-kill-line

bindkey '^[[Z' reverse-menu-complete

bindkey '^[[A' history-substring-search-up
bindkey '^[[B' history-substring-search-down
bindkey "$terminfo[kcuu1]" history-substring-search-up
bindkey "$terminfo[kcud1]" history-substring-search-down

bindkey '^[[L' forward-word

bindkey '[C' forward-word
bindkey '[D' backward-word
```

### `cdpath` の設定

ホームディレクトリ、ドキュメントディレクトリ、開発ディレクトリ(GitHubのソースを落としてきたときに使うディレクトリ)直下のディレクトリに素早く移動できるようにします。

```bash
cdpath=($HOME $HOME/Documents $HOME/Development $cdpath)
```
diffコマンドでファイルを使わず文字列を比較する方法

diffコマンドは引数にファイルを指定する必要があります。よって、diffしたいときは、diffしたい文字列をファイルに出力しなければなりません。しかし、場合によっては、ファイルに書き出すまでもないような処理や、なるべくファイルに書き出したくない場合もあると思います。そんなときは、プロセス展開(<()と記述)を使って次のように記載します。

```
#!/bin/bash

str1=aaa
str2=bbb

diff <(echo ${str1}) <(echo ${str2})
```
このプロセス展開、知っておくといろいろ便利です。ただし、ちょっとした注意事項があり、自分はこれに引っかかってsyntax errorになってしばらく詰まってしまいました。詳細はこちらの記事に書いておきますので、よろしければご覧ください。

[Bashのスクリプトをshで実行するとsh互換のBashとして動作した](http://tagfa.wpx.jp/archives/787)
[cx_Oracle入門](第19回) cx_Oracle8.1リリース 

# 連載目次
[連載：cx_Oracle入門　目次](https://qiita.com/nakaie/items/df170c3fa6895cf1b44d)


# cx_Oracle8.1リリース 
cx_Oracle 8.1がリリースされました。
[以前のリリースの記事](https://qiita.com/nakaie/items/c416b355c1ad5c99f36a)以降解説をすっ飛ばした8.0.1も含め、新機能、変更点などを解説します。

### 1. 利用ODPI-Cのバージョンの変更(8.0.1/8.1)
8.0はODPI-C 4.0.1をベースに開発されていました。8.0.1では4.0.2を、8.1では4.1.0をベースに開発されています。

### 2. 39～40桁のNUMBER型のハンドリングの不具合の修正(8.0.1)
cx_Oracleといよりは元となるODPI-Cの不具合の模様ですが、39～40桁のNUMBER型をcx_Oracleでやり取りする際に「cx_Oracle.DatabaseError: DPI-1044: value cannot be represented as an Oracle number」というエラーになる問題が発生していたのが修正されました。

以下、「NUMBER型って最大38桁じゃないの?」という疑問を持った方への解説です。
桁数を指定していないNUMBER型は、最大38桁の**精度**の数値を格納することが可能です。もう少し言い換えると、桁数指定をしていないNUMBER型は38桁を超える数値もハンドリングすることが可能です。しかし、精度は最大38桁なので、値が正しく入るのは最初の38桁までです。100桁を例にすると、上から38桁目までは値が入っていますが、下62桁はゼロになります。

### 3. setup.pyの変更(8.0.1)
8.0でサポート対象外となったPython2系へのインストールがエラーになるよう修正されました。

### 4. サポート対象Pythonバージョンの変更(8.1)
8.0.1まではPython3.5以上となっていましたが、2020年9月30日にPython 3.5自体のサポート期間が終了したため、8.1では3.5への対応が廃止され、3.6以降の対応となりました。また、Python 3.9がサポートされました。

### 5. ネイティブJSON型への対応(8.1)
Oracle Databaseは12cよりJSONをサポートしていますが、格納データ型はVARCHAR2のような既存の可変長文字列型でした。21cより、ネイティブなJSON型がサポートされました。DBのデータ型の名称は、そのまんま「JSON」です。cx_Oracleのデータ型名は「cx_Oracle.DB_TYPE_JSON」です。「ネイティブ」といっているのは、MongoDBで言うBSONのように、内部的にバイナリ化して保存し、アクセス速度を高めているからです。Oracleなので「OSON」と言ったりします。
cx_Oracle8.1より21cのJSON型に対応しました。SQLアクセスだけではなく、SODA APIも対応しています。

### 6. 未対応OCI属性の取得API(8.1)
元々cx_Oracleで対応していないOCIの属性を取得、設定できるインターナルなメソッドが追加されました。ただし、インターナルですし、リリースノートには「These methods should only be used as directed by Oracle.」とあるので、あまり一般的に利用されることは想定していないようです。
ここでは「OCI」はOracle Cloud Infrastructureではなく、Oracle Call Interfaceのことです。Oracle Call InterfaceはOracle Databaseへの最下層のアクセスAPIです。cx_Oracleの元となるODPI-CがOCIを利用して作成されています。ODPI-CはOCIが提供するすべての機能を網羅してはいません。結果、ODPI-Cに依存しているcx_Oracleもその制限を受けます。その制限を、属性へのアクセスについては回避できる手段ができた、ということだと思われます。

### 7. Alex Henrie氏のコード品質改善のpull requestの受け入れ
この方、8.0の際にも貢献してくださっていますね。

### 8. setup.pyの改善(8.1, タイトルのみ)

### 9. prefetchrowの仕様変更(8.1)
REF_CURSOR型の変数が優先されるようになりました。

### 10. データ型のシリアライズが復活(8.1)
pickleモジュールを使用したcx_Oracleデータ型のシリアライズが再度できるようになりました(解釈に自信なし)

### 11. tox対応(8.1)
toxを使用したテストに対応しました。

### 12. その他
ドキュメントの改善が行われています。

# 参考リンク
[cx_Oracle 8.1に言及しているOracle社のブログ(英語)](https://blogs.oracle.com/opal/faster-json-with-python-cx_oracle-81-and-oracle-database-21s-new-oson-storage-format)
[cx_Oracle リリースノート(英語)](https://cx-oracle.readthedocs.io/en/latest/release_notes.html)
JavaScriptで文字列のゼロパディング（C言語でいう%02d）

`padStart` を使うとよいです。
第一引数で何桁まで埋めるか指定し、第二引数で埋める文字を指定します。

```js

> '1'.padStart(2, '0')
  => "01"
```

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
【Python3】.cim と .png の相互変換

https://mell0w-5phere.net/jaded5phere/2020/04/22/convert-cim/ の逆版も書いて合わせました。

```python
import zlib
from PIL import Image

formats = {
    3: "RGB",
    4: "RGBA",
}

formats_return = {
    "RGB":3,
    "RGBA":4
}

def cim_to_png(cim_dir, png_dir):
    with open(cim_dir, 'rb') as f:
        b = zlib.decompress(f.read())
    im = Image.frombytes(formats[int.from_bytes(b[8:12], "big")], (int.from_bytes(b[:4], "big"), int.from_bytes(b[4:8], "big"),), b[12:])
    with open(png_dir, "wb") as f:
        im.save(f, format="png")


def png_to_cim(png_dir, cim_dir):
    with open(png_dir, "rb") as f:
        img = Image.open(f)
        b = b''.join([img.width.to_bytes(4, 'big'), img.height.to_bytes(4, 'big'), formats_return[img.mode].to_bytes(4, 'big'), img.tobytes()])
    with open(cim_dir, "wb") as f:
        f.write(zlib.compress(b))
```
バイナリの統合とかで少し詰まったので備忘録がてら。cimファイルの変換方法とか書いてあるサイトが少なすぎたので同じ目的で困ってる人が減るといいなあと思いました。
【備忘録】Swift 実践入門について　第10章

Swift 実践入門のまとめ。
分からない部分の抜粋も記載し、解決できたら随時更新していきます。
なお、ここに記載している以外でも「わけわからん…」となっている部分も多々ありますが、
今は必要ない、と言い聞かせて飛ばしています。

第10章
ジェネリスク　IntやStringといった型を自分で作ることができる。
理解度：70％くらいか

関数や型に<T>とつけることで型Tを作れる。Tがよく使われているが、任意の文字でOK。
func Equal<T : Equatable>(_ x : T, _ y : T) -> Bool {
return x == y
}

Equal(“abc”, “abc”)  //true
Equal(1.01, 1.02)  //false
Eual(true, true)  //true

<>の中の型名と、()の中の型名を一致させることで様々な型を柔軟に使うことができる。

10-2
汎用的である点でAny型と似てはいるが、Any型は型の情報を失ってしまうのに対して、ジェネリック型の場合はStringやIntの情報を残している。Any型の場合はダウンキャストが必要になる。