標準入力をとりあえずSlackに通知するhouを作った

ぼんやりとみんGoを眺めていたら、horensoみたいに賢くなくていいから、標準入力をそのままSlackに投げるCLIツールが自分用にあったらいいような気がしてきた。

github.com

そこで作ったのがこれ。horenso(報・連・相)でいうところの「報」しかやらないのでhouという名前。

以下の記事のことも頭の片隅にあったかもしれないけど、nofify_slackのほうが対ISUCONを意識されてるっぽいし実戦的だと思う。くらべてhouはもっと雑。

medium.com

使い方

READMEの通り。オプションで色々渡せるようにしてはあるけど、無くてもいいねって感じのオプションばかり。ついついオプションを増やしたくなってしまう悪い癖がどうも抜けない。

$ export SLACK_API_TOKEN=YOUR_SLACK_API_TOKEN
$ w | hou -channel hello

環境変数にSlackのトークンを入れて、何らかの標準入力を受け取って -channel で指定されたSlackのチャンネルにメッセージを投げる。

メッセージの受信イメージはこんな感じ。

f:id:ariarijp:20170925224302p:plain

せっかく作ったので、ちょっとしたバッチを crontab に設定したときの動作確認とか、VMなどでちょっと重めのバッチを回しているときの完了通知とかに使おうかなと思っているところ。

余談

「ゆとりの法則 - 誰も書かなかったプロジェクト管理の誤解」読んでない気がするし、マーケットプレイスで送料込み500円ちょっとだったのでとりあえず買ってみた。

原題は「Slack: Getting Past Burnout, Busywork, and the Myth of Total Efficiency」らしい。

ゆとりの法則 ? 誰も書かなかったプロジェクト管理の誤解

ゆとりの法則 ? 誰も書かなかったプロジェクト管理の誤解

crontabを多少読みやすくするcrontocを作った

個人的に欲しくなったので作った。crontab(CRON TABle)を読むためのツールなので、crontoc(CRON Table Of Contents)という名前。

github.com

導入方法などはREADMEのとおり。

使い方

crontab -lを見やすくするツールなのでどこで動かしてもいいけど、個人的には「あのサーバー、直近で動くバッチってなんだっけ」というのをサクッと確認したくて作ったので、sshコマンドでcrontab -lを実行し、その結果を標準入力として渡すと良い。

$ ssh remote-server crontab -l | crontoc
Next: 2017-09-19 23:33:00 +0900 JST # * * * * * vmstat >> /tmp/vmstat.log
Next: 2017-09-20 00:10:00 +0900 JST # 10 0 * * * df -h >> /tmp/df.log
Next: 2017-09-20 00:00:00 +0900 JST # */5 0 * * * free >> /tmp/free.log
Next: 2017-10-01 10:00:00 +0900 JST # 0 10 1 * * date >> /tmp/date.log

TODO

  • 毎分や毎時実行されるジョブは直近の1行しかでないので、「n分以内に実行される」みたいなオプションがあってもいい
  • 毎分や毎時実行されるジョブはわかりやすくするため、出力時に色を変えてもいいかも

まとめ

ちょっとしたツールを書くのはやっぱりGoが便利。

会社のどこかにあると思われるみんGoを明日の昼休みちょっとに読み直そうと思った。

みんなのGo言語[現場で使える実践テクニック]

みんなのGo言語[現場で使える実践テクニック]

PhpSpreadsheetで数値、文字列、金額、日付、画像をシートに書き込むサンプル

せっかくなので昨日書いた記事のPhpSpreadsheet版も書いてみることにした。

PHPExcelからPhpSpreadsheetに乗り換えたいという人なんかほとんど居ないと思うけど、そういうひとにとっては参考になるかもしれない。

ariarijp.hatenablog.com

環境

  • PHP 7.0.22
  • PHPExcel 1.0.0-beta

以前の記事でPhpSpreadsheetを紹介したころは開発版しか公開されていなかったけど、現在はbetaリリースまで進んでいる模様。

composer.jsonはこんな感じ。

{
    "require": {
        "phpoffice/phpspreadsheet": "1.0.0-beta"
    }
}

ちなみにドキュメントがRead the Docsで公開されているあたりに今どきっぽさがある。

PhpSpreadsheet Documentation

サンプルコード

サンプルで使用している画像はPHPExcelの記事同様PHPのロゴ。

<?php

require 'vendor/autoload.php';

use PhpOffice\PhpSpreadsheet\Cell;
use PhpOffice\PhpSpreadsheet\Cell\AdvancedValueBinder;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;

date_default_timezone_set('Asia/Tokyo');

// http://php.net/images/logos/php-med-trans.png
$imagePath = 'php-med-trans.png';

// サンプルデータを適当に生成
$rows = array_map(function ($i) {
    return [
        $i, // 数値
        uniqid(), // 文字列
        rand(10000, 100000), // 数値(金額)
        strftime('%Y-%m-%d', rand(0, time())), // 日付
    ];
}, range(1, 20));

