一身上の都合により

プログラミングとかの話

会計クイズの本

大手町のランダムウォーカーさんの会計クイズの本を漸く読みました。
(即買ったのに積んでた)

amzn.to

やっぱり会計クイズは最高のコンテンツだと思う。
ほどよく頭を使うし、社会人として知っておくべき知識も身につく。

本では普段のクイズではあまり触れられない、回答にたどり着くまでのロジックやアプローチもキャラクターの会話形式で示されているので初心者におすすめ。
簿記3級程度の知識は持っていたほうがより楽しめると思う。
というか個人的には社会人はみんな簿記を3級でもいいから勉強しておくべきだと思う。

Twitterを当初から拝見している身としては、昔見たことある問題が多いのがちょっと残念ではあったが、
間違えた問題もあったので良い復習になった。

まだ会計クイズに触れたことのない人は必読の一冊。

Vue のテーブルライブラリ(2)

前回、vue-good-table に不満を覚えて他ライブラリへの移行を検討したが、
結局vue-good-table で編集機能を実装できた。

実装方法を書いておく。

Customizations | vue-good-table
公式ドキュメントのCustomizations を参考にした。

<vue-good-table
  :columns="columns"
  :rows="rows">
  <template slot="table-row" slot-scope="props">
      <span v-if="props.column.field == 'id'">
          <input class="custom-select" type="text"  v-model="rows[props.row.idx].id">
      </span>
  </template>
</vue-good-table>

rows のは辞書型配列で、各辞書型にはidx というindex プロパティを持たせてある。
これは適当にmap メソッドを使えばすぐ用意できると思う。

これならcolumn のfield が "id" のときに、データ rows の "id" の値を v-model で扱える。

Vue のテーブルライブラリ(1)

Vue でデータを表示する目的のテーブルライブラリ(グリッドビュー?)が使いたかったので色々調べてみた。
今回の機能要件としては、

くらいかな。

とりあえずawesome-vueを参考にして、いくつか調べてみた。
github.com

vue-good-table

一番最初に見つけて使っていたのがこれだが、
Edit 機能がないので他に移ることに決めた。

ag-grid-vue

Vue Grid
結論としてはこれにした。
これ有料だった(Community版はあるけど)
JS3大フレームワークに対応しているし、
多機能だし、これでいい気がする。

Vuetable-2

GitHub - ratiw/vuetable-2: data table simplify! -- datatable component for Vue 2.x. See documentation at
一通り機能は揃ってるっぽいし、dependencies もVue.js とaxios だけで環境を選ばず使えそう。
ドキュメントに例のビューを表示しておいてほしいかな。

vue-datasource

https://github.com/coderdiaz/vue-datasource
デモ画面にはEdit ボタンがあるけど、ドキュメントが貧弱で使い方がよくわからない。

vuetiful-datatable

CodePen - Vue.JS 2 - Advanced Datatable Component
やりたいことは一通りできそうだし、使いやすそうだが、
最終コミットが2017年なのが気になる。

vue-easytable

GitHub - huangshuwei/vue-easytable: 🍉 Vue table components, support for cell edit,multi-head fixed, multi-column fixed, clumn drag, sort,conditional filter, custom column ...(vue table 组件,支持 单元格合并、单元格编辑、多表头固定、多列固定、列拖动、排序、自定义列、条件过滤、分页... )
ドキュメントが中国語だが、機能的には問題なさそう。

vue-bootstrap4-table

GitHub - rubanraj54/vue-bootstrap4-table: Advanced table based on Vue 2 and Bootstrap 4 ⚡️
パッと見Edit 機能がない。

vue-data-tables

GitHub - njleonzhang/vue-data-tables: A simple, customizable and pageable table with SSR support, based on vue2 and element-ui
これもなかなか多機能でドキュメントもしっかりしている。
ElementUIベースなので、ElementUIを使うときにはこれがいいんじゃないかな。

閑話:猫のご飯

猫のご飯を変えてみた。

ニュートロ ナチュラルチョイス

成分表示を見るに、生白身魚が一番多く含まれているタイプの総合栄養食。
後述するサイトで多数のキャットフードを獣医の方が分析しており、その中でも上位なのでよほど大丈夫だろう。 軽く調べたが他サイトでも特に悪い評判は見なかった。

