OmniAICreator/Qiita-1.07M huggingface dataset:
iTerm2で会社の株価を表示するコンポーネント作ってみた

## 何を作ったか

iTerm2でコンソール上にステータスバーを表示することができるようになりました🎉🎉🎉
また、独自に作成したコンポーネント (スクリプト)も登録・利用できるようになりせっかくなのでなにか作ってみようと思い今回作成しました。

<img width="486" alt="スクリーンショット 2020-01-11 13.01.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29844/e4cd2493-6ed5-6c31-77d2-93e208458c8a.png">

(※会社はてきとうに選びました）

## どうやって作るか

iTerm2のコンポーネントの作り方は他の記事を参照してください！

大変参考にさせていただきました→ [【iTerm2】ステータスバーのコンポーネントを自作してみた](https://dev.classmethod.jp/server-side/python/iterm2_create_statusbar_component_use_python/)

プログラムとしてはシンプルで、Yahooファイナンスのページから表示したい会社を検索し、情報を埋めているだけです。
以下、ソースコード貼っておきます。

## ソースコード

以下、読み替えて使ってください。

* identifier ... 一意に定まるなにか。
    * 例えば `com.rindo.iterm2.stock_info`
* security_code ... 証券コード

```python
#!/usr/bin/env python3

import iterm2
import re
import requests
from bs4 import BeautifulSoup


async def main(connection):
    component = iterm2.StatusBarComponent(
        short_description="XX株価",
        detailed_description="株価を表示します",
        knobs=[],
        exemplar="会社 12億円 ¥3,456 前日比: +78 (+9%)",
        update_cadence=60,
        identifier="{identifier}"
    )

    @iterm2.StatusBarRPC
    async def coro(knobs):
        SECURITY_CODE = {security_code}
        URL = "https://m.finance.yahoo.co.jp/stock?code={}.T"

        res = requests.get(URL.format(SECURITY_CODE))
        soup = BeautifulSoup(res.text, "html.parser")

        company = soup.find("h2", class_="title").string
        price = soup.find("dd", class_="priceFin").string

        cap = soup.find("div", class_="stockDetail").find_all("dd")[12].string
        cap = re.sub(',|百万円', '', cap)
        cap = int(cap) * 1000000
        cap = float(cap) / 100000000

        down = soup.find("dd", class_="deltaDown")
        up = soup.find("dd", class_="deltaUp")
        change = ""
        if up is not None:
            change = up.string
        else:
            change = down.string

        return "{} {}億円 ¥{} 前日比: {}".format(company, cap, price, change)

    await component.async_register(connection, coro)

iterm2.run_forever(main)

```
[Ruby] memcached のキー一覧を表示する

# はじめに

memcached でキーの一覧を取得するのは面倒だ。

* telnet コマンドで接続する。
* `stats items` で存在する slab 番号を調べる。
* キーが存在しそうな slab 番号で `stats cachedump` を実行する。

ということをやらないといけない。

Perl で書かれた [memcached-tool](https://github.com/memcached/memcached/blob/1.5.20/scripts/memcached-tool) の dump を使うという手もある。しかし、この記事ではお手製の Ruby スクリプトで取得してみる。

# コード

```rb:list_memcached_keys
#! /usr/bin/env ruby
require 'bundler/inline'

# 必要な Gem をインストールする。
gemfile do
  source 'https://rubygems.org'
  gem 'net-telnet'
  gem 'terminal-table'
end

class Item
  attr_reader :slab_no, :key, :bytesize

  def initialize(slab_no, key, bytesize, expires_at)
    @slab_no = slab_no
    @key = key
    @bytesize = bytesize.to_i
    @expires_at = expires_at.to_i
  end

  # UNIX 時間を Time オブジェクトに変換する。
  def expires_at
    return nil if @expires_at.zero?

    Time.at(@expires_at)
  end
end

require 'optparse'
require 'net/telnet'
require 'terminal-table'

# コマンドライン引数で host と port を指定できるようにする。
options = { host: 'localhost', port: 11211 }
OptionParser.new do |opt|
  opt.on('--host=[VALUE]') { |v| options[:host] = v }
  opt.on('--port=[VALUE]') { |v| options[:port] = v }
  opt.parse!(ARGV)
end

def get_items(host, port)
  localhost = nil

  begin
    localhost = Net::Telnet.new('Host' => host, 'Port' => port, 'Prompt' => /^END$/)

    slab_nos = localhost.cmd('stats items').scan(/(?<=^STAT items:)\d+/).uniq.map(&:to_i)
    items = slab_nos.flat_map do |slab_no|
      results = localhost.cmd("stats cachedump #{slab_no} 1000").scan(/^ITEM (\w+) \[(\d+) b; (\d+) s\]/)
      results.map { |result| Item.new(slab_no, *result) }
    end

    items
  ensure
    localhost&.close
  end
end

def build_table_rows(items)
  items.map do |item|
    [
      item.slab_no,
      item.key,
      item.bytesize,
      item.expires_at&.strftime('%Y/%m/%d %H:%M:%S')
    ]
  end
end

items = get_items(options[:host], options[:port])
headings = ['slab no', 'key', 'size (byte)', 'expires at']
rows = build_table_rows(items)

# terminal-table を使って、結果を表形式で標準出力に出力する。
table = Terminal::Table.new(headings: headings, rows: rows)
puts(table)
```

# 実行例

Ruby の memcached クライアントである [Dalli](https://github.com/petergoldstein/dalli) を使って値を set し、さきほどの Ruby スクリプトを実行してみる。

```shell
$ ruby -v
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin18]
```

```rb
require 'dalli'
require 'securerandom'

client = Dalli::Client.new('localhost:11211')
client.set('author', '村上 春樹')
client.set('sentence', '完璧な文章などといったものは存在しない。完璧な絶望が存在しないようにね。')
client.set('very_long_string', SecureRandom.hex(100_000), 60)
```

```shell
$ chmod +x list_memcached_keys
$ ./list_memcached_keys
+---------+------------------+-------------+---------------------+
| slab no | key              | size (byte) | expires at          |
+---------+------------------+-------------+---------------------+
| 1       | author           | 23          |                     |
| 4       | sentence         | 118         |                     |
| 35      | very_long_string | 200013      | 2020/01/11 12:48:36 |
+---------+------------------+-------------+---------------------+
```

CLI にテーブル形式で出力するのに [tj/terminal-table](https://github.com/tj/terminal-table) いいぞ 😎
Microsoft Graphを使ってみた

# Microsoft Graphを使ってみた
## 目的
- Office 365 の操作を自動化したい。そのためのスパイク

## 今回のゴール
- API(Microsoft Graph)を使って、Plannerのタスク一覧を取得する

## やったこと
1. [Graph Explorer - Microsoft Graph](https://developer.microsoft.com/en-us/graph/graph-explorer#) にサインインする
2. 権限を付与する
    - 今回はタスク一覧を取得するのがゴールだったので `Group.Read.All` を付与した
    - 権限付与は管理者に承諾してもらう必要がある
    - [Microsoft Graph のアクセス許可のリファレンス - Microsoft Graph | Microsoft Docs](https://docs.microsoft.com/ja-jp/graph/permissions-reference)
3. APIを実行

## 試したAPI
- `GET my profile` 自身の情報を取得する
    - https://graph.microsoft.com/v1.0/me/
- `GET all groups I belong to` 自身が所属するグループを取得する
    - https://graph.microsoft.com/v1.0/me/memberOf
    - ここで言うグループはMicrosoft Teamsのチームと同じっぽい
- `GET all Planner plans associated with a group` グループに関連付いているPlannerのプランを一覧取得する
    - https://graph.microsoft.com/v1.0/groups/{group-id-with-plan}/planner/plans
    - `group-id-with-plan` にグループの `id` を指定する
- `GET all Planner tasks for a plan` プラン内のタスクを一覧取得する
    - https://graph.microsoft.com/v1.0/planner/plans/{plan-id}/tasks
    - `plan-id` にプランの `id` を指定する
    - これでプラン内のタスクが一覧取得できる
        - `percentComplete` : 進捗状況。 `100` なら完了
        - `appliedCategories`: ラベル。1つ目のラベルが設定されていると `"category1": true` になる
        - `assignments`: 担当者。権限が足りてないせいか、名前が `null` になってた
        - GET https://graph.microsoft.com/v1.0/planner/tasks/{task-id}/details を使わないとチェックリストは取得できなさそう
Jupyter学習ノート_002

## Google colabにJavaをコンパイルや実行する方法

下記の手順で実現できます。

 - Google colabへ接続して、Google driveにも接続する

```test1.py
#google driveをマウントする
from google.colab import drive
drive.mount('/content/drive')
```
 - サンプルソース実行までの処理

```test2.py
#カレントディレクトリを設定する
import os
import shutil
os.chdir('/content/drive/My Drive/Java_sample')
path_name = os.getcwd()

#ソースをアップロードして、指定フォルダーへコピーする
file_name = '/HelloWorld.java'
path_all = path_name + file_name
print(path_all)
shutil.copy(file_name, path_name)

#コマンドでJavaソースをコンパイル＆実行する
!javac HelloWorld.java
!java HelloWorld
```
 - セルという仕組みでテストのため２つを分けています。（実施結果）
![実施結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551056/2161101f-a2b4-91a5-3b5e-bb91883fc9e9.png)
【個人開発】Twitterのプロフ画像からステータスを分析するWebアプリを作った話

# はじめに
Webアプリの本格的な個人開発は初めてだったがようやくリリースまで漕ぎ着けたので、その内容をここにまとめる。以下より、ご利用いただけます！
https://sukauta.site

# 作ろうと思ったきっかけ
友人がWeb開発をしていることもあり、最近のWebアプリ事情に興味を持ち、軽い気持ちで調べたところのめり込んだのがきっかけです。学生時代にHTMLやJS、PHPを用いて簡易的な掲示板アプリを作ってみた経験はあるが、卒業以降は触れていませんでした。昨年一念発起で作ってみようと、最近のWebアプリのフレームワークやクラウドサービスをコツコツ勉強しながらのスタートでした。

# どんなアプリ？
表題の通り一言でいうと「Twitterのプロフ画像からステータス値を分析するアプリ」です。以下のようにTwitterのIDを入力すると、そのIDに紐付いているプロフ画像を画像処理し、４つのステータス値（攻撃力、守備力、素早さ、運）を分析します。分析結果はTwitter上でシェアすることができます。筆者自身がゲーム好き、ドット絵好きというのもあり、レトロゲーム感のあるタッチに仕上げています。
<img width="587" alt="スクリーンショット 2020-01-11 10.56.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431401/29ecadb8-c30f-7991-a2e2-19d6fcc80cf0.png">


# 使用したサービス、フレームワーク
- サービス
    - Neylify・・・フロントエンド用
    - AWS(EC2, ACL, ELB, S3, Route53)　・・・バックエンド用

- フレームワーク
    - Nuxt.js・・・OGPの動的生成のため
    - Django・・・バックエンドの学習、Pythonの使用経験があるため

# システム構成
システム構成は以下のような感じ。
<img width="516" alt="スクリーンショット 2020-01-11 12.21.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431401/92ebbfc6-e32c-f3b5-b80c-616ad3ff2cce.png">


それぞれの処理の流れをざっくりと下記します。

1. クライアントがユーザからTwitterIDの入力を受け付ける
2. クライアントがサーバへ受け付けたTwitterIDで画像分析を依頼する
3. サーバーがWebスクレイピングにより、TwitterIDに紐付いているプロフ画像を取得する
4. サーバーがプロフ画像を分析し、ステータス値を取得する
5. サーバーが分析結果画像を生成し、生成した画像をS３へ保存する
6. サーバーがクライアントへ画像分析結果としてS3の保存先の結果を応答する
7. クライアントが応答内容でS3サーバから画像分析結果を取得し、表示する

# 初の個人開発で得られた知見
## まずがMVPを作るの大事
月並みなことだと思いますが、まずはMVP（Minimum Valuable Product）を作ってデプロイすることが大切だと思いました。デプロイすることで、Google Analiticsでアクセス状況をみると、「より良いものを・・・！」と開発モチベーションがグンと上がります。また、今回がWebアプリとしての初デプロイでしたので、セキュリティ関連の情報も気にするようになりました。

## 周囲の人に話すの大事
いわゆる宣言効果というものですが、自分自身にプレッシャーを与えることでモチベーションの維持に繋がります。また、話題の一つにもなるので、初対面の人でもより自分に興味をもってもらえるようになると思います。

## 実際に手を動かすことが大事
自分は勉強をする上で「アウトプットこそ最大のインプット」だと思っています。また、実際に手を動かすことで想定していたこととは別の課題が色々見えてきます。例えば、Webアプリを始めた頃、OGPについては全く知らず、Twitter側でよしなに生成してくれるものだと思っていました（笑）。

## 自作アプリを使ってもらえることに感動
友人が初めて使ってくれた時は何事にも代えがたいような感動を覚えました。また、やはり実際に使ってもらうことで多少なりともフィードバックや、自分なりの気づきが得られるのでより良いWebアプリを作る上での指針を立てやすいと思いました。

# おわりに
ひとまずはデプロイまでできてよかった。正直まだまだなところが多いので、少しずつ機能追加していければと思います。フィードバックがありましたら、是非お願いいたします！
最後まで読んでいただきありがとうございました！
[iOS/Swift] アプリ開発の実務的アプローチで学ぶデザインパターン ～Flyweight～

この記事シリーズは、iOS/Swiftエンジニアである執筆者個人が、
**ごく普通のiOSアプリ開発でよくある状況**や
**Swiftのコアライブラリやフレームワークで使われているパターン**に
着目してデザインパターンを学び直してみた記録です。

**関連記事一覧**
[[iOS/Swift] アプリ開発の実務的アプローチで学ぶデザインパターン](https://qiita.com/y-some/items/96db5f24ded4a1fab48f)

# Flyweightパターン概要

- **一度生成したインスタンスを使いまわしてメモリ消費や処理時間を抑える手法**、すなわちキャッシュのことです。
- キャッシュはstatic変数またはSingletonで宣言します（単一インスタンスでないとキャッシュとして意味をなさないので）。
- GoFのでデザインパターンでは**構造に関するパターン**に分類されます。

# 使い所

- サーバー上の画像をアプリ上で表示する時、一度取得した画像はアプリ内にキャッシュしておき、毎回サーバーにアクセスすることを避ける、とか。

# サンプルコード

Swiftバージョンは 5.1 です。

```swift
class ImageRepository {
    // 画像キャッシュ
    static var imageCache = NSCache<AnyObject, AnyObject>()

    class func requestImage(from urlString: String, completion: @escaping (UIImage?) -> Void ) {
        // キャッシュを探して存在する場合はそれを使う
        if let cacheImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
            completion(cacheImage)
            return
        }
        
        guard let url = URL(string: urlString) else {
            return
        }
        
        // キャッシュに存在しない場合はサーバーから取得する
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let error = error {
                print("画像取得失敗: ", error)
                return
            }
            
            guard let data = data, let image = UIImage(data: data) else {
                return
            }
            // 画像を取得できたらキャッシュに保存する
            imageCache.setObject(image, forKey: urlString as AnyObject)
            completion(image)
        }.resume()
    }
}

// Usage
class ViewController: UIViewController {
    // 省略
    @IBAction func tappedButton(_ sender: Any) {
        let imageView = UIImageView()
        let urlString = "https://upload.wikimedia.org/wikipedia/commons/5/56/Donald_Trump_official_portrait.jpg"
        ImageRepository.requestImage(from: urlString) { (image) in
            DispatchQueue.main.async {
                imageView.image = image
            }
        }
    }
}
```

補足：
アプリ開発実務においては、UIImageViewのextensionとする方が使い勝手は良いかも知れません。
JWTクレーム・セット部分のデコード方法

# 概要

JavaScriptで、正確にはNode.jsで、IDトークンのペイロード部分をサクッとデコードしたかった・・・・んだけど、しょうもないところででハマッたので、メモ。

以下を目的とする。

* Node.jsの標準ライブラリの範囲で、JWTのペイロード部分を日本語を含めてデコードする

なお、「標準のライブラリで」のこだわりが無ければ、jsonwebtokenとかjwt-decodeとかのライブラリを使うのが吉。

# 検証環境

Node.js - v10.16.0


# JWTペイロード部分（クレームセット）をデコードするスクリプト



```
var atob = (base64) => {
    var buffer = Buffer.from( base64, 'base64' );
    var utf8 = buffer.toString('utf8'); // Not "ascii"
    return utf8;
};
var decodeJwt = (token) => {
    var base64Url = token.split('.')[1];
    var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');

    var encodeURI = encodeURIComponent(atob(base64));
    var decodeString = decodeURIComponent(encodeURI);
    return JSON.parse(decodeString);
};
```

引数に与えてコマンドラインから変換するなら次のようにする。引数「JST_STRINGS」に入れるべきは、例えばOpenID ConnectでのトークンEndPointから取得したJSONのid_tokenキーに格納された文字列など。

```:decode_jst.js
var atob = (base64) => {
    var buffer = Buffer.from( base64, 'base64' );
    var utf8 = buffer.toString('utf8'); // Not "ascii"
    return utf8;
};
var decodeJwt = (token) => {
    var base64Url = token.split('.')[1];
    var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');

    var encodeURI = encodeURIComponent(atob(base64));
    var decodeString = decodeURIComponent(encodeURI);
    return JSON.parse(decodeString);
};

var main = (argv) => {
    if( argv.length < 3 ){
        console.log("\n  [USAGE] node decode_jst.js JST_STRINGS\n");
        return;
    }

    var jsonObj = decodeJwt(argv[2]);
    console.log(jsonObj);

    return;
};

main(process.argv);
```

※encodeURIComponent()は要らんかも？あとで仕様をちゃんと確認しよう【ToDo】

# 補足

ブラウザのJavaScript環境でデコードする場合は、windowsオブジェクト（配下のatob関数）を用いて、以下の記事の方法で出来る。

* https://qiita.com/johnslith/items/d11f827f8b14913b4a28


# 参考サイト

* IDトークンが分かれば OpenID Connect が分かる＞IDトークン
    * https://qiita.com/TakahikoKawasaki/items/8f0e422c7edd2d220e06#7-id-%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3

* JSON Web Token (JWT) - OpenID Foundation Japan＞3.1. JWTの例
    * https://openid-foundation-japan.github.io/draft-ietf-oauth-json-web-token-11.ja.html#Overview
C言語 不完全なreplace関数, split関数自作(自分用メモ) 

C言語を使う仕事が終わりかけなので自作関数備忘録。

## split
配列ではなく、区切り文字までを元の文字列から取り除き、取得する。

```C:これがしたい
char token[4] = {0};
char str[] = "a,,c";
getFirstToken(token, ",", str);  // token[0] = "a", str = ",c"
getFirstToken(token, ",", str);  // token[1] = "",  str = "c"
getFirstToken(token, ",", str);  // token[2] = "c",  str = ""
getFirstToken(token, ",", str);  // token[2] = "",  str = ""
```

標準ライブラリの`strtok_s`を使っていたが、空白を飛ばしてしまうのが不都合だった(上の例でいえば、`token[1]="c"`になる)。

```C:GetFirstToken

//最初のトークンを取得し、元の文字列からは消去
//Shift_JIS未対応
VOID GetFirstToken(CHAR* token, CHAR* delimiter, CHAR* string)
{
if (string[0] == '\0') {
token[0] = '\0';
return;
}
CHAR* p = strstr(string, delimiter);
if (p == string) {
    strcpy_s(string, strnlen_s(string, DATA_LINE_MAX_LENGTH), string + DELIMITER_LENGTH);
    token[0] = '\0';
    return;
}
// If Last token, make the original string empty and return.
if (p == NULL) {
    for (int i = 0; i <= strnlen_s(string, DATA_LINE_MAX_LENGTH); i++) {
        if (string[i] == '\0') {
            token[i] = '\0';
            string[0] = '\0';
            return;
        }
        token[i] = string[i];
    }
}

for (int i = 0; i < p - string; i++) {
    token[i] = string[i];
}
token[p - string] = '\0';

strcpy_s(string, strnlen_s(string, DATA_LINE_MAX_LENGTH), p + 1);
}
```

### 欠点
- 1バイトずつチェックしているので、Shift_JISのダメ文字が含まれていると文字化けする。
例えば、delimiterが`"|"(文字コード7c)`だった場合、`「ポ」(83 7c)`, `「弓」(8b 7c)`, `「竹」(92 7c)`などは2バイト目が区切り文字と同じになり、1バイト目と2バイト目を別々のascii文字として読むため文字化けする。ダメ。
- `strtok_s`の挙動をまねようとしている割にはポインタを返していない。
- 文字列の最後が空文字だった場合に、tokenの数が未知だと空文字なのか全トークン取得済みなのか判別できない

## replace
ほとんどこの記事のまんま。http://urin.github.io/posts/2018/replace-string-by-c-lang
変数名からすべてめちゃくちゃ読みやすかった。
`"\" → "\\"`のように、、置換後の文字が置換前の文字を含んでいると変な感じになったので、最後にポインタをずらす処理を追加している。


```C:replace
//2文字まで→3文字までの置換しか対応していない。数字は変えられる
CHAR* replace(CHAR str, CHAR replaced, CHAR* substitution) {
CHAR* replacedPos = str;
size_t lenReplaced = strnlen_s(replaced, 2);
size_t lenSub = strnlen_s(substitution, 3);

while ((replacedPos = strstr(replacedPos, replaced)) != NULL) {
    CHAR *lastPart = replacedPos + lenReplaced;
    memmove(replacedPos + lenSub, lastPart, strlen(lastPart) + 1);
    memcpy(replacedPos, substitution, lenSub);
    replacedPos += lenSub;
}
return str;
}
```
JuliaのJupyterでプロット動画を素早く確認する

2020/02/17 「折衷案」追記

#さくさくと表示させます
**Julia**は良い言語だが、数値計算の結果を`Plots`の`@gif`や`@animate`とかで**動画作成すると時間がかかる**のが難点
（数値計算が早い分、動画出力の時間が余計に気になってしまう）

今回、**Jupyter-notebook**で素早く**「確認作業」**をします。
こんな感じです
![Plots_anime-Mozilla-Firefox-2020-01-11-11-14-08.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366073/d205c5fd-3102-d36a-0ecb-c57ef5082e5a.gif)




#解説
今回は適当に動くx,y データを300個づつ用意してます。

```julia
using Plots
x,y=rand(300),rand(300)
for i in 2:length(x)     #ランダムウォーク"的"に適当に動いてもらいます
    x[i]=x[i-1]+x[i]-0.5
    y[i]=y[i-1]+y[i]-0.5
end
```
普通にgif動画を出力すると

```Julia
@gif for i in 1:length(x)
    scatter([x[i]],[y[i]],legend=:none,lims=(-5,5),size=(250,250))
end
```
8.140788 seconds 
![tmp.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366073/20cec998-3546-5b18-b4a8-00f9104b0cb9.gif)



と、家の2013年製i5マシンでは**約8秒待ち**です。
(1回はプロット済み)

これを確認用に

```julia
for i in 1:length(x)
    IJulia.clear_output(true)
    scatter([x[i]],[y[i]],legend=:none,lims=(-5,5),size=(250,250))|>display
end
```

とすると最初のgif動画の様に**すぐさま表示**し始め、動きを確認できます。

内容は
`IJulia.clear_output(true)`で**Out欄を削除**し、
`scatter([x[i]],[y[i]],・・・)|>display`で**Out欄に静止画表示**
を**繰り返す**だけです。

`@gif`等によるpng出力&ffmpeg変換の時間がかからず、プロット画像のできたそばから表示されるので、**非常に素早い**ですが、変換してないので、当然**動画は残りません**。

`IJulia`の関数を使うので**Jupyter専用**になりますし、fpsはマシン性能次第ですので調整が必要な場合もあると思います。

#「折衷案」
折衷案ですが、使い勝手がいいです。

```julia
anim = @animate for i in 1:length(x)
    IJulia.clear_output(true)
    scatter([x[i]],[y[i]],legend=:none,lims=(-5,5),size=(250,250))|>display
end

gif(anim,fps=10)
```

pngファイルを作りつつ、上記の連続静止画表示をします。
若干ファイル生成時間が増えますが、gif化がすぐにできるので便利です。
最近はこっちやってます。

#おまけ
###バックエンド`unicodeplots()`
`@gif`は画像ファイルの形式にできないと扱えないのですが、今回のは表示&削除を繰り返すだけなので
![Plots_anime-Mozilla-Firefox-2020-01-11-11-02-23.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366073/e4505fe2-98f5-29eb-507f-3fccfdcc4e32.gif)
のようなテキストも動画的に確認できます。
何か生かせるといいですね。

#最後に
本当は`@gif`が楽なのでいろいろ試したのですが、png変換がボトルネックっぽく、ffmpegに直で渡せたらもう少し早くできるのかもしれませんが、分かりませんでした。何か知ってる方、教えて下さい。

以上、ありがとうございました。
Git Hubアップロード方法

#1 GitHubに新しいレポジトリを作り、ローカルレポジトリと同期する。
Git Hubにて New repositoryを選択する。

![7.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/75ad17aa-e9d8-0d26-8538-572c0e971f35.jpeg)

新しいrepository名を登録する。 GIt Hubに作成した新しいrepository名と同じ名前を持ったフォルダーをローカルに作成する。かつ、そのフォルダーの上で右クリックしGitクローンを実行する。

![8.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/4421a1c5-fd0e-cb6a-c5c7-4cee34b9fdae.jpeg)

![9.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/f473101f-03a9-b2d8-cd78-53393363adc0.jpeg)



# 2 consoleレベルでgithubにコミットする

git remote add origin “リモートリポジトリのURL”
git remote -v
git add .
git commit -m  "T"
git push -u origin master



#2 ローカルの内容をGit Hubにアップロードする。
ローカルリポジトリフォルダーにて右クリック。GItコミットを選択
![10.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/fe8f172a-8eb9-d219-64fb-a967f8ef2588.jpeg)

コミットを実施する

![11.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/d154ca4b-729a-47d4-6731-82670799854b.jpeg)

完了後、「プッシュ」を行う

![12.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/1a452e92-df6c-eaf7-d4c9-d57e85af1170.jpeg)

下記のようになっていることを確認してOKを行う。

![13.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/46d7e4af-f68e-53d1-f2c9-acaf33c8f3b2.jpeg)

ローカルリポジトリの内容がGit Hubにアップロードされている。


#2 git管理をやめる
git管理をしているディレクトリ内に存在する.gitを削除すればそのディレクトリがgit管理から外れる。

<img width="359" alt="100.JPG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150223/57e8e507-61d2-4b35-dab8-0ab203b3145a.jpeg">
[WSL] Windows 10 ストアアプリ Ubuntu でユーザー名を変更する方法

# 概要
ユーザー名を変更したいアカウントにログインしたままだと変更できないので，root にログインして変更する．

# 詳細
コマンドプロンプトを起動し，次のコマンドを実行する．

```:コマンドプロンプト
C:\Users\denkiuo604>ubuntu config --default-user root
```
続けて Ubuntu を起動すると root にログインする．次のようになれば成功である．

```:コマンドプロンプト
C:\Users\denkiuo604>ubuntu
root@PC:~#
```
この状態で次のコマンドを実行する．最初のコマンドは**ユーザー名の変更**，2つ目のコマンドは**ホームディレクトリの変更**，3つ目のコマンドは**グループ名の変更**[^1]を行っている．

```:ユーザー名をoldNameからnewNameに変更する場合
root@PC:~# usermod -l newName oldName
root@PC:~# usermod -d /home/newName -m newName
root@PC:~# groupmod -n newName oldName
```
[^1]: これを行わないと，グループ名に古いユーザー名が残る．
これでユーザー名の変更が完了したので，root からログアウトする．

```
root@PC:~# logout
```
最後に，ログイン先を元に戻して終了．

```:コマンドプロンプト
C:\Users\denkiuo604>ubuntu config --default-user newName
```
再度 Ubuntu を起動すると，ユーザー名が変更されているはずである．

```:コマンドプロンプト
C:\Users\denkiuo604>ubuntu
newName@PC:~$
```

# 参考
- [Linuxのユーザー名を変更する - そんなこと猫でもできる](https://nekodeki.com/linux%E3%81%AE%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E5%90%8D%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B/)
- [グループの設定を変更する！groupmodコマンド詳細まとめ【Linuxコマンド集】](https://eng-entrance.com/linux-command-groupmod)
Twitter のスクレイピングについて

最近pythonを本格的に取り組み始めた者なのですが、python(もしくはJavaScript) を用いてTwitterで任意のユーザーが呟いたツイートの中で特定の単語を含む物を随時取得したいと考えています。
しかし最近始めたこともあり、スクレイピングに関してほとんど知識が無く、Twitterのスクレイピング禁止規約において、どこまでが許されてどこまでが許されないかがわかりません。
少し調べてみたのですが、twitterAPIを経由して検索欄から特定の単語を検索、抽出するのは規約に違反するのでしょうか？
Twitterの規約に反せず、特定の単語を含むツイートを探す方法を知っている方がおられましたらご教授頂きたいです。拙文で申し訳ございません。
ダークモードに切り替えたときに画像がダークモード用に切り替わらない問題の対応

iOS13以降でダークモードを対応しようとしたときにlightモードからdarkモードへ、またその逆に切り替えたときにColorAssetsはちゃんと切り替わるのに画像は切り替わらなかった問題に遭遇したのですが、解決したのでその方法を残しておきます。

 1. Appearancesを**Any, Light, Dark**を選択する
 2. Render AsでDefaultになっているのを**Original Image**に変更する
<img width="863" alt="スクリーンショット 2020-01-11 11.32.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176180/6dd28d6b-06bf-b2f7-a700-04a19ac0722c.png">

僕自身もcode上では`image.renderingMode(.alwaysOriginal)`みたいな記述をしていたのですが、それでは解決されず、この設定で解決しました。誰かの参考になれば幸いです。
remot3.itのMac版アプリを試してみる

リモート端末管理用アプリの[remot3.it](https://remote.it/jp)にMac対応版のアプリケーションが出たらしいので、試してみた。

#インストール

[dmgファイルがダウンロード](https://remote.it/jp/uncategorized/%e3%81%8a%e7%9f%a5%e3%82%89%e3%81%9b-%e3%83%87%e3%82%b9%e3%82%af%e3%83%88%e3%83%83%e3%83%97%e3%82%a2%e3%83%97%e3%83%aa%e3%81%a8%e3%82%b3%e3%83%9e%e3%83%b3%e3%83%89%e3%83%a9%e3%82%a4%e3%83%b3%e3%83%84/)できるので、そちらを利用します。

#インストールとデバイスの登録
アプリをインストールしremote.itにサインインすると、CLIのインストールを促されます。これでremot3.itのCLIがMacで使えるようになります。

![Screen Shot 2019-12-31 at 16.03.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/05c81c5b-9bdf-2dad-69d5-c6a5d812ea6e.png)

ログインしたらCLIのインストールを促されます。

![Screen Shot 2019-12-31 at 16.04.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/d644e2b9-7a56-ad58-5518-48372595a6eb.png)

諸々インストールが完了したら、ターミナルで```sudo remoteit signin```と実行すると、remot3.itへのログインができます。


```terminal
$ sudo remoteit signin
Username XXXXXXXX@xxxx.xxx
Password ************
You have been signed in successfully!

You can now setup your device using "remoteit setup"

WARNING: You are using a development version of the remote.it CLI, please do not use this in production applications!
```

次いで、```sudo remoteit setup```コマンドとで利用しているMacのデバイス登録が自動で行われます。かなり楽チンです。

```terminal
$ sudo remoteit setup
Device name kojis_mac_mini_local
Your device 'kojis_mac_mini_local' was registered successfully!
		
Please run 'remoteit add' to add services to your device.

WARNING: You are using a development version of the remote.it CLI, please do not use this in production applications!
```

しばらく待つとWebの管理画面にMacが登録されました。

![Screen Shot 2019-12-31 at 17.43.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/c604362e-7d45-6fc6-611c-665fba467e33.png)

#プロトコルのインストール

remot3.itで通信するためのプロトコルのセットアップもCLIから簡単に行えます。対話式で進められるようになっているので、これも簡単です。試しにSSHとVNCを登録してみます。

```terminal
$ sudo remoteit add
Which type of service is this? SSH
Port 22
Service name SSH
Your SSH service named 'SSH' has been added and is running at 127.0.0.1:22

WARNING: You are using a development version of the remote.it CLI, please do not use this in production applications!

$ sudo remoteit add
Which type of service is this? VNC
Port 5900
Service name VNC
Your VNC service named 'VNC' has been added and is running at 127.0.0.1:5900

WARNING: You are using a development version of the remote.it CLI, please do not use this in production applications!

```

はい、これでプロトコルのインストールも完了しました。試しにiPadからVNCでアクセスしてみると・・・。

![スクリーンショット 2020-01-11 11.23.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/102769/6f1a897e-e5db-8755-605d-844aba559cc8.png)


はい、これだけでVNCビューアによるアクセスができてしまいました。（全然リモート感のないキャプチャになってしまいましたが）今回iPadからアクセスをしていますが、利用したアプリは[Remoter Pro](https://apps.apple.com/jp/app/remoter-pro-vnc-ssh-rdp/id519768191)になります。


#まとめ
リリース当初は色々とコマンドの初期化などが大変だったremot3.itがだいぶ楽にMacにも適用が可能になりました。これだけ楽になれば、もう環境構築に悩む必要もなさそうですね。まだまだUpdateがかかるようなので、これからにも期待です。
VCIアイテムの角度をバーチャルキャストのディスプレイみたいに補正をかける。

わかりにくいかもしれないけど15度単位の角度に補正されています。
![ダウンロード (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150633/4a8a65da-02eb-9747-0b02-204590336fed.gif)

#解説も面倒なので答えをぽん
ざっくり、下のコードを使えばいけるぜ！！

```lua:CorrectRotation.lua
---角度を15度単位で補正する。第2引数を1から90で指定すればその角度単位で補正する。第2引数は省略可。
---@param subItem ExportTransform
---@param degree number
local function CorrectRotation(subItem,degree)
    --degreeが数値で0<degree<=90ならdegreeの値を使う。そうでないなら15を使う。
    local localDegree = (type(degree) == "number" and 0 < degree and degree <= 90 ) and degree or 15
    --subItemの回転量をx,y,zのeuler角度で取得する。
    local subItemEuler = subItem.GetRotation().eulerAngles
    --subItemのrotationに補正をかける
    subItem.SetRotation(Quaternion.Euler(math.floor(subItemEuler.x / localDegree + 0.5)*localDegree,
        math.floor(subItemEuler.y/ localDegree + 0.5)*localDegree,math.floor(subItemEuler.z/ localDegree + 0.5)*localDegree))
end
```

#使用例だよ



```lua:
local correctSubItemName = "Item"

local correctSubItem = vci.asset.GetSubItem(correctSubItemName)

---角度を15度単位で補正する。第2引数を1から90で指定すればその角度単位で補正する。第2引数は省略可。
---@param subItem ExportTransform
---@param degree number
local function CorrectRotation(subItem,degree)
    --degreeが数値で0<degree<=90ならdegreeの値を使う。そうでないなら15を使う。
    local localDegree = (type(degree) == "number" and 0 < degree and degree <= 90 ) and degree or 15
    --subItemの回転量をx,y,zのeuler角度で取得する。
    local subItemEuler = subItem.GetRotation().eulerAngles
    --subItemのrotationに補正をかける
    subItem.SetRotation(Quaternion.Euler(math.floor(subItemEuler.x / localDegree + 0.5)*localDegree,
        math.floor(subItemEuler.y/ localDegree + 0.5)*localDegree,math.floor(subItemEuler.z/ localDegree + 0.5)*localDegree))
end

---[not SubItemの所有権&Grab状態]アイテムをUngrabしたときに呼ばれる。
---@param target string @UngrabされたSubItem名
function onUngrab(target)
    if target == correctSubItemName then
        CorrectRotation(correctSubItem)
    end
end
```
AWS上に構築したWordPressで「504 gateway timeout」が頻発した時の対処法

![504-gateway-timeout-error-in-chrome.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178837/4d58bb9b-74ce-c3c5-19f7-f32dee23d7fc.png)

#概要
AWS上に構築したWordPressで「504 gateway timeout」が頻発したので、その際の対処方法を記載します。

#前提
AWS上に構築したWordpressは以下のようなアーキテクチャで動作しています。
route53は使用しないシンプルな構成になってます。

　インターネット
　↓
　ALB(セキュリティーグループのインバウンド設定:http,https) 
　↓
　EC2(セキュリティーグループのインバウンド設定:http) 

#事象
504 gateway timeoutが頻発した。
うまく表示される時もあれば、うまくいかないこともあったりと不安定な状態。
またページの読み込みもなんだか遅いような感覚。

#解消方法
上記の前提にもあるようにALB→EC2の通信は httpでしか疎通しないアーキテクチャになっている。

ALBのターゲットグループを確認したところ、http,httpsの両方が設定されていた。これをhttpのみ設定したところ504の事象が解消し、ページの読み込みが早くなった！

おそらくALBからhttpsでEC2に送った際に、EC2側で通信が受け取れないために発生したみたい。
【OGP】Twitter共有時にURL文字列を非表示にする

# はじめに
OGPに対応したTwitterの共有機能を作成したところ、共有ツイート時、以下のようにハイパーリンクのついたURL文字列が表示されてしまう。
<img width="586" alt="スクリーンショット 2020-01-11 10.56.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431401/d94b1b71-1c8d-d415-c7ee-a0b51f78ce9b.png">

# 解決方法
以下のように**文字列の最後**にURLを入れる。

```
Twitterのプロフ画像の解析が完了しました！ #スカウタ
https://sukauta.tech/share/15b04dbd91bb4b539d282ee87c2c35fe/
```

すると以下のようにURL文字列のないツイートが生成される。

<img width="587" alt="スクリーンショット 2020-01-11 10.56.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431401/a4d87f55-cf1d-2fa2-ff5b-dc7f25bd59ae.png">


# おわりに
Webアプリの宣伝です。Twitter画像解析アプリ「スカウタ！」を運営しています。よかったら使ってみて下さい！
https://sukauta.site/
sdkman の使い方

1) インストール

>参考ページ
[Installation](https://sdkman.io/install)

```bash
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
```

インストールの確認

```text
$ sdk version

SDKMAN 5.7.4+362
```

2) springboot のインストール

```bash
sdk install springboot
```

インストールの確認

```text
$ spring --version
Spring CLI v2.2.2.RELEASE
```

サンプルのプログラム

```groovy:app.groovy
@Controller
class ThisWillActuallyRun {

    @RequestMapping("/")
    @ResponseBody
    String home() {
        String str_aa = "*** start ***<p />"
        str_aa += "Hello<p />"
        str_aa += "*** end ***<p />" 
        return str_aa
    }

}
```

実行

```bash
spring run app.groovy
```

クライアントでアクセス

```bash
curl http://localhost:8080
```

ポートを変更して実行する方法

```bash
spring run app.groovy -- --server.port=9000
```

クライアントでアクセス

```bash
curl http://localhost:9000
```

3) Gradle のインストール

```bash
sdk install gradle
```

インストールの確認

```text
$ gradle -version

------------------------------------------------------------
Gradle 6.0.1
------------------------------------------------------------

Build time:   2019-11-18 20:25:01 UTC
Revision:     fad121066a68c4701acd362daf4287a7c309a0f5

Kotlin:       1.3.50
Groovy:       2.5.8
Ant:          Apache Ant(TM) version 1.10.7 compiled on September 1 2019
JVM:          13.0.1 (Oracle Corporation 13.0.1+9)
OS:           Linux 5.4.10-arch1-1 amd64
```
firebase-admin sdkでファイルのアップロードとダウンロードURLの取得


firebase (client) SDKとやり方が違うのでメモ。

## 最初に知っておいたほうがいいこと

* クライアントのSDKとはやり方（使える関数とか）が違う。
* アップロードしたファイルが[Web Consoleのバグで閲覧できない](https://github.com/firebase/firebase-admin-node/issues/694)（これでしばらくはまった）。
* ダウンロードURLはStorageのパスそのままではなく、[署名入りの専用URL](https://cloud.google.com/storage/docs/access-control/signed-urls)を取得する必要がある
* 返り値に含まれるmediaLinkでも閲覧はできるが、CGPの方でallUsersに閲覧権限を付与する必要がある（使わない）
* vscodeが生成するリンクはゴミが入ってうまく表示されない（vscode依存）

## 実装

実装自体はなんら難しくありません。

```js:index.js
const admin = require('firebase-admin');
admin.initializeApp({
    credential: admin.credential.cert("/path/to/key.json"),
    storageBucket: "xxxxxxxx.appspot.com",
});

const bucket = admin.storage().bucket();
const localFilePath = "./test.pdf";
const cloudFilePath = "docs/test.pdf";

const main = async () => {

    try {
    
        //upload
        const res = await bucket.upload(localFilePath, { destination: cloudFilePath, metadata: { contentType: "application/pdf" } });
        
        
        //get download url
        const url = await bucket.file(cloudFilePath).getSignedUrl({
            action: 'read',
            expires: '12-31-3020' //1000年後に設定
        });
        
        console.log(url);
        
    } catch (e) {
        console.log(e);
    }

}

main();
```

ダウンロードURLとして下記のようなものが生成されます。

```bash
[
  'https://storage.googleapis.com/test-fc01e.appspot.com/docs/test.pdf?GoogleAccessId=firebase-adminsdk-d9f6b%40test-fc01e.iam.gserviceaccount.com&Expires=33166249200&Signature=mi7v8aF7j%2FJSmfgszijUDlIF9dW0PNe3hd4fAPs7rqjja5yMV8ClKZgKj%2BAMhatNdQIscz2306%2BF7uMyXxP8dJgjKGuJHJowgs28R1zq%2FnMy3D5iQGUEitJWdmpebO6C87EALVh1b8JE5nB6BKsv0NKp9%2FFHLq3epTP01z6S68%2BPSXXwZz4HlSBwLOjUMPvrNLUf%2B80dCgrh5pjKQKJm6VAIUQmTpE8RBk4qQLiQKvU5gy8e8F2yzS%2BA1kV9cM8C%2F3ARJE9HGe%2FxBTFCBynbpliPc09YuhTUcQlRPnlYpeTklAmPq9%2BK1XFINs0mUs5534mzifHR0TAGuUN%2BLRrVqjw%3D%3D'
]
```
Terraformで警告エラー「Warning: Quoted type constraints are deprecated」

![https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_321850_853d8fc1-22f8-159f-0191-0271e18659ee.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178837/2baaf7ea-6b8b-2ccc-0905-06e9de32a99b.jpeg)
#概要
以下の記事を参考にTerraformでIAMユーザ作っていたら警告エラーが出たのでメモ

TerraformでIAMグループ・ユーザを作成する
https://qiita.com/digitalpeak/items/427f6cf7ed14f4187cd2
#事象
Terraformでplanを実行したら以下のような警告エラーが出た

```
Warning: Quoted type constraints are deprecated

  on variables.tf line 2, in variable "aws_iam_user":
   2:   type = "list"

Terraform 0.11 and earlier required type constraints to be given in quotes,
but that form is now deprecated and will be removed in a future version of
Terraform. To silence this warning, remove the quotes around "list" and write
list(string) instead to explicitly indicate that the list elements are
strings.
```

よくわからないのでGoogle翻訳にぶち込んでみる

>
警告：引用符付きの型制約は、変数 "aws_iam_user"のvariables.tf行2で非推奨になりました。
現在、フォームは非推奨であり、Terraformの将来のバージョンで削除されます。
>
この警告を黙らせるには、「リスト」を囲む引用符を削除し、代わりにリスト（文字列）を記述して、リスト要素が文字列であることを明示的に示します。 

書き方が古いらしい。。。

#解消方法
 「type =   “list”」を「type =   list(string)」にすることで解消した。
Scrapyでパイプライン処理コードをspider別にファイル分離する方法

# はじめに

[Scrapy](https://scrapy.org/)でスクレイピング対象(spiderファイル)を増やしていった場合、パイプライン処理コードが同一ファイル内に増加してしまい見通しが悪くなり保守性に問題が出てきた。
最終的にはspider別にパイプライン実装ファイルを分離する事が出来たので方法を紹介する。



# 暫定対象方法

Scrapyのプロジェクトには`setting.py`という設定ファイルが存在する。
`setting.py`内に`ITEM_PIPELINES`の設定項目がありspiderが複数存在しても、
ここで指定した単一の実装ファイル内のクラスにパイプライン処理を集約するしかないと当初思っていた。

```python:setting.py
ITEM_PIPELINES = {
    'example_project.pipelines.DBPipeline': 100,
}
```

- siperの名称で処理分岐するパターン

spider名称をキーにルーティングしていたが、spiderが増加していくとコードの見通しが悪くなる事が明白。

```python:pipelines.py
class DBPipeline(object):
    def process_item(self, item, spider):
        if spider.name in ['example_spider']:
            # example_spiderのパイプライン処理
        
        if spider.name in ['example_spider2']:
            # example_spider2のパイプライン処理 
```

# 結論

下記の様にspider毎に`custom_settings`で`ITEM_PIPELINES`項目を設定すると、
パイプライン処理の実装ファイルを個別にする事が出来る。[^1]

```python:example_spider.py
class ExampleSpider(scrapy.Spider):
    custom_settings = {
        'ITEM_PIPELINES': {
            'example_project.example_pipelines.ValidationPipeline': 100,
            'example_project.example_pipelines.DBPipeline': 200,
        }
    }
```

```python:example_spider2.py
class ExampleSpider２(scrapy.Spider):
    custom_settings = {
        'ITEM_PIPELINES': {
            'example_project.example_pipelines2.DBPipeline': 100,
        }
    }
```

`custom_settings`で設定した通り以下のパイプライン処理に個別ルーティングされる。

```python:example_pipelines.py
class ValidationPipeline(object):
    def process_item(self, item: scrapy.Item, spider: scrapy.Spider):
        # example_spider.pyを実行した時に処理される

class DBPipeline(object):
    def process_item(self, item: scrapy.Item, spider: scrapy.Spider):
        # example_spider.pyを実行した時に処理される
```

```python:example_pipelines2.py
class DBPipeline(object):
    def process_item(self, item: scrapy.Item, spider: scrapy.Spider):
        # example_spider2.pyを実行した時に処理される
```

以上でスクレイピング対象（spider）が増加してもパイプライン処理コードの見通しを良い状態で保てるようになった。

[^1]: 同様に`SPIDER_MIDDLEWARES`など別の項目についてもカスタム設定出来ると思われます。
Terraformで警告エラー「Warning: Interpolation-only expressions are deprecated」

![terraform-eyecatch.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178837/7d963a3d-d3e3-fead-70c9-1025b868e3c8.png)
#概要
以下の記事を参考にTerraformでIAMユーザ作っていたら警告エラーが出たのでメモ

TerraformでIAMグループ・ユーザを作成する
https://qiita.com/digitalpeak/items/427f6cf7ed14f4187cd2

#事象
Terraformでplanを実行したら以下のような警告エラーが出た

```
Warning: Interpolation-only expressions are deprecated

  on iam_user.tf line 2, in resource "aws_iam_user" "example_exp":
   2:   count         = "${length(var.aws_iam_user)}"

Terraform 0.11 and earlier required all non-constant expressions to be
provided via interpolation syntax, but this pattern is now deprecated. To
silence this warning, remove the "${ sequence from the start and the }"
sequence from the end of this expression, leaving just the inner expression.
```
よくわからないのでGoogle翻訳にぶち込んでみる

>
警告：補間のみの式は、リソース「aws_iam_user」「example_exp」のiam_user.tf行2で非推奨です：2：count = "$ {length（var.aws_iam_user）}" Terraform 0.11以前では、非定数式が補間構文を介して提供されますが、このパターンは非推奨になりました。
>
この警告を黙らせるには、この式の最後から「$ {シーケンスを開始し、}」シーケンスを削除して、内側の式のみを残します。テンプレートに複数の補間シーケンス、またはリテラル文字列と補間の混合が含まれている場合、式から文字列を構築するためにテンプレート補間構文が引き続き使用されます。この非推奨は、完全に単一の補間シーケンスで構成されるテンプレートにのみ適用されます。 （および他の同様の警告）

なるほど、今インストールしているバージョンで、以下の書き方が古い臭いですって怒られているみたい。

```
2：count = "$ {length（var.aws_iam_user）}"
```

#解消方法
バインドの書き方を変えたら直った！

```
2：count = length（var.aws_iam_user）
```
初心者がvuexでハマった沼

# アクション内で別モジュールのアクションを発火したい

### 状況
`store/foo.js`のactionAから`store/bar.js`のactionBを呼び出したい

```foo.js
export const actions {
  actionA(context){
    ...
    // bar.jsのactionBをdispatchしたい
  }
}
```

```bar.js
export const actions {
  actionB(context){
    ...
  }
}
```

### 失敗コードと成功例

```foo.js
export const actions {
  actionA(context){
    ...
    // 失敗
    context.dispatch('bar/actionB')
    // 成功
    context.dispatch('bar/actionB', {root: true})
  }
}
```

### ポイント
`{ root: true }`を指定することで他のモジュールのアクションを発火できるようになります。


# commit時に複数の変数を渡したい

### 状況
foo.jsのstateをまとめて変更したいのでcommitに複数の引数を渡したい。

### 失敗コードと成功例
```foo.js
export const state {
  hoge1: ''
  hoge2: ''
}

export const mutations {
  // 失敗コード
  setHoge(state, hoge1, hoge2){
    state.hoge1 = hoge1;
    state.hoge2 = hoge2;
  }

  // 成功コード
  setHoge(state, {hoge1, hoge2}){
    state.hoge1 = hoge1;
    state.hoge2 = hoge2;
  }
}
```

### ポイント
mutationに渡す引数が複数の場合は`{}`で囲んでオブジェクトにする必要があります。


以上私がハマった沼を紹介しました。お役に立てれば嬉しいです。よかったらいいね！してくださると励みになります！
【AWS完全に理解したへの道】データベース(RDS/ElastiCache/DynamoDB)基本編

[【AWS完全に理解したへの道】 VPC 基本編](https://qiita.com/y-u/items/40fa729d54ebab52fcb3)
に続く、データベース(RDS/ElastiCache/DynamoDB)基本編(項目の然るべき順序的なものは後で整理するかも)

AWSが提供しているデータベースサービスは以下の4つ。

1. リレーショナルデータベースサービス ： RDS
2. NoSQLデータサービス : DynamoDB
3. インメモリキッシュサービス : ElastiCache
4. データウェアハウスサービス : Redshift

## 1. RDS
RDSで選択できるデータベスエンジンは以下6種類。

1.1 Amazon Aurora
1.2 MySQL
1.3 MariaDB
1.4 PostgreSQL
1.5 Oracle
1.6 Microsoft SQL Server

### 1.1 Amazon Aurora
Amazon AuroraはMySQLと互換性のあるAWS独自のリレーショナルデータベースエンジン。
※各データベースエンジンの差異についてここでは触れない。

### マルチAZ
複数のAZにRDSインスタンスを配置して可用性を高め、フェイルオーバーサポートを提供する。
マルチAZ配置を使用するDBインスタンスでは、同期データレプリケーションが発生するため、シングルAZ配置よりデータの書き込みとコミットのレイテンシーが増加する可能性がある。

### 自動バックアップ機能
1日1回自動的にデータのバックアップ(スナップショット)を取得するRDS標準機能。

* バックアップの取得時間帯を設定可能
* バックアップの保持期間は0~35日の間で指定可能  
(デフォルトは7日、0日を指定するとバックアップを取得しない設定となる)
* トランザクションログも自動的に取得しており、5分に1回永続ボリュームに書き込まれる。
* 利用者の任意のタイミングでバックアップを取得することもできる。任意のタイミングで取得したバックアップは利用者が明示的に削除するまで保持される。

### ストレージ
3種類のストレージタイプを選択できる。

* 汎用SSD
* プロビジョンドIOPS
* マグネティック

それぞれの違いについては[こちら](https://awsjp.com/AWS/hikaku/EBS-Storage-Type-chigi.html)の記事がわかりやすい。

### パッチ機能
* 自動パッチ適用機能が実行される曜日/時間帯を設定できる
* パッチ適用時の数分のダウンタイムを避けるために、マルチAZ配置にする必要がある。(先にスタンバイしているマスターにパッチが適用され、ファイルオーバーした後に旧マスターにパッチを適用するといった処理が行われるため、ダウンタイムによる影響を軽減することができる。）
* 自動パッチ適用は利用者が有効/無効を設定できるが、重要なセキュリティ脆弱性が発生した場合には、自動パッチ適用を無効化していても、自動的に適用されることがある。

## 2. DynamoDB
### 特徴
* ストレージ容量を拡張
* 秒間あたりのI/O性能(スループット)を指定できる
* ストレージはSSDのみで安定したI/O性能を提供
* データを3つのデータセンタに複製することで高可用性と高耐久性を提供
* 読み込み整合性の強弱を指定することで、性能と整合性のバランスを選択可能

## 3. ElastiCache
下記２つのキャッシュエンジンから選択可能

* Memcached
##### 特徴
Key-Value Store形式
マルチノード

* Redis
##### 特徴
Key-Value Store形式
マスタースレーブ

* アクセス制限
セキュリティグループでアクセス制限する。
ElastiCacheはAZサービスなので、VPC環境ではEC2からのアクセスのみ受け付ける。

MemcachedとRedisの違いについては下記
[Redis作者自身によるRedisとMemcachedの比較](https://yakst.com/ja/posts/3243)
初心者向けReactおすすめ教材

## 概要
本文は年始から業務ではじめてReactを使うことになった筆者が学習に用いた教材を紹介するものです。他の初心者の方の参考になればと思い、記載します。

## 学んだ教材たち
### 無料
#### reactチュートリアル
https://ja.reactjs.org/tutorial/tutorial.html
なんだかんだで公式チュートリアルがわかりやすかったです。

チュートリアルの中にもありましたがこちらを触った後で、一段階ずつ[学べるガイド](https://ja.reactjs.org/docs/hello-world.html)を読んでみたら、及第点をとれるぐらいにreactを理解できているはずです。

### 有料
#### プロゲートReactコース
https://prog-8.com/languages/react

上で紹介したチュートリアルが難しいと感じたのならば、progateで勉強してみるのがいいと思います。最初は無料なのでそこから合いそうか判断し、一月分だけ契約してみるのがいいです（月額税込1078円）。また、この教材も難しいと感じるのなら、javascriptの講座を受けてみるのもいいと思います。

#### フロントエンドエンジニアのための React ・ Redux アプリケーション開発入門
https://www.udemy.com/course/react-application-development/
説明は他の教材よりは荒いですが、実際に手を動かして学べるのがいいです。また上の教材では触れていなかった、reduxなどの概念にも触れてます（わかりやすかったとは言いにくいですが）

#### React + Redux を使用したモダンフロントエンド開発
https://www.udemy.com/course/react-redux-basic/

Reactで必要なjavascriptの概念の説明も途中で挟んでくれるのがよかったです。こちらもreduxなどの概念にも触れてます（こちらもあんまりわかりやすいとは言い難かったですが）。あと教材がudemyの平均に比べて高いですが、[筆者のブログ](https://uncle-javascript.com/article/)もjavascriptに関する有益な情報が載っていておすすめです。

## まとめ
以上、初心者向けReactおすすめ教材をまとめてみました。初学者の方に役立てば幸いです。
Mac に Terraform をインストールする（homebrew 利用）

![cb14e812eaf3d5de141a6bf4d7b0258b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178837/e237522d-bcec-97ae-129f-836c6f62ee47.png)

#概要
homebrewを使ってTerraformを導入する。
※Terraformとは
　クラウドのインフラアーキテクチャを「.tfファイル」に記述/定義することで
　定義した内容の応じて自動で環境を構築してくれる。

#環境
端末:Mac book Pro（13-inch,2019）
OS:macOS Catalina 10.15.2

#前提
Homebrewの利用を前提とした導入方法になります。
※HomebrewとはmacOSでよく使われるパッケージマネージャです。ターミナルからパッケージなどをインストール際に使用するのでこのタイミングで導入しちゃいましょう！
まだHomebrewを導入していない場合は以下の記事を参照ください。
https://qiita.com/kimurayut/items/6f50cf1060e44e664c3b

#手順
以下を実行するだけです(他にも方法ありますが個人的にはこれが一番簡単)

    $ brew install terraform
Ubuntu 18.04LTS にてファイルリンクを作成する

詰まったので備忘録です。
参考：[GUIを使用してUbuntu 18.04のフォルダーへのリンクを作成する方法](https://www.it-swarm.net/ja/shortcuts/gui%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6ubuntu-1804%E3%81%AE%E3%83%95%E3%82%A9%E3%83%AB%E3%83%80%E3%83%BC%E3%81%B8%E3%81%AE%E3%83%AA%E3%83%B3%E3%82%AF%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95/998368287/)

### 方法1
Ctrl+Shiftを押しながらドラッグ＆ドロップ.



### 方法2
ファイル>設定>動作>
「シンボリックリンクを作成するアクションを表示する」にチェック.

![b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70558/d59eefdf-03ac-9446-5424-3db5ba3cb8a6.png)

![Screenshot from 2020-01-11 09-58-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70558/4eb9f396-a19e-8812-f322-1674d92dee66.png)

ファイルを右クリックすると「リンクを作成する」という項目が追加されている.

windowsのようにコピーしたいファイルをCtrl+cでコピーした後、リンクを作成したい場所で右クリックしてリンクを作成することも可能.

以上です.
MacでGit導入(Homebrew利用)

![icon_git.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178837/3f2c70e1-03d0-613f-29b3-352d44c52b00.png)
#概要
エンジニアでMac買ったらまずやることはGit導入ですね。
Gitないと何もできません。
#環境
端末:Mac book Pro（13-inch,2019）
OS:macOS Catalina 10.15.2
#前提
Homebrewの利用を前提とした導入方法になります。
※HomebrewとはmacOSでよく使われるパッケージマネージャです。ターミナルからパッケージなどをインストール際に使用するのでこのタイミングで導入しちゃいましょう！
#手順
##Xcodeをインストール
App storeからXcodeをインストール
https://apps.apple.com/jp/app/xcode/id497799835?ls=1&mt=12

##Homebrewをインストール

    $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

以下の結果が取得できればインストール成功

    $ brew doctor
      Your system is ready to brew.

##Gitのインストール
    $ brew install git

以下の結果が取得できればインストール成功

    $ git --version
      git version 2.24.1
GitLabからプロジェクトをcloneする方法

![gitlab-logotest2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178837/79942d7d-3151-b2d6-adfb-eb7824d1652b.png)

#概要
SSHの方法もあるけどSSHキーの鍵ペアを作成したり、公開鍵をGitLabに登録したりそういったことが面倒であれば以下のコマンド実行すればcloneできます。

#前提
Gitが導入済みであること

※もしGitの導入がまだであれば以下の記事を参考ください！
https://qiita.com/kimurayut/items/6f50cf1060e44e664c3b

#コマンド
    $ git clone https://gitlab.com/プロジェクトオーナーID/プロジェクト名.git
HJサーボテスターを使ってみた

# HJサーボテスター

サーボモータを買ってきて、とりあえず動作確認をするためにサーボモータを買ってみました。
説明書などが付いていなかったので、おぼえがき。

![69F5AA5E-58B8-42F5-8538-7689C18BEFE3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/f60e01f2-8ddb-ffbb-60a6-acc3c17d1799.png)

## 電源
左側のピンの真ん中がVDD、下がGNDで、4.8-6Vだそうな。
上：空
中：VDD
下：GND
で電源ボックスと繋いでいます。

![電源.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/1fbd197c-716a-fbac-7e63-9384bd36396e.png)

単三電池3本の電源ボックスにeneloopなので1.2V×3で3.6Vだと思うのですが、写真のSG92Rは動作しました。

# ボタン操作
釦は2つ付いていて、左側はモード選択、右はパルス幅の設定で両方とも3項目のトグルになっています。

* Select
    * Manual
        * ダイヤルでサーボを動作
    * ニュートラル
        * ニュートラル位置(1500μSec)固定
    * Auto
        * 反復動作（ランダム？）
* PulseWidth
    * 50H(20us 50Hz)
        * アナログサーボ
    * 125H(80us 125Hz)
        * デジタルステアリングギア
    * 250H(4us 250Hz)
        * デジタルサーボ
# ダイヤル操作
800μsecから2200μsec

#接続例
![IMG_4896.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28184/483f602d-782c-a007-314c-f0c11c4af7c0.jpeg)

サーボモータの端子は
上：SIG（制御信号）
中：VDD（電源）
下：GND
で接続しています。

今回接続したマイクロサーボ SG92Gのスペックは
* PWMサイクル：20mS
* 制御パルス：0.5ms~2.4ms
* 制御角：±約90°(180°)
* 配線：茶=GND、赤=電源[+]、橙=制御信号 [JRタイプ]
* トルク：2.5kgf・cm
* 動作速度：0.1秒/60度
* 動作電圧：4.8V
* 温度範囲：0℃~55℃
* 外形寸法：23x12.2x27mm
* 重量：9g
です。
秋月電子で買ってきました。
http://akizukidenshi.com/catalog/g/gM-08914/
Elasticsearch 基本リファレンス

#Elastic Searchの用語
メモです。


##Indices API
index、index setting, mapping, index templateを管理するAPI。

###Index API
ドキュメントをインデックスに追加する、もしくは更新する。

```
PUT twitter/_doc/1
{
    "user" : "kimchy",
    "post_date" : "2009-11-15T14:12:12",
    "message" : "trying out Elasticsearch"
}
```

###Index Template
インデックスが**作成される時**自動的に適用されるテンプレート。




##Mapping

###Mapping
インデックスされるドキュメントのフィールドがどう保存され、どうインデックスされるかを定義する。RDMのスキーマに近い概念。


##Index Module

### Index Settings
Index毎の設定。
Index作成時に設定できるstatic settingと、稼働中に変更できるdynamic settingがある。


##Analyzer
テキスト解析の仕組み。
テキスト解析はCharacter Filter, Tokenize, Token FIlterなどいくつかのステップに分けれる。

###Character filters
文字(1語)を意味で解釈して標準化する。
例えば、アラビア数字の(٠‎١٢٣٤٥٦٧٨‎٩‎)は(0123456789)と変換される。

###Tokenizer
文字列を語(token)に分割する。
例えば　"明日は晴れる"は["明日", "は", "晴れる"]と分割される。

###Token filters
Tokenizerで分割されたtoken(語)を標準化する。

例えば、City => city など、大文字は小文字の形式に変換され、同義語辞書を登録していれば同義語が一つの語に標準化される。

単語単位で実行するため、Character filterと違いTokenizerの後のプロセスとなる。
AutoML Visionで雲の識別をした話

[AutoML Visionで雲の識別をした話 - Idea, COncept, and Technology.](http://yayoi-taka.hatenablog.com/entry/2020/01/11/080323)の転載です。

##きっかけ
公開しているこちらのアプリ、「おまかせ撮影」と言う機能があります。撮った雲の種類を判別すると言うものです。ただ、これまでは単に画像の類似度を見て判定していただけなので精度はイマイチどころかそれ以下でした。
[‎「CloudWatcher」をApp Storeで](https://apps.apple.com/jp/app/cloudwatcher/id404409062?ign-mpt=uo%3D4)

そこで、GCPのAutoMLで雲を識別できないかと思い立ちました。調べたところ、すでに取り組み事例がありました。
[Googleの画像認識APIを基に、好きな画像を学習させて認識機能を簡単にカスタマイズできる「Cloud AutoML Vision」発表 － Publickey](https://www.publickey1.jp/blog/18/googleapicloud_automl_vision.html)
ハンズオンもあります。
[AutoML Vision でクラウド内の雲の画像を分類する | Qwiklabs](https://www.qwiklabs.com/focuses/8406?locale=ja&parent=catalog)
ただ、こちらのデータセット、3種類の雲しかなくて私のアプリではそのまま使えませんでした。
##データセット作成
アプリでカバーしている24種類の雲の画像を集めなくてはと言うことで。Google画像検索を使いました。手作業で取るのはしんどいのでこちらのスクリプトを利用させてもらいました。
[Google 画像検索結果のサムネイル画像を一括保存するスクリプト | AWS構築ハックノート](https://hacknote.jp/archives/40194/)
単に雲の名称で検索するとノイズが入ったり、「サンプル画像」の注釈が入ったものがヒットします。こちらはAutoMLにインポートしてから目検で除外しました。後者は検索時にサイト除外してもいいかもしれません。

結果的に1種類あたり100画像程度準備しました。
##学習

データセットのインポート、ラベル修正を行ったのち学習を実行。無料枠を使いました。私の場合3時間程度で学習を完了。雲種によっては精度は変わります。正直人間の目で見ても判別が難しいものもあるので、仕方ない部分もあるかと。
## アプリへの組み込み

ここで少しつまづきました。CoreMLでモデルをエクスポートしてアプリに組み込んだのですが、予測した際に参照するVNClassificationObservationのidentiferがデータセットの雲ラベルではなく独自に振られたランダム文字列になってしまいました。試行錯誤したのですが解決できず…。

結局、CoreMLをそのまま使うのは諦めて、FirebaseのML Kit経由で組み込みました。
[AutoML でトレーニングされたモデルを使用して画像にラベルを付ける（iOS）  |  Firebase](https://firebase.google.com/docs/ml-kit/ios/label-images-with-automl)

ただ、上記リンク先の説明、日本語と英語とで若干記述が異なります。英語の方に従って実装しないとエラーとなります。

エッジ側で画像認識できるのは性能面でも嬉しいですね。
Vim スクリプトの autocmd 実行中に、インサートモードに入って CTRL キーを入力する何かを実行したい

## はじめに

Vim スクリプトの `autocmd` で実行するコマンド中に、インサートモードに入ってから CTRL キーを押すコマンドをどうやって実行すればいいかがわからなかったので、そのやり方を調べてみました。

## やりたかったこと

Vim を起動すると同時に、インサートモードである文字列を入力したい場合を考えます。
例として、ノーマルモードで `<C-H><C-H>` と入力するとインサートモードで日付を入力する場合は以下の通りです。

```vim
nmap <C-H><C-H> <ESC>a<C-R>=strftime("%Y%m%d")<CR>
```

`<ESC>a` でインサートモードに入り、`<C-R>=` で指定された評価式 `strftime("%Y%m%d")` の評価結果を挿入します。最後の `<CR>` は入力されたコマンドを実行することを表します。

上記を実行すると、`20200111` のような日付が挿入されます。

しかし、これを `autocmd` 内で実行する方法がわかりませんでした。

## tl;dr

調べた結果、以下のような形で実現できました。

```vim
autocmd VimEnter * startinsert | call feedkeys("\<C-R>=strftime("%Y%m%d")\<CR>")
```

まず `autocmd VimEnter *` で、Vim の起動完了後に指定されたコマンドを実行します。

指定された関数は [startinsert](https://vim-jp.org/vimdoc-ja/insert.html#:startinsert) コマンドで、`i` や `a` キーからではなく、コマンドからインサートモードに入ります。

次に `|` に続けて、インサートモード後に実行する関数を指定します。

指定した関数は組み込み関数の一種である [feedkeys](https://vim-jp.org/vimdoc-ja/eval.html#feedkeys()) 関数です。この関数で、指定された文字列をユーザーがタイプしたかのように処理キューに入れてくれます。

キーの中で`<C-R>` や `<CR>` という特殊なキー入力がありますが、そのキー入力をシミュレートするにはダブルクォートと `"\..."` 記法を使うというのがミソでした。
[マニュアル](https://vim-jp.org/vimdoc-ja/eval.html#expr-quote)に書かれていますが、ダブルクオートで囲った文字列の中で、`\<C-R>` は `CTRL-R`、 `\<CR>` は `Enter`　の押下を表してくれます。

上記を指定しておくと、Vim 起動時にインサートモードで `20200111` のような日付が挿入されると思います。

## おわりに

実現のために知らない関数を探したりするのが大変でしたが、実現できるととても面白いなと感じました。

## 参考

* [how to reenter normal mode after startinsert - Vi and Vim Stack Exchange](https://vi.stackexchange.com/questions/10979/how-to-reenter-normal-mode-after-startinsert)
* [Vim: why doesn't ":normal! i" enter insert mode? - Stack Overflow](https://stackoverflow.com/questions/11587124/vim-why-doesnt-normal-i-enter-insert-mode)
c#でatcoder その11

#概要

c#でatcoderの練習問題やってみる。

#練習問題

N個の整数値を受け取り、一度の操作ですべての数を2で割る。割り切れなくなるまで割っていった時、何回割ることが出来るか？

#成果物

https://paiza.io/projects/wIr8T17Z1T58ebBla_BxTA

以上。
c#でatcoder その9

#概要

c#でatcoderの練習問題やってみる。

#練習問題

2つの整数値を受け取り、その積が2の倍数かを判定する問題です。

#成果物

https://paiza.io/projects/mLrxhiw8vPkeRN191Q7ayw

以上。
c#でatcoder その8

#概要

c#でatcoderの練習問題やってみる。

#練習問題

500円玉をA枚、100円玉をB枚、50円玉をC枚持っているとき、これらの硬貨の中から何枚かを選び、合計金額をちょうどX円にする組み合わせを求める問題です。

#成果物

https://paiza.io/projects/AD9DMtM2HyJrlrl4s-H5rg

以上。
c#でatcoder その7

#概要

c#でatcoderの練習問題やってみる。

#練習問題

1以上N以下の整数のうち、10進法での各桁の和がA以上B以下であるものの総和を求める問題です。

#成果物

https://paiza.io/projects/qAg3FF1ecSgnM7EEC83HAw

以上。
c#でatcoder その6

#概要

c#でatcoderの練習問題やってみる。

#練習問題

N枚のカードを二人で交互に取っていき、カードに書かれている数字の和の差を求める問題。

#成果物

https://paiza.io/projects/f4a8O9cV2Pfw-vRvG40mXg

以上。
c#でatcoder その5

#概要

c#でatcoderの練習問題やってみる。

#練習問題

N枚の整数値が与えられ、それらの重複しない要素の数を数える問題です。

#成果物

https://paiza.io/projects/Vt3TFgWEQzPFMbZlBv6f4w

以上。