$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

// セルの書式を指定する時に必要
// http://phpspreadsheet.readthedocs.io/en/develop/topics/recipes/#write-a-date-or-time-into-a-cell
Cell::setValueBinder(new AdvancedValueBinder());

$offset = 1;
foreach ($rows as $i => $row) {
    $rowNum = $i + $offset;

    // A列からD列までの値を書き込み
    $sheet->fromArray($row, null, Cell::stringFromColumnIndex(0) . $rowNum);

    // C列の書式を金額(JPY)に変更
    $sheet->getCell(Cell::stringFromColumnIndex(2) . $rowNum)
        ->getStyle()
        ->getNumberFormat()
        ->setFormatCode('"¥"#,##0');

    // D列の書式をYYYY/MM/DD形式に変更
    $sheet->getCell(Cell::stringFromColumnIndex(3) . $rowNum)
        ->getStyle()
        ->getNumberFormat()
        ->setFormatCode(NumberFormat::FORMAT_DATE_YYYYMMDDSLASH);


    (new Drawing())
        ->setPath($imagePath)
        ->setWorksheet($sheet)
        ->setHeight(30)
        ->setCoordinates(Cell::stringFromColumnIndex(4) . $rowNum);

    // 行の高さを画像にあわせて変更
    $sheet->getRowDimension($rowNum)
        ->setRowHeight(30);
}

// Excel(.xlsx)として書き出す
$writer = new XlsxWriter($spreadsheet);
$writer->save('sample.xlsx');

生成されるファイル

こんな雰囲気。Excelが手元にないのでLibreOfficeで表示。

f:id:ariarijp:20170823220105p:plain

まとめ

PHPExcelに比べてドキュメントが整っているのが良い。PHPExcelと見比べるとわかるけど、だいたいPHPExcelと同じインターフェースなので、移行しようと思ったらそれなりにできそうではある感じ。

名前空間がPSR-4準拠なので、そのあたりの書き味もPhpSpreadsheetの方が良い感じ。

最近Excelばかり触っているので、Excel拡張機能も作ってみたくなってきた。

PHPExcelで数値、文字列、金額、日付、画像をシートに書き込むサンプル

最近どうもExcelを扱うことが多いので、久しぶりにPHPExcelを触ってみたメモ。

以前PhpSpreadsheetを紹介したけれど、あえてPHPExcelを選んだのは困った時に検索などで情報を得やすいため。

ariarijp.hatenablog.com

環境

  • PHP 7.0.22
  • PHPExcel 1.8.1

composer.jsonはこんな感じ。

{
    "require": {
        "phpoffice/phpexcel": "^1.8"
    }
}

サンプルコード

サンプルで使用している画像はPHPのロゴ。

<?php

require 'vendor/autoload.php';

date_default_timezone_set('Asia/Tokyo');

// http://php.net/images/logos/php-med-trans.png
$imagePath = 'php-med-trans.png';

// サンプルデータを適当に生成
$rows = array_map(function ($i) {
    return [
        $i, // 数値
        uniqid(), // 文字列
        rand(10000, 100000), // 数値(金額)
        strftime('%Y-%m-%d', rand(0, time())), // 日付
    ];
}, range(1, 20));

$book = new PHPExcel();
$sheet = $book->getActiveSheet();

$offset = 1;
foreach ($rows as $i => $row) {
    $rowNum = $i + $offset;

    // 数値をセルに書き込み
    $sheet->setCellValueByColumnAndRow(0, $rowNum, $row[0]);

    // 文字列をセルに書き込み
    $sheet->setCellValueByColumnAndRow(1, $rowNum, $row[1]);

    // 数値をセルに書き込んで、書式を日本円に変更
    $sheet->setCellValueByColumnAndRow(2, $rowNum, $row[2], true)
        ->getStyle()
        ->getNumberFormat()
        ->setFormatCode('"¥"#,##0');

    $dt = DateTime::createFromFormat('Y-m-d', $row[3]);
    $date = PHPExcel_Shared_Date::PHPToExcel($dt, true, 'Asia/Tokyo');

    // 日付をセルに書き込んで、書式をYYYY/MM/DD形式に変更
    $sheet->setCellValueByColumnAndRow(3, $rowNum, $date, true)
        ->getStyle()
        ->getNumberFormat()
        ->setFormatCode(PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDDSLASH);

    // 画像をセルに書き込み
    (new PHPExcel_Worksheet_Drawing())
        ->setPath($imagePath)
        ->setWorksheet($sheet)
        ->setHeight(30)
        ->setCoordinates(PHPExcel_Cell::stringFromColumnIndex(4) . $rowNum);

    // 行の高さを画像にあわせて変更
    $sheet->getRowDimension($rowNum)->setRowHeight(30);
}