今までロイヤルカナンだったのだが、知人から「おすすめの猫の餌ってある?」と聞かれたのを切っ掛けに調べなおしてみたところ、 そこまで優れているというわけでもなさそうだったので変えてみた次第。

比較

試しにニュートロとロイヤルカナンを並べて置いてみたところ、ニュートロに食いついた。 動画は録画ボタンが押せておらず撮れなかった。

参考にさせて頂いた記事

my-best.com

メカニカルキーボードを使い始めた

購入したもの

英字配列の61サイズなので最小限のキーだけで、 Insert あたりは Fn あたりと組み合わせて入力するタイプですね。 あと青軸なのでめちゃくちゃカチカチいいます(クリッキーってやつ?)。

タイプ感は軽やかなので青軸でも今のところ疲れません。

ざっくり良いところと気になるところを書きます。

良いところ

  • タイプ感が軽い
  • Bluetooth 3台ペアリング可能
  • Type-Cで有線接続も可能
  • キー操作で接続先を切り替えられる
  • 値段が安い
    HHKB買うか迷っていたので尚更

気になるところ

  • 操作が説明書と違うところがある
    • ペアリングは説明書ではまずFn + P を長押しでペアリングモードに入ると書いてあるのに、そうならない。
      結局Fn + Q, W, E 長押しでペアリングモードになった。
    • 有線・無線モード切替は説明書ではFn + Tab と書いてあるが、裏のスイッチの切り替えをしないといけない模様(OFF が無線)。
  • 電池残量が判らない
    急にAltだけ効かなくなり、いろいろ調べた末直らなかったのであきらめて充電して寝たら翌朝には直っていた。バッテリーが問題?

トータルでの満足度は高いです。

Hangouts Chatの雨通知botを爆速で作る

:warning:この記事は2019/12/06 にQiita に投稿したものです

@murs313 さんのSlackの雨通知botを爆速で作る【メッ●●】を読み、 早速同じ物を作ろうと思いましたが、弊社はSlackが使えませんでした。

というわけでHangouts Chat版雨通知botを作ってみました。 GASだと真似しすぎで作っててつまらないのでPythonとcronで実装してみました。

※ なおHangouts ChatはG Suiteでのみ使用でき、個人のGoogleアカウントでは現在使用できません。

つくったもの

テストで送信してみたものがこちら。 実際は時間を合わせて17:00に送信されるように設定しています。 IMG_0068.jpg Hangouts ChatはMarkdownっぽい記法が使えるのが良いですね。 (画像では強調表示がうまいこといってませんが……)

つかったもの

Hangouts ChatへのPOST

Hangouts Chat は incoming webhook が使用できます。 @iitenkida7 さんの [超簡単]Hangouts Chat の incoming webhooks を使ってAPIから簡単にメッセージを投稿するを参考にさせて頂きました。

Pythonソースコード

import requests

weather_url = 'http://weather.livedoor.com/forecast/webservice/json/v1?city=130010' # Tokyo
resp = requests.get(weather_url)
data = resp.json()

# 一部ぼかしていますがコピペでおk
webhook_url = "https://chat.googleapis.com/v1/spaces/HOGEHOGE/messages?key={YOUR_KEY}&token={YOUR_TOKEN}" 

tomorrow_weather = data['forecasts'][1]
telop = tomorrow_weather['telop']
max_temp = tomorrow_weather['temperature']['max']
min_temp = tomorrow_weather['temperature']['min']
image = tomorrow_weather['image']['url']

text = ''
text += '明日の天気は'
text += '*' + telop + '*'
text += 'です。\n'
# なぜかmaxとminが時々取得できない
if max_temp is not None:
    text += '最高気温'
    text += max_temp.get('celsius')
    text += '℃'
if min_temp is not None:
    text += '/最低気温'
    text += min_temp.get('celsius')
    text += '℃\n'
text += image

content = {"text": text}
response = requests.post(webhook_url, json=content)

cronの設定

cronの設定はこちらが参考になると思います。

cronの設定を行うにはcrontab -eを叩きましょう。

$ crontab -e

するとVimで設定ファイルが開くので、実行する時間と実行するコマンドを入力しましょう。 今回はホームディレクトリ直下に置いたという設定です。

0 17 * * * python3 ~/weather.py

