OmniAICreator/Qiita-1.07M huggingface dataset:
django-environを使ってSECRET_KEYを.envから読み込む方法

# 目的
djangoのsettings.pyにはSECRET_KEYやDBのパスワードなど重要で外部に公開したくないものがあるはず。

そこでそれらを.envファイルに記述し、その.envファイルをgitignoreに追加することで、外部からはそのキーを見れないようにしようと思う。


# 概要
django-environを使って、.envファイルを読み込む

# 実装
[django-environ公式](https://django-environ.readthedocs.io/en/latest/)にあるように実装していく。

### pipでdjango-environをインストール
```terminal
$ pip install django-environ
```

### 各ファイルを編集していく

settings.py

```settings.py
import environ

# instanceを作成
env = environ.Env(
    # 初期値を設定
    DEBUG=(bool, False)
)

# .envファイルのパスを指定するためにBASE_DIRをmanage.pyのある階層に指定
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# manage.pyのある階層にある.envを読み込む
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))

# .envのSECRET_KEYをSECRET_KEYに代入
SECRET_KEY = env('SECRET_KEY')
```
.envファイル

```python
SECRET_KEY = 'cecnecdeeincaliohejfioq3'
```



こうすることでsettings.pyに

```python
SECRET_KEY = 'cecnecdeeincaliohejfioq3'
```
が記述されたことになる。

ここでローカルサーバーを立ち上げて問題ないか確認してみる。

```terminal
$ python manage.py runserver
```

# 問題発生
```
django.core.exceptions.ImproperlyConfigured: Set the SECRET_KEY environment variable
```
!?!?!?!?何で！？


```SECRET_KEY = 'cecnecdeeincaliohejfioq3'```
このように = の周りにスペースを入れたくなるがこのスペースが入るとSECRET_KEYは読み込まれない。

そこで
```SECRET_KEY='cecnecdeeincaliohejfioq3'```
とすることで問題なくSECRET_KEYが読み込まれる。

ここで実は数時間詰まっていた。

# 参考文献



https://django-environ.readthedocs.io/en/latest/
私がコンテナを使ったdevopsを考えるにあたって何を検討したか

# 本記事について
既にコンテナを運用している方からしたら当たり前の事かもしれないです。

# アプリケーションのデータの保存場所
### 結論
クラウド上のRDSなどのPaaSを使うのが良い。
### 理由
スケーラビリティの担保&管理がシンプル
### 詳細
コンテナは可搬性に優れているがデータを永続的に持つ事は苦手である。
docker runでコンテナが作られる段階では、真っさらなデータになってしまう。
既にあるコンテナからイメージ化する事は可能だが、コードでの管理を放棄してしまっており、中身があるべき状態=もとのDockerfileのままである事を保証できない。
なのでvolumeを使って永続化を行う方法がまずある。
しかしvolumeを使う場合もローカルpcに入れるとスケールしない(トラフィックの増加に対応できない)ので、クラウド上のrdsや、s3など、永続化に強いコンポーネントと組み合わせて使う方が良い。
クラウドのPaaSをデータベース基盤に据える前提ありきであれば、ローカルDBからの移行の手間もあるので最初から最後までPaaSで行う方が好ましい。

https://12factor.net/backing-services

# 使用するコンテナオーケストレーター
### 結論
Amazon ECSを使用する
### 理由
AWSとの親和性がある&学習コストが比較的低い
### 詳細
ECSを採用するかKubernatesを採用するかEKSを採用するかで迷った。
私自身これらのいずれについても無知な状態から取り組み始めるにあたって、まずKubernatesの学習コストの高さについては気になって選択肢から外れた。
ECSとEKSの選択ではこちらの方のサイトが参考になった。

https://zenn.dev/yoshinori_satoh/articles/2021-02-13-eks-ecs-compare

今回だと、サービスの複雑度が低い事と料金コスト、学習コスト等の面でECSの採用が適正であると判断。


# ECSのネットワークモード
### 結論
awsvpcモードを使用する
### 理由
AWSリソースとの連携が容易
セキュリティグループの割り当てができることで柔軟な制御が可能であること
### 詳細
host,bridge,awsvpcのモードが存在する。
hostモードはその名の通りEC2ホストのENIを使うモード、
bridgeモードはdockerのデフォルトであるdefault0インターフェースを使うモード、
awsvpcはホストから独立したENIを使用するモード
になる。

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task-networking.html

# デプロイ方式
### 結論
ブルーグリーンデプロイを使用する
### 理由
アプリケーションのテストを検証・本番で2回行うのではなく1回で済ませる為。
### 詳細
ローリングアップデート、ブルーグリーンデプロイ、外部デプロイの3つの方法がある。
外部デプロイはあまり一般的でなく、情報も少ない。また、Auto Scalingがサポートされないという制約がある。ローリングアップデートがECSのデフォルトであり、ブルーグリーンデプロイに比べるとデプロイに時間がかかるが、リソースに無駄がない、設定がシンプルというメリットがある。ブルーグリーンデプロイはそれなりの設定が必要であり、即時性と可用性を担保できるので、停止が許されないシステム向けである。このような観点からローリングアップデートを基準として選択すべきと考えた。
しかし、採用しようとしている環境が開発環境、検証環境、本番環境という区分けでかつ検証環境と本番環境は環境変数以外は同一の構成としたいという希望があった。
ここで問題になるのが検証・本番に関してほぼ同じ構成であっても結合テストは2回行わなければいけないということがあった。
これを回避できる方法として、そもそも検証環境と本番環境ではなく、ブルー環境とグリーン環境として定義し、いずれかを本番トラフィックの向き先とする。アップデート時は本番トラフィックの向き先ではない環境にデプロイを行い、結合テストを実施後にトラフィックのスワップを実施することで、テストを二重に行う必要がなくなるということから、ブルーグリーンを採用した。

# Dockerfileを使うか、docker-composeを使うか
### 結論
Dockerfileをメインで使う
### 理由
バージョンや依存関係に直結する部分を管理しやすいから
### 詳細
Dockerを使う利点の一つはアプリケーションの実行環境の依存関係やバージョンをパッケージング出来ること。
Dockerfileとdocker-composeを比較すると、Dockerfileはミドルウェアのベースイメージ自体にバージョンが付与されており、イメージをカスタマイズする形でバージョン管理が実現できるのに対して、docker-composeを使ってバージョンや依存関係を管理するとそこからDockerfileが呼ばれるような形になるので階層が一つ深くなり管理の煩雑さが生まれたり、またそれにより厳密なコントロールがしづらくなる事が懸念された。
docker-composeの使途としてコンテナの起動順序の制御などもあるが、ECSにもこの機能はあり、こちらで実現可能であることを見込んでのこともあった。
【Flutter】Algoliaの基本的な実装方法

# はじめに

FlutterでAlgoliaを使用したのですが、英語の記事が多く苦労したので、まとめておきます。

本記事では、
**パッケージのインストール ～ 検索を実行し、その結果を取得**
を記載します。

# Algoliaの実装方法

### 使用するパッケージ

"algolia"というパッケージを使用します。

https://pub.dev/packages/algolia

#### pubspec.yaml

```yaml:pubspec.yaml
dependencies:
 algolia: ^1.0.4
```

### コード

#### アプリケーションIDおよびAPIキーの設定

```dart
Algolia.init(
      applicationId: 'YOUR_APPLICATION_ID',
      apiKey: 'YOUR_API_KEY',
    );
```

init()はstaticのため、以降で生成するインスタンス全てにおいて、
設定したアプリケーションIDおよびAPIキーが適用されます。

#### クエリの生成

```dart
AlgoliaQuery query = algolia.instance.index('contacts').query('john');
```

index()の引数には、指定したいindex名を、
query()の引数には、検索したいキーワードを、設定します。

#### クエリに検索条件を設定

```dart
// 最大取得件数を設定
query = query.setLength(100);
```

一例として、最大取得件数を設定しました。
他の検索条件を設定する場合でも、同じようにすればOKです。

##### <注意>

以下の書き方では、設定した事になりません。

```dart
// 最大取得件数を設定
query.setLength(100);
```

setLength()の戻り値(AlgoliaQuery)を、queryで受け取りましょう。

#### 検索を実行

```dart
AlgoliaQuerySnapshot snap = await query.getObjects();
```

#### 検索結果を見る

```dart
// 検索にヒットしたオブジェクトの数
snap.nbHits; // int

// 検索にヒットしたオブジェクトのリスト
snap.hits; // List<AlgoliaObjectSnapshot>
// 1件ずつ内容を見る
for (final hit in snap.hits) {
  // オブジェクトの内容は、Mapに格納されている
  final object = hit.data; // Map<String, dynamic>
}
```

### コードまとめ

今までのコードを、一つにまとめたコードです。

```dart
// アプリケーションIDとAPIキーの設定
Algolia.init(
      applicationId: 'YOUR_APPLICATION_ID',
      apiKey: 'YOUR_API_KEY',
    );

// クエリの生成
AlgoliaQuery query = algolia.instance.index('contacts').query('john');

// 検索条件を設定
query = query.setLength(100);

// 検索を実行
AlgoliaQuerySnapshot snap = await query.getObjects();

// 検索結果を見る
for (final hit in snap.hits) {
  final object = hit.data; // Map<String, dynamic>
}
```

# おわりに

分かってしまえば簡単なのですが、分かるまでが大変でした。。

今後、Flutterでalgoliaを使う方がいれば、参考にしていただければ幸いです。
では。
Webアプリケーション開発のお勉強 #6

# 1.この記事の内容

[Webアプリケーション開発のお勉強 #5](https://qiita.com/ryoma-jp/items/9283b1347327c3b3991c)まででブラウザからの操作による学習の開始・停止までを実行できるようにしました．

本記事では学習状態を可視化する為，Tensorboardとの連携方法を記載します．
コードは[著者のGithub](https://github.com/ryoma-jp/AI_Dashboard)のコミット[ade8e7df34c1da924c0a97ef103ffdba6592cf37](https://github.com/ryoma-jp/AI_Dashboard/commit/ade8e7df34c1da924c0a97ef103ffdba6592cf37#diff-4063d89c35a1753480fa80a62d99820d7751e63625b21757096d3cda0c42c0f8)です．

# 2.実装方法

学習開始時に```subprocess.Popen```でTensorboardを起動し，ページ上にTensorboardへのリンク（本実装例ではボタン）を配置するだけです．

# 2-1.Tensorboardの起動

log_dirはTensorFlowの学習ログを格納するディレクトリを指定します．
portは空いているポートを指定します．

Tensorboardが不要となった際にプロセスを閉じられるように，PIDをデータベースへ登録します(subproc_tensorboard.pid)．
※Tensorboardの終了処理は，記事執筆時点では未実装です．

```python
subproc_tensorboard = subprocess.Popen(['tensorboard', \
                            '--logdir', log_dir, \
                            '--port', port])
```

# 2-2.Tensorboardへのリンク

手元のPCで動かしている為，実装例ではホスト名はlocalhostです．
<port>にはTensorboard起動時に指定したポート番号を指定します．

```html
<a class="btn btn-primary mx-3" href="http://localhost:<port>/" role="button" target="_blank" rel="noopener noreferrer">Open Tensorboard</a>
```

![open_tensorboard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/983907/ebb50b78-8e27-3f03-4e5f-d41a49daef72.png)


# 3.さいごに

コマンド一つで可視化できる仕組みが増えていて便利ですね．

# 4.関連リンク

* [Webアプリケーション開発のお勉強　目次](https://qiita.com/ryoma-jp/items/1a0a438313dcb30ef6fc)
* [TensorBoard を使う](https://www.tensorflow.org/tensorboard/get_started)
* [HTMLでリンクを別タブ・別ウィンドウで開く方法と、脆弱性に関する注意](https://toragramming.com/web/html/html-link-target-blank/)
Webアプリケーション開発のお勉強 #5

#1.この記事の内容

[Webアプリケーション開発のお勉強 #4](https://qiita.com/ryoma-jp/items/ee6f49b5bd990a2aef15)まででTensorFlowでGPUを用いた学習ができるところまでを実装しました．
しかし，#4の時点ではブラウザ上の操作で学習を中断することができず，使い勝手が悪い状態でした．

本記事では，ブラウザ上の操作でTensorFlowの学習を停止する方法を記載します．
実装例は[筆者のGitHub](https://github.com/ryoma-jp/AI_Dashboard)に公開しています．コミットIDは[05c065df417c08d3033b454591b886b970364912](https://github.com/ryoma-jp/AI_Dashboard/commit/05c065df417c08d3033b454591b886b970364912)です．


#2.実装方法

実装のポイントは下記の3点で，各々次節以降で解説します．

* 学習処理のマルチプロセス化
* TensorFlowの学習をコマンド操作で停止する仕組みの実装
* ブラウザから学習プロセスに対して停止コマンドを送信

#2-1.学習処理のマルチプロセス化

Webアプリ(Djangoのワーカプロセス)から直接学習を実行すると，学習が完了するまでワーカプロセスがロックされ，Webページの応答性が悪くなります．
そこで，Webアプリから学習プロセスを分離し学習対象のモデルとPIDを関連付けてデータベースで管理することとしました．

![webapp-structure-modification.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/983907/ce9b31e5-9fe0-bb06-0011-854ead7b13f3.png)


#2-2.TensorFlowの学習をコマンド操作で停止する仕組みの実装

TensorFlow側の実装は下記の記事で掲載した通りの方法となります．
バッチ毎にカスタムコールバック内でFIFOをチェックし，文字列```stop```が書き込まれていれば学習を終了するように実装しました．

https://qiita.com/ryoma-jp/items/cbae4003d9033bc8dec1

#2-3.ブラウザから学習プロセスに対して停止コマンドを送信

今回は動作確認用に下記にFIFOを置くこととし，学習停止の文字列を```stop```としました．
ブラウザから停止ボタンをクリックすることでFIFOに```stop```を書き込み，学習を停止させます．

```python
def _stop_trainer():
    fifo = '/tmp/fifo_trainer_ctl'
    with open(fifo, 'w') as f:
        f.write('stop\n')
    return
```

#3.さいごに

TensorFlowの学習を外部プロセスから中断することに対して実装例が見つけられず，本記事に記載の方式を採用しました．
このあたりのスマートな実装方法があればコメントなどでご教示いただけますと幸いです．

なお，本記事の執筆時点では，ダッシュボードのレイアウトや状態管理はガバガバですが，この辺りも後々整備する予定です．

#4.関連リンク

* [コールバックを書く](https://www.tensorflow.org/guide/keras/custom_callback?hl=ja)
* [TensorFlowの学習(tf.keras.Model.fit)を任意のタイミングで抜ける方法](https://qiita.com/ryoma-jp/items/cbae4003d9033bc8dec1)
* [Webアプリケーション開発のお勉強　目次](https://qiita.com/ryoma-jp/items/1a0a438313dcb30ef6fc)
* [Webアプリケーション開発のお勉強 #4](https://qiita.com/ryoma-jp/items/ee6f49b5bd990a2aef15)
【React】アクセス制限設定方法

## はじめに
　本記事は、プログラミング初学者が、学習を進めていて疑問に思った点について調べた結果を備忘録も兼ねてまとめたものです。
　そのため、記事の内容に誤りが含まれている可能性があります。ご容赦ください。
　間違いを見つけた方は、お手数ですが、ご指摘いただけますと幸いです。

## アクセス制限

### グローバルstateの作成
```AuthProvider.jsx
import {createContext, useState} from 'react'

export const AuthContext = createContext();

export const AuthProvider = (props) => {
  const [loading, setLoading] = useState(true)
  const [isSignedIn, setIsSignedIn] = useState(false)
  const [currentUser, setCurrentUser] = useState()

  return (
    <AuthContext.Provider
      value={{
        loading,
        setLoading,
        isSignedIn,
        setIsSignedIn,
        currentUser,
        setCurrentUser
      }}
    >
      { props.children }
    </AuthContext.Provider>
  )
}
```

### グローバルstateを使用できるようにする
```App.jsx
import React from "react"
import "./App.css"
import { Router } from "../router/Router"
import { AuthProvider } from "../providers/AuthProvider"

export default function App() {
  return (
    <AuthProvider>
      <Router />
    </AuthProvider>
  )
}

```

### ルーティングの設定をする
```Router.jsx
import React, { useEffect, useContext } from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { Header } from '../components/Header';
import { MyPage } from '../components/MyPage';
import { Page404 } from '../components/Page404';
import { PageTodo } from '../components/PageTodo';
import { Record } from '../components/Record';
import { SignIn } from "../components/SignIn";
import { SignUp } from "../components/SignUp";
import { getCurrentUser } from "../lib/api/Auth";
import { AuthContext } from '../providers/AuthProvider';

export const Router = () => {
  const { loading, setLoading, isSignedIn, setIsSignedIn, setCurrentUser } = useContext(AuthContext)

  const handleGetCurrentUser = async () => {
    try {
      const res = await getCurrentUser();
      if (res?.data.isLogin === true) {
        setIsSignedIn(true);
        setCurrentUser(res?.data.data);
        console.log(res?.data.data);
        console.log(setIsSignedIn)
        console.log(res?.data.isLogin)
      } else {
        console.log('no current user');
      }
    } catch (err) {
      console.log(err);
    }
    setLoading(false);
  };

  useEffect(() =>{
    handleGetCurrentUser();
  }, [setCurrentUser])

  const PrivateRoute = ( {children} ) => {
    if (!loading) {
      console.log(isSignedIn)
      if (isSignedIn) {
        return children ;
      } else {
        console.log(isSignedIn)
        return <Navigate  to='/sign_in' />;
      }
    } else {
      return <></>;
    }
  };

  return (
    <BrowserRouter>
      <Routes>
        <Route path="sign_in" element={<SignIn />} />
        <Route path="sign_up" element={<SignUp />} />
        <Route path="mypage" element={<PrivateRoute><Header /><MyPage /></PrivateRoute>} />
        <Route path="todo" element={<PrivateRoute><Header /><PageTodo /></PrivateRoute>} />
        <Route path="record" element={<PrivateRoute><Header /><Record /></PrivateRoute>} />
        <Route path="*" element={<Page404 />} />
      </Routes>
    </BrowserRouter>
  )
}

```
iframeにPOSTでデータを送る

URL指定してiframe開くとき／開いたあととかに、POST形式でデータを送る方法について試したので、忘れないように書いておく
（GETならsrcのURLにクエリパラメータつけるだけで済むが、POSTはそうもいかない）

# TL;DR

・iframe作る （ `document.createElement` ）
→このiframeには必ず`name`つけておく
（・iframeにsrcでURLを指定する）
・form要素作る （ `document.createElement` ）
・form要素に`action`で送信対象URLを、`method`でPOSTを、`target`でiframeのnameを、それぞれ指定する
・input要素作る （ `document.createElement` ）
・input要素にて、POSTで送信するデータをkey-value形式でname, valueを指定する
・input要素をform要素の下に配置する（ `appendChild` ）
→複数データを送りたい場合は、このinput要素作成をデータの個数分だけ繰り返す
（・iframe要素、form要素をbody下に配置する （ `appendChild` ））
・form要素を送信（`.submit()`）する
→iframeのonloadに合わせて実行するようにすると割と安全っぽい
（・form要素をページ上から削除（`.remove()`）する）

```iframe_post.js
var frame = document.createElement("iframe");
frame.name = "receiveIframe";
frame.src = "https://hogehoge.ne.jp";

var form = document.createElement("form");
form.action = "https://hogehoge.ne.jp";  //POSTするデータの送信先URL
form.method = "POST";
form.target = "receiveIframe";  //上でiframeにつけたnameを指定

var sendData = document.createElement("input");
sendData.name = "price";
sendData.value = "180";
//これで、 price: 180 という感じのデータが送れる
form.appendChild(sendData);

//他にも送りたい場合は、データの数だけ繰り返す
//データの形式次第ではfor使ってもいいかも
/*var sendData1 = document.createElement("input");
sendData1.name = "version";
sendData1.value = "1.5";
form.appendChild(sendData1);*/
/*var sendData2 = document.createElement("input");
sendData2.name = "date";
sendData2.value = "2022/01/20";
form.appendChild(sendData2);*/

document.body.appendChild(frame);
document.body.appendChild(form);

var frameOnload = frame/onload = function () {
    form.submit();
    form.remove();
};
```

# 理屈

（素のJavaScriptだと）データをPOSTで送るには、基本`form`要素を`submit()`する形になるので、一時的な`form`を作り中身に送りたいデータ指定して、任意のiframeを対象にして`submit()`すればいいよね、って話

わかれば単純ですよね
【Flutter】ProviderをRiverpodに置き換える



業務にてFlutterでモバイル開発をしています。
最近ProviderをRiverpodに置き換える作業を行なったので共有します。

### 何が良いのか？
状態管理をアプリ全体で共有して使えることです。
Providerを使用していた頃はProviderを使用するWidgetの範囲を見定め、MultiProviderなどでラップしていました。

```dart
@override
Widget build(BuildContext context)
    return provider.MultiProvider(
      providers: [
        provider.ChangeNotifierProvider(create: (ctx) => Counter()),
      ],
    child:Scaffold()
```

しかしモーダル（showModalBottomSheet）を表示する際に、ある問題がおきます。

```dart
 await showModalBottomSheet(
      context: _context,
      useRootNavigator: true, // コレ！！！
      builder: (BuildContext context) {
          final count = context.watch<Counter>.count
          // 続く
```

ウィジェットツリーで親ではなくルート（MaretialAppなどその辺）をみてしまうんですね。
そうするとProviderが見つかりません、とエラーが吐かれます。
MultiProviderでラップする場所を変更せねば...という事態になります。

その点Riverpodではアプリの一番高いレイヤーで宣言し、グローバルで使用できるのでそんな問題は起きません。

```dart
void main() {
  runApp(
    // 本当に一番上
    ProviderScope(
      child: MyApp(),
    ),
  );
}

```

そしてどこでも状態にアクセスすることができます。

```dart:provider.dart
final counterProvider = StateNotifierProvider<Counter, int>((ref) {
  return Counter(ref);
});
```

```dart:home.dart
class HomeView extends ConsumerWidget {
  const HomeView({Key? key}): super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // どこでも呼べる！！！
    final counter = ref.watch(counterProvider);
    return Text('$counter');
  }
}
```

ツリーのことを気にする必要がなくなりました。
Riverpodの良さはまだまだあるので、今後も共有していきます。
【知識】ファイルに関する学習

ファイルとは
-
データやプログラムを扱うときのひとかたまりの単位。
コンピュータ上の情報はすべてファイルとして保存される。

ファイルの種類1
-
**データファイル:**
文書ファイルや画像ファイルなど、プログラムで表示、編集などするためのデータが格納されたファイルのこと

**システムファイル:**
コンピュータのストレージ（外部記憶装置）に保存されているファイルのうち、OS（オペレーティングシステム）の動作に必要不可欠なもの。

**プログラムファイル:**
コンピュータのストレージ（外部記憶装置）に保存されるファイルの種類の一つで、コンピュータで実行可能なプログラムが格納されているもの。

ファイルの種類2
-
**テキストファイル**
文字や記号のみのテキストデータで作られているファイルを、テキストファイルといいます。メモ帳やテキストエディタで作成したファイル。
例:
txtファイル、CSVファイル、XMLファイル、HTMLファイル、GPXファイル、KMLファイル、GeoJSONファイルetc...

**バイナリファイル**
テキストファイル以外のファイルはすべてバイナリファイルであり、実行形式のプログラム、画像、音声、動画などのファイルの多くが該当する。
例:
Microsoft Ofiiceのファイル、PDFファイル、JPGファイル、MP3ファイル、MP4ファイルetc...
>これらのファイルは、JPG、PDFなど種別を表す拡張子が付いていますが、その拡張子に対応したアプリケーションで開いたり編集したりします。

補足
-
**実行ファイル**
プログラムの起動に関わるファイルで、プログラムファイル、バイナリーファイルになります。
実行形式ファイル、exeファイル、エグゼファイルともいい、プログラミング言語で記述されているファイルです。クリックするとプログラムが起動したり、ウィザード形式によってプログラムのインストールが始まります。
【Vue.js】classのバインドはシングルクォートで囲っておく

#CSSのバインドで3分困った

久しぶりにVue触ったら、CSSが適用されない自体が発生

```html
<p :class="{text-red: isRed, newItem: isNew}">こんにちは</p>
```

text-redのCSSが反映されないのは、CSSクラス名に'-'が含まれている場合はシングルクォートで囲わないと認識しないため

```html
<p :class="{'text-red': isRed, newItem: isNew}">こんにちは</p>
```

これで正常に動く
CSSのクラス名記法が混在するのは仕方ないので、もう全部シングルクォートで囲っておけばいい気がした。
Pandasのgroupbyで日付の順序がおかしくなる問題を解決する。

#結論

作成したDataFrameの日付のフォーマットや型に問題がある！

**→ 日付のフォーマットは、1/1は01/01の様に記述するなど統一されているか？**
**→ 日付のデータの型は、to_datetime関数を使うなどして日付の型になっているか？**

#DataFrameを作成し、groupbyのsumで日付別の合計値を求める。(ダメな例)

下記の様な日付ごとに果物をいくつ買ったかというDataFrameがあるとする。

```python:
import pandas as pd

df = pd.DataFrame({'Date': ['2022/9/1', '2022/9/1', '2022/9/12', '2022/9/12','2022/10/20', '2022/10/20', '2022/11/27', '2022/12/31'],
                'Fruit': ['Apple', 'Banana', 'Apple', 'Orange', 'Grape', 'Apple', 'Grape', 'Orange'],
                'Sale': [3, 1, 2, 7, 3, 1, 8, 4]})
```

実際のDataFrameは以下の様な感じ。

|  | Date | Fruit | Sale | 
| :-: | :-: | :-: | :-: | 
| 0 | 2022/9/1 | Apple | 3 |
| 1 | 2022/9/1 | Banana | 1 |
| 2 | 2022/9/12 | Apple | 2 |
| 3 | 2022/9/12 | Orange | 7 |
| 4 | 2022/10/20 | Grape | 3 |
| 5 | 2022/10/20 | Apple | 1 |
| 6 | 2022/11/27 | Grape | 8 |
| 7 | 2022/12/31 | Orange | 4 |

このDataFrameから、日付別で果物合計いくつ購入したかを以下の様なコードで算出。

```python:
df.groupby(by=['Date']).sum()
```

しかし、結果として`Date`の順序がおかしくなる。

| Date | Sale |   
| :-: | :-: | 
| 2022/10/20 | 4 |
| 2022/11/27 | 8 |
| 2022/12/31 | 4 |
| 2022/9/1 | 4 |
| 2022/9/12 | 9 |

本来やりたいのはこっち⇩

| Date | Sale | 
| :-: | :-: | 
| 2022/9/1 | 4 |
| 2022/9/12 | 9 |
| 2022/10/20 | 4 |
| 2022/11/27 | 8 |
| 2022/12/31 | 4 |

何故こうなってしまうのか？

##原因： Dateがstr型として扱われている。

以下のように`Date`列の各々の要素が`str型`であるので、`groupby`メソッドを`Date`に対して使用した結果、`str型`の順にsortされるといった現象が起こっている。

```python:
type(df.iat[0,0])
>>> str
```

これにより`2022/9/1`と`2022/9/12`のsort順がおかしくなる現象が発生している。


#解決法①： Date列にto_datetime関数を利用し、str型から日付データに変換する。

DataFrameの`Date`列に対し、以下のコードを実行する事で、`str`型から`Timestamp`型に変更できる。

```python:
df["Date"] = pd.to_datetime(df["Date"])
```

上記コードを実行した後`groupby`すれば、以下の様なDataFrameが作成される。

| Date | Sale | 
| :-: | :-: | 
| 2022-09-01 | 4 |
| 202209-12 | 9 |
| 2022-10-20 | 4 |
| 2022-11-27 | 8 |
| 2022-12-31 | 4 |

こちらの方法だと、型を変更する事で日付のフォーマットを統一することができる。

#解決法②： groupbyメソッドを使用した際に、sortせずに元の順序を維持する。

何らかの事情で、元のフォーマットや型をいじりたくない人には、こちらを使って欲しい。
元の順序を維持するには、`.groupby(..., sort=False)`とする事で解決する。

```python:
df.groupby(by=['Date'], sort=False).sum()
```

結果、以下の様なDataFrameが作成される。

| Date | Sale | 
| :-: | :-: | 
| 2022/9/1 | 4 |
| 2022/9/12 | 9 |
| 2022/10/20 | 4 |
| 2022/11/27 | 8 |
| 2022/12/31 | 4 |


ニッチな領域だと思うが、誰かの役に立てれば幸いである。
OSSECについて学び始めた その５

#はじめに
ふとセキュリティについて何か学習を始めたいと思いました。オープンソースのプログラムを解読しながら、理解を深めていきたいと思います。
まずはオープンソースのOSSECについて学習します。まずはドキュメントを読むことから始めます。
https://www.ossec.net/docs/

今回は第五弾としまして、OSSECのAgentsについて読んでいきます。
https://www.ossec.net/docs/docs/manual/agent/index.html

#Agent
OSSECのエージェントには、
・インストール型エージェント
・エージェントレス型エージェント
の2種類が存在します。

#####インストール型エージェント
ホスト上にインストールされ、OSSEC暗号化メッセージプロトコルを介して中央のOSSECサーバーにレポートバックします。
#####エージェントレスエージェント
リモートホストにインストールする必要はありません。エージェントは、OSSECマネージャから起動されるプロセスで、リモートシステムから情報を収集し、任意のRPCメソッド（例：ssh、snmp rdp、wmi）を使用します。

##Agent
###Communication between agents and the OSSEC server
エージェントとOSSECサーバー間の通信は、一般的にセキュアモードではポート1514/udpで行われます。ossec-remotedでsyslogモードを使用する場合、ポート514がデフォルトとなります（UDPとTCPの両方がサポートされています）。これらのポートは、ossec.conf の remote セクションで設定可能である。

一般的には、syslogよりもセキュアな接続方式が好まれます。また、ossec-remoted の syslog サポートの代わりに、外部の syslog デーモン (rsyslog や syslog-ng など) を使用することも可能です。
###Managing Agents
manage_agentsを使用してOSSECマネージャにエージェントを追加するには、以下の手順で行う必要があります。
1.OSSECサーバーでmanage_agentsを実行する。
2.エージェントを追加する。
3.エージェントのキーを抽出する。
4.そのキーをエージェントにコピーする。
5.エージェント上でmanage_agentsを実行します。
6.マネージャーからコピーした鍵をインポートします。
7.マネージャのOSSECプロセスを再起動します。
8.エージェントを起動します。
###Agent systems behind NAT or with dynamic IPs (DHCP)
固定IPアドレスのないシステムやNATデバイスの背後にあるシステムにエージェントをインストールしたい場合、CIDRアドレスまたは任意のIPアドレスを使用してエージェントを設定する必要があります。
###Adding an agent with ossec-authd
自動化された方法で鍵を追加することは可能である。ossec-authdとagent-authはこの機能を提供する。
###Centralized agent configuration
リモートでエージェントを設定できるようにしたいとお考えでしたら、バージョン2.1からはそれが可能になりますので、ご満足いただけると思います。ファイル整合性チェック(syscheckd)、ルートキット検出(rootcheck)、ログ解析のための一元的な設定が可能になりました。

##Agentless
###Agentless Monitoring
エージェントレス監視では、エージェントがインストールされていないシステム（ルータ、ファイアウォール、スイッチ、Linux/BSDシステムも含む）で整合性チェックを実行することができます。通常のファイル整合性チェック（チェックサム変更の警告）と同じように実行したり、差分を取って何が変更されたかを正確に表示したりすることが可能です。
・Agentless configuration options
・Getting started with agentless
・Configuring agentless
・Running the completed setup
・Alerts
###Writing Agentless Scripts
OSSECエージェントレスセキュリティモニタリングで動作するすべてのスクリプトは、OSSECサーバーへの通信と報告にstdoutを使用します。このため、OSSEC用のスクリプトは、stdoutへのprintやecho以上のことをする必要がないため、簡単に書くことができます。出力の形式は OSSEC の仕様に合わせる必要がありますが、それは非常に簡単なことです。
・Agentless Script Types
・Agentless Script: ssh_integrity_check_linux
・Modifying to make own Agentless Script: ssh_dmz_linux

設定回りのスクリプトが記載されています。ソースコード解析と設定スクリプトを理解していきたいと考えます。

#おわりに
今回は以上です。では、また。
フリーランスがサラリーマンの1.5倍の月単価で契約する必要がある理由を計算で出した。100万/月（フリ） = 820万/年（サラ）

# なぜ計算しようと思ったか？
ちまたにあふれているフリーランス2倍稼げ説が説得力ないので、具体的に計算するハメになりました。自分が今サラリーマンで、フリーランスになる場合に何を判断の根拠にすればいいか知りたかったため。

# 前提
ここではフリーランスは企業に所属している業務委託の金額ではなく、その名の通りフリーランスとして契約している場合。企業に業務委託として人を出してもらうと何かあった場合のリスクヘッジ分契約金額が高くなるため。

# 年収対応表
サラリーマンの手取り額とフリーランスの手取り額を計算して比較することで、フリーランスの月単価とサラリーマンの年収を比較できる対応表を作りました。
一番右の1.5くらいの数値が今回の計算結果。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122840/5dd916f8-1f6d-c12f-46bd-6603468dc401.png)

# 説明

左から説明していくと以下。

* 「単価/月」：フリーランスの契約時の単価。
* 「年売上」：単価を12か月かけたもの。
* 「11か月売り上げ」：フリーランスがずっと途切れず契約できるとは限らないので、契約が年間で平均1か月空くことを想定する
* 「11か月売り上げ（万円）」：計算用に単位変えただけ。
* 「営業支払い後」：フリーランスが直で契約していればそれが一番いいのだが、レバテックやギークスみたいな営業を通して契約している場合に10%差し引かれると想定して計算。（10%以上のところもあると思うがここでは10%で計算する）
* 「手取り」：フリーランスの場合の手取り。これは青色申告時の場合で計算。以下サイトから、「営業支払い後」の金額から各種税を払った後に残る金額。
http://jigyou-tax.hajime888.com/j07.html
* 「サラリーマン年収」：サラリーマンの年収。一般的なサラリーマンが扶養家族なしの状態で（控除が無い状態）一つ左のセルの「手取り」が得られる年収を計算。これは簡単に一覧表がWEBに落ちているのでそこから計算。
* 「年売上/サラリーマン年収」：これが今回求めたかった数値。サラリーマンの年収（税払う前）の金額が、フリーランスで稼いでいる金額と比較したときに何倍になるか。おおよそ1.5倍であることがわかる。

# 計算結果の考察
この計算結果から1.5倍というのは大外れしていなさそうであることがわかる。
なぜこうなるのかというのを一言で言うと、企業はいろいろ社員のためにお金を払ってくれているから。

ただし、この計算結果は少しフリーランス優位になっている可能性がある。まず営業に10%以上払っている人が多いこと、401kなどの企業型確定拠出年金を企業が別で出している場合（大きな企業だと例えば月3万で年間36万が勝手に運用してもらることが多い。ここには課税はされないので、手取り額に純粋に36万足して問題ない）を考慮していないため。

まあでも営業入れずに契約を途切れないように頑張ればかなりもっと高いですね！

# フリーランスになる場合の判断要素

* 現在の年収の1.5倍稼げる自信があるか？
* 35歳40歳になったときにしっかりキャリアを築けているか？
* フリーランスとサラリーマンの年収の一般的な上限ラインを考えて、どちらが上を目指せそうか？

最後のポチは、フリーランスだと120万/月くらいがなんとなく普通の上限になりそう。それ以上いくとコンサル的な要素が増えてくるイメージ。サラリーマンだと企業によるが、同等の手取りを得たいのであれば970万/年でいいのでこれは部長とかのクラスならいけそうなイメージ。出世コースにのれば1200万/年はいけそう。
とはいえ1200万/年が完全な上限になりそうなイメージはあり（それ以上はかなり運の要素が高そう）、フリーランスなら160万/月-170万/月くらいなら頑張ればいけるのではないだろうか。
android 通知 Kotlin

#はじめに

アプリ画面のボタンを押下すると通知が届く様にする
※コード以外省略

###実装

``` Kotlin:MainActivity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)
        val channelId = "channel_id"
        val channelName = "channel_name"
        val channelDescription = "channel_description "
        val notificationButton = findViewById<Button>(R.id.button)
        ///APIレベルに応じてチャネル作成
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name = channelName
            val descriptionText = channelDescription
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val channel = NotificationChannel(channelId , name , importance).apply {
                description = descriptionText
            }
            /// チャネル登録
            val notificationManager: NotificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }

        /// 通知の中身
        val builder = NotificationCompat.Builder(this , channelId)
            .setSmallIcon(R.drawable.ic_launcher_background)    /// アイコン
            .setContentTitle("肉まん")           /// タイトル
            .setContentText("美味しい")           /// コンテンツ
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)   /// 通知の優先度


        var notificationId = 0   /// notificationID
        notificationButton.setOnClickListener {

            /// ボタンを押して通知を表示
            with(NotificationManagerCompat.from(this)) {
                notify(notificationId , builder.build())
                notificationId += 1
            }
        }
    }
}
```

これだけ、、、
画像が通知の表示部分

![スクリーンショット 2022-01-19 23.55.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/704865/09837c04-9724-2c3a-f357-3fe9206b5091.png)
Node.js 【MySQL 8.0 に接続できない。】

# Node.js 【MySQL 8.0 に接続できない。】
DBからデータ取得ができずに詰まったので備忘録。

## 起きたこと
- node.jsのパッケージ使って、MySQL8.0に接続しようとするとエラー発生

```
Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client
```


```
エラー：ER_NOT_SUPPORTED_AUTH_MODE：クライアントはサーバーから要求された認証プロトコルをサポートしていません。 MySQLクライアントのアップグレードを検討してください

（Google翻訳）
```

## 原因
- MySQL8.0のパスワード認証形式の違いが原因。
- MySQL8.0の認証プラグイン
    - **caching_sha2_password**
- （MySQL5.7までは、**mysql_native_password**）

```
mysql> SELECT user, host, plugin FROM mysql.user;
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | caching_sha2_password |
+------------------+-----------+-----------------------+
4 rows in set (0.00 sec)
```

## 解決策
- root（user）, localhost（host）の plugin を変更。
    - caching_sha2_password → mysql_native_password

```:plugin変更
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
```

```:特権を更新
flush privileges;
```

```:確認
mysql> SELECT user, host, plugin FROM mysql.user;
+------------------+-----------+-----------------------+
| user             | host      | plugin                |
+------------------+-----------+-----------------------+
| mysql.infoschema | localhost | caching_sha2_password |
| mysql.session    | localhost | caching_sha2_password |
| mysql.sys        | localhost | caching_sha2_password |
| root             | localhost | mysql_native_password |
+------------------+-----------+-----------------------+
4 rows in set (0.00 sec)
```

その後、DBからデータ取得が取得できるようになりました。

# おわりに。

こちらの[記事](https://www.chuken-engineer.com/entry/2020/09/04/074216) 
mysql2 をインストールするという手もあるようです。

```
npm install mysql2
```

**caching_sha2_password** の状態でも接続できるようです。
初めてPCを自作してみたんですが、意外となんとかなりました　～①事前検討編～

#はじめに
PCを自作してみたい。
憧れというにはあまりにも幼い好奇心を抱きながら、ぼんやりと先延ばしにし続けていた。
しかしこの度、使っているノートPCの動作が不安定になってきた（動画を見ながらExcelを動かすと再起動する）ため、一念発起。
デスクトップPCの自作に挑戦することを決意した。
この記事は、初心者が事前知識ほぼ0の状態から自作PCを完成させるまでの記録である。
（この時点ではまだ組み上げるどころかパーツも買っていないので、本当に「意外と何とかなる」のかは分かりません。おまじないです。）


#パーツを把握しよう
まずは必要なパーツの一覧を調べる。
[TSUKUMOのPC通販ショップ](https://www.tsukumo.co.jp/bto/pc/)でBTO販売されているものを参考にすると、構成要素は以下の通り。（付属するソフト等、一部割愛しています。）

- OS
- CPU
- オプションCPUクーラー
- CPUグリス
- マザーボード
- メモリ
- グラフィック機能
- サウンド
- ネットワーク
- Wi-Fi 無線LANモジュール
- SSD
- ハードディスク
- 光学ドライブ
- 3.5ドライブベイ
- PCケース
- 電源ユニット
- 入力機器

いっぱいあるな？？？？？
↓下のページを参考に、各パーツの概要を把握。
https://btopcs.jp/basic/before/parts/
PCに詳しい友人に聞きながら、TSUKUMOのセットと比較して必要なスペックを考えた。



###OS
>システム全体を管理するソフトウェア。
>Windowsとか、macOSとか、Linuxとか、そういうやつ。

使い慣れているため、Windowsを選択。
また、Hyper-Vを使いたいため、proにする。



###CPU
>パソコン上のハードウェアやソフトウェアから受け取る全ての指示を処理するパーツ。
>人間でいうと脳にあたる。

TSUKUMOのセットでは
Intel Core i7-11700 ﾌﾟﾛｾｯｻｰ (8コア 16スレッド / 2.5GHz、Boost時最大4.9GHz)
https://www.intel.co.jp/content/www/jp/ja/products/sku/212279/intel-core-i711700-processor-16m-cache-up-to-4-90-ghz/specifications.html
がデフォルトで指定されていた。
特に問題が無さそうなので、同等のスペックにする。



###オプションCPUクーラー
>CPUを冷却するためのファン。
>ファンはケースに付属しているが、冷却性能や静音性を求めるなら追加、変更する。

今回はPCの性能を求めないので、冷却性能は不要。
音も気にしないため、静音性も不要。
なので、今回は必要ない。



###CPUグリス
>CPUに塗ることで熱伝導率が上がり、冷却性能を高める。

今回は必要ない。



###マザーボード
>パソコン本体の裏側あるいは底に位置しているプリント基板。
>CPU・グラフィックボード・メモリ等を取り付ける。
>それぞれ搭載できるCPUやメモリなどが異なる。

デフォルトでは
Intel B560 MicroATXマザーボード （ASUS PRIME B560M-A)
https://www.cfd.co.jp/product/motherboard/intel-motherboard/b560m-hdv/
特に問題が無さそうなので、同等のスペックにする。



###メモリ
>情報を電気的に保持する。
>よく机に例えられ、大きいとたくさんの処理をスムーズに動かせる。

現状使用しているPCが8GBだが少々動きに不満があるので、16GBとする。



###グラフィック機能
>3D映像をモニターに映すための演算を行う。
>最低限のものは内蔵されているが、ゲームやWEBデザイン、CADを動かす場合には増設が必要になる場合がある。

上記の用途は想定していないため、今回は必要ない。



###サウンド
>音を出すためのパーツ。
>最低限のものは内蔵されている。

音にこだわることはないため、今回は必要ない。



###ネットワーク
>インターネットにつなぐための機構。

マザーボードにデフォルトでついている。
（ついてないモデルってあるのか？）



###Wi-Fi 無線LANモジュール
>Wi-FiやBluetoothを受信するためのパーツ。
>（マザーボードによっては内蔵されている。後付けもできる。）

無線接続を使用しないため、今回は必要ない。



###SSD
>ハードディスクよりも読み書きの早いストレージ。
>OSをインストールしたりする。

デフォルトでは
［高速M.2 Gen3］ 500GB WD BLUE SN570 (読込最大 3500MB/s, 書込最大 2300MB/s, NVMe）
https://www.westerndigital.com/ja-jp/products/internal-drives/wd-blue-sn570-nvme-ssd#WDS250G3B0C
特に問題が無さそうなので、同等のスペックにする。



###ハードディスク
>容量の大きな記憶装置。
>写真や動画、アプリケーション等を保存する。

容量はSSDで足りているため、今回は必要ない。



###光学ドライブ
>DVDやブルーレイ等、ディスクを読み込む。

DVDの視聴に使うため、DVDが再生できるものを買う。



###3.5ドライブベイ
>カードリーダー。
>SDカード等を読み込む。

今回は必要ない。



###PCケース
>各部品を取り付けるパーツ。
>拡張性や冷却性、掃除のしやすさ、そして何より見た目にかかわる。

今回、BTOではなくバラ買いにした一番の理由。
拡張性や冷却性は特段求めないが、自分のずぼらな性格ゆえに掃除のしやすさは必須。
また、どうせならかっこいいほうがいい。テンション上がるし。
店頭で店員さんにおすすめを聞いて選ぶ。



###電源ユニット
>コンピューターの各パーツに電源を供給する。
>メモリやグラフィックボードによって必要な電源容量が異なる。

当日、決定したパーツによってちょうどよい電源を選択する。



###入力機器
>キーボードやマウスのこと。

キーボードは、パチパチとスイッチ感のある物が好みなのでそれを買う。
マウスは現在使用しているものがあるので不要。



いっぱい書いた！以上です！



#さあ、買いに行くぞ！
以上のリストを持って、TSUKUMOへ赴きます。
[②購入編](https://qiita.com/rararalanlan/items/16855365fc716b4dc949)へ続く！
【小ネタ】政令指定都市で市と区と分けたいときの書き方

## Introduction

住所のデータを引っ張ってきた時に都道府県、市区町村、町字ってわけるのが多いと思うのですが
政令指定都市だと市区町村のところが、〇〇市〇〇区になっていたりする。
※町字に入ることもあるけど。。。

地味にここを分けて表示したいというニーズが意外となかったので、備忘録！！

## Code

```javascript

const cityName = "川崎市中原区"
const cityNameString = cityName.replace(/^(.{1,4}市)(.+)/, '$1 $2').split(' ');

// 四日市など”市”の前に”市”を使う地名があるためここで整形
const cityNameArray = cityNameString.match(/^(四日市|廿日市|野々市)/) !== null ? cityNameString.replace(/\s+/g, "").split('') : cityNameString.split(' ')

// ['川崎市', '中原区']

// 政令指定都市の中で最長は'さいたま'の4文字、最短が'堺'の1文字のため、1〜4文字で判定するようにしてます
```

## Conclusion

町字で区とそれ以下を分けるときも同じことできそうｗ

## 追記・編集　2022年1月21日


@jnchito さんからのアドバイスにより、文字列作成の箇所を更新いたしました！！
ご助言ありがとうございます！！
https://qiita.com/ChaaaBooo/items/781fa952b58198eb2a0b#comment-2c831ede953799252614

##　References

- 都道府県と市町村区を分割し、配列に格納する。
https://qiita.com/kajitack/items/d457fb4a811ddc53952a
BigQueryのグループ集計時にfirst_valueを使いたい

# 概要

BigQueryのデータ加工時に、グループ集計時に特定の条件でソートした際の最初の行の値を取得したいといったことがある。
分析関数としてfirst_valueが用意されているが、処理の目的によってはグループ集計で実現できることで便利なこともあるため、その方法を備忘録としてまとめる。


# やり方

方法は複数あるが、BigQueryの場合、`array_agg`を活用するのが一番スマート。

例えば、`student_id`に対して、試験日(`exam_date`)が最新の`score`を取得する場合は以下の通り。

```sql:サンプル
select
    student_id,
    array_agg(score order by exam_date desc limit 1)[offset(0)] as last_score
from
    exam_resuts
group by
    student_id
```

複数の値を取得したい場合は、structを活用すると良い


```sql:サンプル2
select
    student_id,
    array_agg(struct(exam_date as last_exam_date, score as last_score) order by exam_date desc limit 1)[offset(0)].*
from
    exam_resuts
group by
    student_id
```
React チュートリアル　パート11　（npm install  react-toastify ）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/0d181e85-e7be-0977-f475-184d8481f7a3.png)

#目標:military_medal:
アプリケーションでこのようなポップを見たことないですか:interrobang:
今日は、これを実装します！

![2022-01-19_23h26_48.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/aefcaa5a-4ba8-1740-27ca-14b76b0280da.gif)

公式ドキュメント:arrow_down:

https://www.npmjs.com/package/react-toastify

#”npm install react-toastify”をインストール
まず、Vscodeのターミナルを開いて**”npm install react-toastify”**をインストールしてください。

#アプリケーションとサーバーを起動する
**npm start** と **npm run server**をターミナルに入力

#App.jsに記述
![code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/265c8242-76d2-8442-6fdf-27ec83312e5a.png)

>【4と5行目】ToastContainerとtoast,このパッケージ用のcssをimportします。

>【45～48行目】ここで実装しています。

>【56行目】ToastContainerを記述します。

#ブラウザで確認:fire::fire:

![2022-01-19_23h39_04.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654025/6c9483a9-6a5c-6a07-b498-14162a7ab2fc.gif)

#次回:bell:
npm の便利なパッケージを紹介します！
(長期)インターンの面接、面談を通して

これまで数社（ベンチャー多め）の長期インターンの面談や面接をしてきたので、今後の就活のためにも得た経験や知識、課題をアウトプットします。

## 面談、面接でよく聞かれたこと
### プログラミングを初めたきっかけ
これは面談の時点で結構聞かれた気がします。言い方によっては行動力があるという面でプラスになる事ができますが、「フリーランスになりたいから」とか「楽して稼ぎたいから」は良くないと思いますね。そもそもエンジニアは楽して稼げません。

### 将来的に何をしていきたいか
キャリアプラン、どんなエンジニアになりたいかという類の質問ですね。深堀りされても迷わず答えれるように、自分の性格や能力、理想から導き出していきましょう。「将来はフリーランスになりたい」とかはダメですね。「いずれはあなたの会社を辞めます！」と言ってるようなものなので。

### （技術的に）苦労したこと
ほぼ鉄板の質問ですね。エンジニアになるための勉強や経験内で答えた方が良さそうです。同時に、どうやって克服したのかも具体的に考えておく必要があります。僕の場合、「技術的に苦労したことは何ですか？」と聞かれて「技術的にってなんやねん」ってなりました。

### ポートフォリオの説明
これもエンジニアの面接では対策しておくべき質問ですね。どのようなサービスか、このサービスをつくるに至った背景、（技術的に）工夫したことを考えておきましょう。

### 使った技術の質問
学んできたプログラミング言語などの技術質問ですね。`httpリクエスト`や`MVCモデル`といった主流な技術の知識も説明できるように取り入れておく必要があるかもしれません。浅く広い勉強をしてきた人は苦労するかもしれませんね（僕のように）。

### プランの説明（なぜそうしたか）
今まで行動してきた内容に関する質問ですね。選択してきた技術に対しても聞かれます。もちろん「みんなが使ってるから」とか「モダンだから」はダメで、目的から逆算してきた筋の通った説明をしましょう。

## 面接、面談を通して思ったこと
### いろんな業務形態がある
エンジニアインターンといっても、フロントエンドやバックエンド、サイト制作、開発、テストなど様々な業務があります。インターンで実務経験ができるからといってどこでも良いというわけにはいきません。インターンは最初にカジュアル面談を行う会社がほとんどなので、そこでの会話で自分と会社の期待値のすり合わせを行って、学んできたことなどから本当に自分が働きたいところなのか考えるべきですね。

### やっぱり東京が多い
求人サイトで調べてみるとわかりますが、ほとんどの募集が東京にあります。このご時世やエンジニアだとリモートワークが多いですが、それでも大阪在住の僕にとっては応募数が限られて苦労しました。

### やっただけで満足しない
いくらプログラミングを頑張ってハイレベルなポートフォリオをつくったりインターンを経験しても、それを上手く言語化して伝えないと意味ないし落とされます。やってきたことだけで終わらせず、同じぐらいそれを伝えるような努力をする必要があります。

## 課題

### しっかり相手の目（オンラインならカメラ）を見て、堂々として笑顔で話す
そもそも僕はコミュニケーションが苦手で相手の目を良く見て話すこともあまりできていませんが、面接ではそうも言ってられません。普段から相手の目を見て話すように意識して、多少の笑顔を欠かさないように練習します。

### 技術の復習をしておく
使った技術の説明を深堀りされて答えられなかったことがあるので、面接前に技術の復習をしておくか、知識が浅いのならその旨を事前に言うか、そもそもその技術を使ったと言わないようにします。

## まとめ
最近になってインターンの選考もますます難しくなっている気がします。僕の能力不足が原因ですが面談の時点で落とされたこともありました。エンジニアになるためにはそれなりの覚悟を持って学習や面接の対策にしっかり励みましょう。ちなみに、勉強会などから上手いこと人脈作ってコネで入るのが最善かもしれません。
Windows10で802.1X認証を実装した無線LANに接続できない

社内の無線LANでは.1X認証（証明書認証）を実装しているのだが、一部ユーザでパソコンに証明書をインストールされているのに無線LANへ接続できないという事象が発生した。
どうやらSSIDへ接続しても「接続できません」と表示されるようだ。
ということで原因を調べてみた。

#原因
コンピュータ証明書ストアに複数のクライアント証明書が入っている場合に、Wi-Fiインターフェースがどの証明書を利用していいかわからなくなってしまい、接続できないようだ。

　▼certmgr.mscの結果
　ここの「個人」に証明書が沢山入っていると、Wi-Fiインターフェースがどれを利用すればいいか悩んじゃうらしい
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2492453/441ef3b1-21de-19c5-87d4-73f210a5830e.png)

#解決方法
どの証明書を利用すればよいのか、Wi-Fiインターフェースに教えてあげれば解決。

　コントロールパネル->ネットワークと共有センター->ワイヤレスネットワーク接続->ワイヤレスのプロパティ
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2492453/336962d6-1a14-3765-05ba-ca5801dc038a.png)

　セキュリティタブ->設定
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2492453/6e5e794c-e472-3d3a-39cb-7f9b06dd113d.png)

　詳細設定
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2492453/b6354c0b-60b1-fe51-ab31-39a46d65656a.png)

　証明書発行者にチェックを入れ、無線LANで利用する証明書を指定する
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2492453/362f459b-07b1-5216-2364-43284ae1c89c.png)

なんかWindows7の時代は、こうやって利用する証明書を指定すら出来なかったらしいね。
なので個人配下に無線LAN以外の証明書が入っていると、絶対に無線LANに接続できなかったみたい。

#参考
https://faq1.soliton.co.jp/faq/show/10390?site_domain=default
Google Cloud FunctionsでSeleniumがうまく動かなかったので、Phantom JS Cloudで代用した

## Google Cloud Functionsでスクレイピングを自動化
### ざっくり背景
もともとFXの自動取引で使用していたお名前VPSにPythonをインストールして、Windowsのタスクスケジューラでスクレイピングを自動化していましたが、

月々1,300円払ってスクレイピングするのも馬鹿らしくなったので、Google Cloud Functionsで動かすことにしました。

色々つまずきながらも、ほぼすべてのプログラムをGoogle Cloud Functionsに移管でき、ついに最後のプログラム。

最後のものが一番の難関でした・・・Selenium使っとる:sob::sob::sob::sob::sob:

### なぜSeleniumは面倒なのか
Javascriptで生成された動的なページの場合、BeautifulSoupではソースコードをうまく取得することができないため、部分的にSeleniumを使っていました。

で、SeleniumでスクレイピングするためにはWebDriverなるものを動かさないといけない。

そのためにはWebDriverをCloud Functionsにデプロイしなければいけない。

デプロイしても権限？の問題でなぜか動かせない。

* 「Google Cloud Functions Selenium」
* 「Google Cloud Functions webdriver」
* 「GCF chromedriver」

とかとか、とにかく検索しまくって海外サイトも調べ漁っても答えが見つかりませんでした。

で、色々考えていたらPhantomJSのことを思い出しました。

## PhantomJSCloud使えば一発だった
### サンプルコード
要はデプロイして手元で動かさずに、クラウドサービスに動かしてもらえばええやんって話。

```python:sample.py
payload = {'url':'https://google.com/','renderType':'HTML','outputAsJson':'true'}
payload = json.dumps(payload)
payload = urllib.parse.quote(payload, safe = '')

phantomjs_key = 'API-KEY'
url = 'https://phantomjscloud.com/api/browser/v2/' + phantomjs_key + '/?request=' + payload

pj_res = requests.get(url)
pj_res = pj_res.json()
html = pj_res["content"]["data"]

soup = BeautifulSoup(html, 'html.parser')
```

PhantomJSCloudは無料プランだと1日500リクエストまで使えるので、基本はBeautifulSoupでスクレイピングして、JSで作られた動的なページだけPhantomJSCloudに任せればOK。

### おまけ
Google Cloud Functions × Seleniumに関して5時間くらい調べても動かすことができなかったのですが、PhantomJSCloudという方法に気づいてからは一瞬でした。

同じ悩みを抱えている人はPhantomJSCloud使いましょう。
【Ruby】インスタンスの生成

※初心者向け
※アウトプット練習の為

開発環境
rails 6.0.4.4
ruby 2.6.5

##問題
クラスFruitを以下の仕様で定義してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2451862/b3e5911a-abf2-f0db-a2f0-a706d4bd1b9a.png)
実行結果は以下のようになります。

```ruby
採れたて新鮮な果実です
リンゴは120円です
オレンジは200円です
イチゴは60円です
```

雛形

```ruby

class Fruit
 def クラスメソッド
   # 正しくメソッドを定義した上で、ここに処理を記入してください
 end

 def initialize
   # ここに処理を記入してください
 end

 def インスタンスメソッド
   # 正しくメソッドを定義した上で、ここに処理を記入してください
 end
end

# 3つのインスタンスを生成してください

# クラスメソッドを呼び出し、「採れたて新鮮な果実です」と表示してください
# インスタンス毎にインスタンスメソッドを呼び出し、「【名前】は【価格】円です」と表示してください

```

##解答

```ruby
class Fruit

 def self.fresh
   puts "採れたて新鮮な果実です"
 end

 def initialize(name, price)
   @name = name
   @price = price
 end

 def introduce
   puts "#{@name}は#{@price}円です"
 end
end

apple = Fruit.new("リンゴ", 120)
orange = Fruit.new("オレンジ", 200)
strawberry = Fruit.new("イチゴ", 60)

Fruit.fresh
apple.introduce
orange.introduce
strawberry.introduce
```

##解答
まずは、インスタンスの生成と、インスタンス毎のインスタンス変数（@nameと@price）を定義します。
ポイントは、インスタンス生成時の引数と、initializeメソッドです。
initializeメソッドは、newメソッドの引数を受け取ることができます。
インスタンス毎の名前と価格をnewメソッドから受け取り、インスタンス変数（@nameと@price）に代入しましょう。

```ruby
class Fruit
 def クラスメソッド
   # ここに処理を記入してください
 end

 def initialize(name, price)
   @name = name
   @price = price
 end

 def インスタンスメソッド
   # 正しくメソッドを定義した上で、ここに処理を記入してください
 end
end


apple = Fruit.new("リンゴ", 120)
orange = Fruit.new("オレンジ", 200)
strawberry = Fruit.new("イチゴ", 60)

# クラスメソッドを呼び出し、「採れたて新鮮な果実です」と表示してください
# インスタンス毎にインスタンスメソッドを呼び出し、「【名前】は【価格】円です」と表示してください
```

次に、クラスメソッドfreshを定義します。
処理内容通りの文言が出力されるよう、putsメソッドを用いて記述しましょう。

```ruby
class Fruit
 def self.fresh
   puts "採れたて新鮮な果実です"
 end

 def initialize(name, price)
   @name = name
   @price = price
 end

 def インスタンスメソッド
   # 正しくメソッドを定義した上で、ここに処理を記入してください
 end
end


apple = Fruit.new("リンゴ", 120)
orange = Fruit.new("オレンジ", 200)
strawberry = Fruit.new("イチゴ", 60)

# クラスメソッドを呼び出し、「採れたて新鮮な果実です」と表示してください
# インスタンス毎にインスタンスメソッドを呼び出し、「【名前】は【価格】円です」と表示してください

```
続いて、インスタンスメソッドを定義します。
各インスタンスに対して処理内容通りの文言が出力されるよう、putsメソッドを用いて記述をしましょう。

```ruby
class Fruit
 def self.fresh
   puts "採れたて新鮮な果実です"
 end

 def initialize(name, price)
   @name = name
   @price = price
 end

 def introduce
   puts "#{@name}は#{@price}円です"
 end
end


apple = Fruit.new("リンゴ", 120)
orange = Fruit.new("オレンジ", 200)
strawberry = Fruit.new("イチゴ", 60)

# クラスメソッドを呼び出し、「採れたて新鮮な果実です」と表示してください
# インスタンス毎にインスタンスメソッドを呼び出し、「【名前】は【価格】円です」と表示してください
```

最後に、定義したクラスメソッドとインスタンスメソッドを呼び出します。
クラスメソッドは「クラス名.メソッド名」で、インスタンスメソッドは「インスタンス.メソッド名」で呼び出しましょう。

```ruby
class Fruit

 def self.fresh
   puts "採れたて新鮮な果実です"
 end

 def initialize(name, price)
   @name = name
   @price = price
 end

 def introduce
   puts "#{@name}は#{@price}円です"
 end
end

apple = Fruit.new("リンゴ", 120)
orange = Fruit.new("オレンジ", 200)
strawberry = Fruit.new("イチゴ", 60)

Fruit.fresh
apple.introduce
orange.introduce
strawberry.introduce
```
以上です。
podのIDを調べてログインするスクリプトを作ってみる

GCPで動くpodってデプロイすると毎回ID変わってアクセスするのが面倒くさいです
なので、思いつきでログインするスクリプトを作りました。

まずは、pod idを取得した結果をelixirで加工するスクリプトを作ります。
※きっと良いやり方はあるはずだが、知識がない範囲で即席で作りました。

いざとなったら、elixirで文字加工をしてしまえばいいという荒業です。

```podex.exs

#! /usr/bin/env elixir
defmodule Main do
  def main do
    [_, ret] = cmd("kubectl", ["get", "pod"])

    [ret | _]= ret |> String.split(" ")

    ret
    |> IO.puts

  end

  defp cmd(command, params) do
    list =
      System.cmd(command, params)
      |> Tuple.to_list()
      |> List.first()
      |> String.split("\n")

    list -- [""]
  end
end

Main.main()
```

実行するスクリプト

```execpod.sh
#!/bin/sh
POD=`./podex.exs`
kubectl exec -it $POD -- sh
```

execpod.sh を実行すると、podにログインできます。
ただし、ヒットした１件目のpodにしか対応してません。
.htsccessで正規化をしたときの見落としがちなポイント

#記事を書こうと思ったわけ
ボランティアでお手伝いしている、あるクライアント様のウェブサイト管理。古き良きHTMLで記述されていて、メンテに手間がかっていました。全てHTMLファイルになっているから、別パーツをインクルードするとかなかなかできないわけです。CMSで管理されているわけでもない。

なので、共通要素（たとえばグローバルナビゲーション）に変更を加える場合、全てのページの要素を更新しなきゃなりません。そして、これは往々にして、人為的なミスにつながることが多いです。
もっぱらPHPでサイトを作っていた僕としては、どうにかならんかと思っていて、ゆくゆくはPHPで管理できるようにしたいと考えていました。

その場合、拡張子は.phpとなります。すると、これまで.htmlでアクセスされていたURLを変更する必要が出てきてしまいます。そこで、これをコントロールする.htaccessで制御しなきゃならないなと思うに至りました。

ところが、原理はわかっていても、いざサーバーにhtaccessを上げてみると、Internal Server Error500が発生してしまいました。
どういうわけか全然わからず、これまで失敗続きだったわけですが、このたび、見事にそれをクリアしました。
ググれば、正規化の記述のしかたはたくさんありますが、あまり言及されてないポイントで引っかかったので、メモとして残します。

#.htaccessで見逃しそうなポイント
##文字コードはUTM-8(BOM無し)
.htaccessはテキストエディタで書くことが多いと思うので、そこでは必ずUTM-8を文字コードに指定しておく。このとき、BOM無しにしておくことが重要です。そうでないと、サーバーエラーが起こります。

##改行コードはLF
これも盲点でした。UNIX系の改行コードはLFを使うので、それに合わせて改行コードをLFにしておきます。

##最終行は空行にしておく
これをやっていなかったせいでサーバーエラーが生じていました。
最低1行は挿入しておくこと。![image-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223929/35c7cd4d-59ee-d415-db12-51369a624aa3.png)
6行目と7行目に注目。空行が入っている。ただし、空白スペースは入れるとエラーになります。

##サーバーアップロード方式はアスキーモードで
モードを自動変換してアップしてくれるFTPクライアントばかりだと思いますが、あえてマニュアルであっぷすることもありかもしれません。
MVCモデルについて

###学習した理由
最近ポートフォリオを作成し終えた初学者になりますので、これを機にもう一度基本に戻り、プログラムの基礎を学ぶことを目的としてこの記事を作成しております。

###MVCモデルとは
MVCモデルとは「モデル（Model）」「ビュー（View）」「コントローラ（Controller）」の三つに分類し、コードを分けて記述する方法になります。

###「モデル（Model）」
「モデル（Model）」の役割は表示内容や入力に関係しない処理を担当する部分になります。
もっと詳しく説明すると、処理したデータをデータベースに反映することです。
担当する処理として、データの検索、データの検証、データの変換、データの紐付けなどになります。

###「ビュー（View）」
「ビュー（View）」の役割は実際に目に見えるページを表示することになります。
「ビュー（View）」に関してはユーザー側の処理が多く、入力内容の取り込みや、リクエストをコントローラーに渡す役目も果たしています。

###「コントローラ（Controller）」
「コントローラ（Controller）」の役割はView（ビュー）からのリクエストを受け取り、Model(モデル）へのメッセージへと変換することです。そのレスポンスとして、画面をビューに渡します。
また、モデルからデータを受け取り、レスポンスとしてビューに返す処理も実行しています。

###まとめ
今回はMVCモデルについて自分の学習用に投稿しております。
初歩的な学習記事ですが、私と同じような初学者の役に立てばと思っています。
VSCode のターミナルを下・左・右に配置する

VSCode のターミナルは、下・左・右に配置することができる。

ディフォルト表示は「下」。
用途によって表示位置を変更したい。

- パネルを開いた状態で「ターミナル」や「デバッグコンソール」上で右クリック。
- 変更したい選択肢を選ぶ。

![VSCode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19223/1199384c-3a74-4c2a-bc9c-766c4c71517d.png)

パネル表示のショートカット

- macOS: `cmd + j`
- Windows: `ctrl + j`
MacのPythonで音声データを扱う：オーディオデバイスの情報を取得

PythonでAudioデータを使ったアプリを作っていきます。
手始めに、オーディオデバイスの情報を取得します

##開発環境
・Mac(Mac Book Air ２０１３）
・MacOS Big Sur(11.5)
・Python 3.9.1
・PortAudio 19.7.0
・PyAudio  0.2.11

##参考
PyAudioのAPI仕様を参考にします

https://people.csail.mit.edu/hubert/pyaudio/docs/#pyaudio.PyAudio.get_device_count

##手順
１．オーディオデバイスの数を取得
２．全オーディオデバイスに対して情報を取得

```
import pyaudio

p = pyaudio.PyAudio()

for i in range(0,p.get_device_count()):
    print(p.get_device_info_by_index(i))
```

私のMacでは以下のような出力が得られました。

```
{'index': 0, 'structVersion': 2, 'name': 'Built-in Microphone', 'hostApi': 0, 'maxInputChannels': 2, 'maxOutputChannels': 0, 'defaultLowInputLatency': 0.0029478458049886623, 'defaultLowOutputLatency': 0.01, 'defaultHighInputLatency': 0.01310657596371882, 'defaultHighOutputLatency': 0.1, 'defaultSampleRate': 44100.0}
{'index': 1, 'structVersion': 2, 'name': 'Built-in Output', 'hostApi': 0, 'maxInputChannels': 0, 'maxOutputChannels': 2, 'defaultLowInputLatency': 0.01, 'defaultLowOutputLatency': 0.007551020408163266, 'defaultHighInputLatency': 0.1, 'defaultHighOutputLatency': 0.017709750566893424, 'defaultSampleRate': 44100.0}
```

オーディオ端子のマイク入力がindexの０番として認識されていますね。
ここで得た情報をもとに、オーディオアプリを作っていけそうです。
今回はここまで。
pipとは〜スクレイピングでライブラリインストール際に登場し疑問だったので調べてみました〜

# はじめに
昨年の年度末ごろからpythonの学習を始め、
その中スクレイピングで使用する際や仮想環境構築する際に出てきた「pip」

忘れないようにメモベースでまとめています。
詳細は参考URLを見てください！

参考URL

https://prograshi.com/language/python/python-pip/


#pipとは

>URLより引用

>pipとはpython公式のパッケージ管理システムです。
インストールしたpythonの中に入っています。
Pip Installs Packages（またはPython）の略です。ピップと読みます。
pythonに公式に登録されたパッケージのインストールやアンインストールが出来ます。
＜補足＞
pythonのパッケージはPyPIで管理されています。
　└ Python Package Indexの略
　└ 呼び名：パイパイ
　└ 公式ページ：https://pypi.org/
コマンドでPyPIにアクセスし、ファイルをインストールしたりするのがpipです。

イメージとしてはLinuxでいうrpmコマンドみたいな感じぽい
正式名称があるのにびっくりして、面白いなと思いました。

ちなみに今回学習で使った

     pip list

で現在インストール済みのライブラリーが確認可能
何かライブラリーをインストールする際は、

    pip install ライブラリー名

見たいな感じ。

ちなみに仮想環境立ち上げ後にpipコマンドを使って確認できるようになった。
その時のコマンドは、

    virtualenv env(ここは仮想環境名)

一応メモ。
架空のサービス(devops)を企画してみた

# 本記事について
標題の通りですが、架空の企業にて使う架空のソリューションサービス企画・要件定義・設計を行った記録になります。
本記事は企画編です。

# そもそもの目的
自分自身の修行の為、
* 上流工程について理解を深める
* Dockerについて理解を深める

この2つの意図で始動しました。

# サービスについて
Dockerコンテナを使ったdevopsを実現できる汎用的な構成を提案・提供するサービス(ソリューション)

# 実現方法
AWSの開発者向けサービス群を組み合わせて実現する。

## Dockerとは？
Dockerは、コンテナ仮想化を用いてアプリケーションを開発・配置・実行するためのオープンソースソフトウェアあるいはオープンプラットフォーム。（Wikipediaより引用）
導入メリット:
アプリケーションの開発・保守対応におけるサーバー構築の大部分を自動化でき、なおかつ構成管理の側面も持つため、開発・検証・本番環境の差異を（必要な差異を除いて）なくすことができる。
参考画像１
<img src="https://extremecoolbucketpublic.s3.ap-northeast-1.amazonaws.com/kujira.png" width="80%">

## devops とは？
DevOpsは、ソフトウェア開発手法の一つ。開発担当者と運用担当者が連携して協力する開発手法をさす。ソフトウェアを迅速にビルドおよびテストする文化と環境により、確実なリリースを、以前よりも迅速に高い頻度で可能とする組織体制の構築を目指している。（Wikipediaより引用）
導入メリット:
アプリケーションの迅速な改善対応が可能となる
参考画像２
<img src="https://extremecoolbucketpublic.s3.ap-northeast-1.amazonaws.com/machine_jidou_seisan_line.png" width="80%">

# 対象企業
SaaSサービスを開発している企業で、Dockerをこれから導入したい会社という想定。

# 導入によって得られるメリットについて
## その1:ワークロードの環境差異を最小化する
### 処方箋となるケース
プロダクトの稼働環境が元々運用管理チームにより管理されていたが、近年クラウド移行の推進により開発サイドの介入が増えた。それに付随して、開発環境では起こらなかったバグが本番環境で起こるなど、環境差異に起因する問題が顕在化している。

## その2:環境構築の手間を最小化する
### 処方箋となるケース
開発環境においてプログラミング言語や各種ライブラリなどのバージョンが一元管理されておらず、開発やテストの為の環境を揃えるのに時間を要している

## その3:迅速な開発体制を促進
### 処方箋となるケース
アジャイル開発の導入が始まっており、ビルド、デプロイなどリリース関係のフローを自動化・簡略化して迅速な開発スパンを回せる体制を構築したい

# コンポーネント構成
## メインコンポーネント
* Amazon ECS
* Amazon ECR
* AWS CodeBuild
* AWS CodeDeploy
* AWS CodePipeline
* AWS Systems Manager

## サブコンポーネント
* AWS CloudFormation
* AWS IAM
* AWS CloudWatch Logs
* GitHub

# 要件定義編に続く。。。

https://qiita.com/miyabi-san/items/e78e12313cc49b7e4529
Linux(Ubuntu)に非rootユーザがPython環境構築する方法

# 目的

Linuxマシンにroot権限がないユーザがPython環境を構築する方法。
滅多にない場面だが、共有GPUサーバーを利用する際に、root権限がないユーザを発行される & pipすら入っていない、という状況に遭遇した。
`sudo apt install python3-pip` とするとエラーが出たので、他の方法を試す。


# 前提

curl、もしくはwgetがインストールされているものとする。

# 方法

Anacondaをインストールする。具体的には以下の通り。

- https://repo.anaconda.com/archive/ を訪れ、自分が使いたいバージョンのダウンロードリンクを確認する。例えば、記事執筆時(21/01/19)時点の最新バージョンだと https://repo.anaconda.com/archive/Anaconda3-2021.11-Linux-x86_64.sh である。
- 以下のコマンドを順に打ち込む。まずシェルスクリプトをダウンロード。

```bash:terminal
curl -O ${先ほど確認したリンク}
```

例えば、

```bash:terminal
curl -O https://repo.anaconda.com/archive/Anaconda3-2021.11-Linux-x86_64.sh
```

curlがインストールされていない場合はwgetを使ってください。

- Anacondaをインストール

```bash:terminal
sh ${先ほどダウンロードしたファイル} -b -p
```

例えば、

```bash:terminal
sh Anaconda3-2021.11-Linux-x86_64.sh -b -p
```

もうシェルスクリプトはいらないので捨ててください。

```bash:terminal
rm ${先ほどダウンロードしたファイル}
```

- 環境変数を設定

```bash:terminal
echo ". $HOME/anaconda3/etc/profile.d/conda.sh" >> ~/.bashrc
echo "conda activate base" >> ~/.bashrc
```

適宜自分のシェルに合わせて読み替えてください。
再起動するか、 `source ~/.bashrc`

- anacondaを最新版に（任意） 

```bash:terminal
conda update conda
conda install anaconda
```

これでPython環境が出来上がりました。 `which python3` として出てきたパスがホームディレクトリ以下の `anaconda3/bin/python3`であれば正しいです。

# 終わりに

sudo必要なかったですね。condaの使い方は他の記事読んでください。
これとか。
https://qiita.com/naz_/items/84634fbd134fbcd25296