// ファイルに書き出し
$writer = PHPExcel_IOFactory::createWriter($book, 'Excel2007');
$writer->save('sample.xlsx');

生成されるファイル

こんな雰囲気。Excelが手元にないのでLibreOfficeで表示しています。

f:id:ariarijp:20170822232143p:plain

まとめ

見やすいドキュメントが見つからなかったものの、サンプルコードを読みつつ、PhpStormなどでコード補完が使えればなんとか使える。

レポートなどではGoogleスプレッドシートが使われることも多くなってきていますが、まだまだExcelとはお付き合いが続きますね。

仕事で使っているのがMicrosoft Office for Mac Home and Business 2011なので、そろそろOffice 365あたりに変えたいところ。特に評価シートを書いている途中で落ちるのがつらい。

Google Spreadsheetからデータを取得してPandasのDataframeに変換する例

最近はPythonを書く機会が多いのでPythonネタをメモ程度に書き留めておきます。

事前準備

データの準備

動作確認用のスプレッドシートを作成し、サービスアカウントで編集できるように権限を設定します。

(GoogleAPIを使用するためにサービスアカウントを使用しますが、設定手順は割愛します)

そのスプレッドシートに「iris」という名前のシートを作成し、シートの内容を以下のようにします。

f:id:ariarijp:20170528204805p:plain

Irisのデータは以下のリンクなどから入手できます。

UCI Machine Learning Repository: Iris Data Set

環境の準備

venv などを使用して、以下の requirements.txt を使用できるようにしてください。

google-api-python-client==1.6.2
httplib2==0.10.3
numpy==1.12.1
oauth2client==4.1.0
pandas==0.20.1
pyasn1==0.2.3
pyasn1-modules==0.0.8
python-dateutil==2.6.0
pytz==2017.2
rsa==3.4.2
six==1.10.0
uritemplate==3.0.0

コード例

処理についてはコメントを参照してください。

import httplib2
import os

import sys
from apiclient import discovery
from oauth2client.service_account import ServiceAccountCredentials

import pandas as pd

CLIENT_SECRET_FILE = os.environ.get('CLIENT_SECRET_FILE', 'client_secret.json')
SCOPES = 'https://www.googleapis.com/auth/spreadsheets.readonly'
DISCOVERY_URL = 'https://sheets.googleapis.com/$discovery/rest?version=v4'

credentials = ServiceAccountCredentials.from_json_keyfile_name(
    filename=CLIENT_SECRET_FILE,
    scopes=SCOPES)
http = credentials.authorize(httplib2.Http())
service = discovery.build(serviceName='sheets',
                          version='v4',
                          http=http,
                          discoveryServiceUrl=DISCOVERY_URL)

# スプレッドシートのIDをコマンドライン引数から取得する
spreadsheet_id = sys.argv[1]

# スプレッドシートからデータを取得する
values = service.spreadsheets().values() \
    .get(spreadsheetId=spreadsheet_id,
         range='iris!A:E') \
    .execute() \
    .get('values', [])

# 取得したデータをDataframeにする
df = pd.DataFrame \
    .from_records(data=values[1:],
                  columns=values[0])

# 全ての列がobject型になってしまうので、数値の列の型をfloatに変換する
df = df.astype({
    'sepal_length': float,
    'sepal_width': float,
    'petal_length': float,
    'petal_width': float,
})

# 動作確認
print('[df.describe]')
print(df.describe())
print()
print('[df.head]')
print(df.head(5))

実行

$ python example.py スプレッドシートID
[df.describe]
       sepal_length  sepal_width  petal_length  petal_width
count    150.000000   150.000000    150.000000   150.000000
mean       5.843333     3.054000      3.758667     1.198667
std        0.828066     0.433594      1.764420     0.763161
min        4.300000     2.000000      1.000000     0.100000
25%        5.100000     2.800000      1.600000     0.300000
50%        5.800000     3.000000      4.350000     1.300000
75%        6.400000     3.300000      5.100000     1.800000
max        7.900000     4.400000      6.900000     2.500000

[df.head]
   sepal_length  sepal_width  petal_length  petal_width      species
0           5.1          3.5           1.4          0.2  Iris-setosa
1           4.9          3.0           1.4          0.2  Iris-setosa
2           4.7          3.2           1.3          0.2  Iris-setosa
3           4.6          3.1           1.5          0.2  Iris-setosa
4           5.0          3.6           1.4          0.2  Iris-setosa

まとめ

とにかくなんでもPandasのデータフレームに持ち込めれば、Pandasの強力な機能の数々を使うことができるので、 様々なデータソースを使った処理を書いたり、分析したりする人にとって、Pandasはうれしいツールですね。もうちょっと勉強しつつ、実践投入していく予定です。

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

この本は図書館で借りたことがあったけど、手元に置いておきたいかも。