ファイルを保存してVimを閉じた時にcrontab: installing new crontabと表示されれば設定完了です。

動作確認する場合は、再度crontab -eを開き、

*/1 * * * * python3 ~/weather.py

のように記述すれば1分おきにコマンドが実行されるようになります。

感想

参考にさせていただいたリンクたちがわかりやすかったので特に詰まることなく設定できました。 これで私も雨の日の前日にPCを持って帰るのを忘れずに済みます。

……まぁ弊社は先日リモート禁止の御触れがでちゃったんですけど。

FlaskでファイルアップロードAPIを実装し REST Clientで叩いた

:warning:この記事は2019/10/04 にQiita に投稿したものです

TL; DR

FlaskでファイルアップロードAPIを実装し、REST Clientで叩きました。

はじめに

この記事ではFlaskで簡単なGETのAPIを実装するところから、 実際にファイルをPOSTするAPIを実装するところまでを、3 Stepsで記載しています。 Flaskにあまり馴染みが無い方も超簡単にファイルアップロードAPIできるようになれます(きっと)。

環境

Python 3.7 Flask 1.1 VSCode 1.38

ディレクトリ構成 / ├ data/ ├ __init__.py ├ api.py ├ call.http ├ face.png └ requirements.txt

1. APIの実装(JSONのGET)

FlaskでのAPIの実装マジで簡単。やったぜ。

1-1. 実装

from flask import Flask, jsonify

app = Flask(__name__)


@app.route('/api/item', methods=['GET'])
def get_item():
   item = {"item_name": 'hogehoge'}

   return jsonify(item) 

if __name__ == '__main__':
    app.run(debug=True)

これだけ。やったぜ。

1-2. 叩く

さっそく試しに叩いてみよう! ......あれよく考えたらあんまり叩き方知らないな?(情弱)

1-2-0. REST Client

ググってみたら次の記事を見つけました。

REST Client allows you to send HTTP request and view the response in Visual Studio Code directly.

VSCode上で HTTP リクエストができるとな?便利そう。 早速 REST Client を入れて叩いてみました。

1-2-1. Flask起動

$ python api.py 
* Serving Flask app "api" (lazy loading)
* Environment: production
  WARNING: This is a development server. Do not use it in a production deployment.
  Use a production WSGI server instead.
* Debug mode: on
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 111-858-152

1-2-2. リクエス

### GET item
GET http://localhost:5000/api/item

Cmd + Option + R で送信! (Windows は Ctl + Alt + R)

1-2-3. 結果

HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 30
Server: Werkzeug/0.16.0 Python/3.7.3
Date: Thu, 03 Oct 2019 16:54:12 GMT

{
  "item_name": "hogehoge"
}

やったぜ。

2. API 実装(ユーザー情報のPOST)

GET はうまくいったので POST も試してみよう。 ということでまずはFlaskでAPIを実装。

2-1. 実装

from flask import Flask, jsonify, request
import bleach

app = Flask(__name__)

TOKEN = 'YOUR_TOKEN'

@app.route('/api/v1/user', methods=['POST'])
def add_user():
    # check token
    header = request.headers.get('Authorization', None)
    _, token = header.split()
    if token != TOKEN:
        return jsonify({'Forbidden': 'Access is denied'}), 403

    # method check
    if request.method != 'POST':
        return jsonify({'Method Not Allowed': 'Method is invalid.'}), 405

    # create new user
    new_user = {}
    for key in request.form.keys():
        new_user[key] = bleach.clean(request.form.get(key))

    return jsonify(new_user)


if __name__ == '__main__':
    app.run(debug=True)

これだけ。やったぜ。 ほんとはDBと繋いで new_user を登録したりしたんですけど、今回は割愛。

Flask でリクエストを受け取る時は request を使用します。 今回は Content-Type は multipart/form-data で送信するつもりなので、request.form を使用しています。 他にもパラメータを受け取る場合は request.args 、json を受け取る時は request.json を使用します。

2-2. 叩く

2-2-1. Flask起動

さっきと同じようにapi.pyを動かしておきましょう。

2-2-2. リクエス

#######
### POST user info
POST http://localhost:5000/api/v1/user
Authorization: Bearer YOUR_TOKEN
Content-Type: multipart/form-data; boundary=HOGEHOGEBOUNDARY

--HOGEHOGEBOUNDARY
Content-Disposition: form-data; name=user_name

SAKAMOTO RYOMA
--HOGEHOGEBOUNDARY
Content-Disposition: form-data; name=user_email

ryoma.sakamoto@...
--HOGEHOGEBOUNDARY--

なるほど multipart 形式でPOSTする場合は boundary っていうのを設定するんですね。(情弱) 送信する要素ごとに --{your boundary} を記述して、 要素の最後は --{your boundary}-- で締めくくるんですねぇ。 このへんはRFC7578で規定されていますのでご確認ください。

Content-Disposition の name が記述されていますが、これはHTMLの

<form>
 <input name="user_name">
</form>

と同じです。

2-2-3. 結果

HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 98
Server: Werkzeug/0.16.0 Python/3.7.3
Date: Thu, 03 Oct 2019 17:24:57 GMT

{
  "user_email": "ryoma.sakamoto@...",
  "user_name": "SAKAMOTO RYOMA"
}

やったぜ。無事POSTできましたね。

3. APIの実装(アップロードファイルのPOST)

さて、ようやく本題です。

ユーザー情報送るなら顔写真とかも送れた方が良いよね?そうでもないですか? ファイルアップロードのAPIって実装めんどくさいよなぁと思ってましたが超簡単でした! そうFlaskとREST Clientならね。

3-1. 実装

(10月7日check token部分修正しました。)

from flask import Flask, jsonify, request
import bleach

app = Flask(__name__)

TOKEN = 'YOUR_TOKEN'


@app.route('/api/v1/user', methods=['POST'])
def add_user():
    # check token
    header = request.headers.get('Authorization', None)
    if header is not None:
        _, token = header.split()
        if token != TOKEN:
            return jsonify({'Forbidden': 'Access is denied'}), 403
    else:
        return jsonify({'Forbidden': 'Access is denied'}), 403

    # method check
    if request.method != 'POST':
        return jsonify({'Method Not Allowed': 'Method is invalid.'}), 405

    # create new user
    new_user = {}
    for key in request.form.keys():
        new_user[key] = bleach.clean(request.form.get(key))

    ### このへん追加
    # save uploaded file into data folder
    for file in request.files:
        if file is None:
            break
        upload_file = request.files.get(file)
        upload_path = 'data/%s' % upload_file.filename
        upload_file.save(upload_path)
        new_user[file] = upload_file.filename

    return jsonify(new_user)


if __name__ == '__main__':
    app.run(debug=True)

これだけ。やったぜ。 Flaskでファイルを受け取る場合、 request.files の中に入っていきます。

3-2. 叩く

3-2-1. Flask起動

さっきと同じようにapi.pyを動かしておきましょう。 また、事前に顔写真 face.pngapi.py と同じディレクトリに置いておきましょう。

3-2-2. リクエス

#######
### OK
POST http://localhost:5000/api/v1/user
Authorization: Bearer YOUR_TOKEN
Content-Type: multipart/form-data; boundary=HOGEHOGEBOUNDARY

--HOGEHOGEBOUNDARY
Content-Disposition: form-data; name=user_name

SAKAMOTO RYOMA
--HOGEHOGEBOUNDARY
Content-Disposition: form-data; name=user_email

ryoma.sakamoto@...
--HOGEHOGEBOUNDARY
Content-Disposition: form-data; name="file"; filename="face.png"
Content-Type: application/octet-stream

< ./face.png
--HOGEHOGEBOUNDARY--

3-2-3. 結果

HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 98
Server: Werkzeug/0.16.0 Python/3.7.3
Date: Thu, 03 Oct 2019 18:01:26 GMT

{
  "file": "face.png",
  "user_email": "ryoma.sakamoto@...",
  "user_name": "SAKAMOTO RYOMA"
}

dataフォルダをのぞいてみるとちゃんと face.png が保存されていました! スクリーンショット 2019-10-04 3.03.35.png くぅ疲、これにて目的達成です。やったぜ。

感想

ほんとFlask はAPI さくっと書けて優秀だなぁと思います。 情弱の弁解ですが、普段はAPIJavascriptAjaxだったり、Pythonのrequestsだったりで叩いているのですが、 curlについて調べているときにREST Clientについて知ったので使ってみた次第です。

参考

Qiita

Auth0