Make(旧Integromat)でサークルスクエアのイベントをGoogleカレンダーに登録する

モチベーション

子どものラグビースクールのスケジュールがサークルスクエアで管理されているのですが、サークルスクエアは度々セッションが切れてしまいその度にログインを求められ、イベントリストを確認するときに煩わしさを感じていました。

そのため、ノーコード自動化ツールのMake(旧Integromat)を使ってサークルスクエアのイベント情報をGoogleカレンダーに転記する仕組みを作ったので紹介します。

構成

2つのシナリオから構成されます。

前半シナリオの各モジュールの詳細

イベントリストの取得

Makeのみでサークルスクエアにログインしてイベントリストを取得するのが難儀だったのですべてをMakeで完結させることは諦め、ここは自前のWebサービスを作ることにしました。

将来、サークルスクエアがAPI公開してくれればMakeのみで完結できるのでそれを期待したいところ

コードは以下 github.com

Heroku等にデプロイすればWebサービスとして動きます。

なお、アカウントとパスワードをこのWebサービスに送信することになるのでコード流用するときはそれを留意してお使いください。(アカウント情報をこっそり保存したりするmalwareではありませんがコードご確認の上ご利用ください)

HTTP Make a request モジュールを使って自前のイベントリスト取得用WebサービスAPIアクセスします

入力は上記のアカウント情報と取得したいイベントの日付(対象月)になります。

HTTPパラメータは以下のような感じです。

応答は以下のようなJSON文字列が返ってきます

[
  {"name":"イベント1","start_time":"2023/9/3 09:00","end_time":"2023/9/3 11:30","url":"https://www.c-sqr.net/events/xxx"},
  {"name":"イベント2","start_time":"2023/9/4 09:00","end_time":"2023/9/4 11:30","url":"https://www.c-sqr.net/events/yyy"}
]

前月に翌月分のイベント情報を取得したい場合と当月分のイベント情報を取得したい場合があるので リクエストパラメータのdateの値を自動的に決めることができず、HTTPモジュールのパラメータを適宜、手動設定する必要がありそこは残課題です。

JSONパース

Webサーバーからの出力されたJSON文字列をパースします

Iterator

イベント1個ずつ、Googleスプレッドシートに記録済みかを判定するので Iteratorを置きます。なお、Iterator は Flow Control の中にあります。

名前(name)でループさせます

Googleスプレッドシート

記録用のスプレッドシートはあらかじめ作っておき、それを指定します。

スプレッドシートのヘッダ行は

  • A列: Event
  • B列: Starttime
  • C列: Endtime
  • D列: URL

とします。

新規イベントかのチェック

Google Sheetsの Search Rows モジュールを使って新規イベントかどうかを判定します。

スプレッドシートの各列の情報とイベントリストのJSONの対応するキーの情報がすべてマッチするかどうかで判定します。

すべてマッチする場合は既存イベントとなり、1つでも一致しない列があれば新規データとしてみなします。

なので、サークルスクエア側でイベント更新があった場合は新規データとみなされます。つまり、イベント更新があった場合、元のカレンダーイベントも残っちゃうのでそこは今後の課題です。

新規データがあったかの判定

フィルターを作ります。

Googleスプレッドシートへの記録

Google Sheets の Add a row モジュールを使います。

後半シナリオの各モジュールの詳細

Googleスプレッドシートの新規データのトリガー

Google Sheets のWatch New Rows を使います。 新規の行が追加されていたらその行を抽出します。

SpreadSheet IDでスプレッドシートを指定し、Sheet Name でシート名を指定します。Limit は適当な値をセットします。

Iterator

Iterator を追加し、行ごとに処理させます。

時刻情報があるイベントとそうでないイベントの判別

Router を追加し、時刻情報があるイベントと時刻情報がないイベントに分岐させます。 それぞれにフィルターを設定します

  • 時刻情報があるイベントのフィルター
  • 時刻情報がないイベントのフィルター

Googleカレンダーへの記載

Google Calendar の Create an Event モジュールを分岐の先にそれぞれ追加します。

時刻情報がある場合は、All Day EventをNo、 時刻情報がない場合は All Day EventをYes にします。

あとは

  • Event Name: 1. Event (A)
  • Start Date: 1. Starttime (B)
  • End Date: 1. Endtime (C)
  • Description: 1. URL (D)

とし、スプレッドシートの記載内容をイベント情報に反映させます。

使ってみて

今のところ、適当なタイミングでMakeのダッシュボードから前半シナリオと後半シナリオを手動で実行して使っています。 スケジュールを確認したいとき、Googleカレンダーだけを確認すればよいのでそこはやはり便利です。

イベントリストの取得タイミングを月末などあるタイミングで固定すれば、取得したいイベントの日付(対象月)をMake上で自動で設定して定期実行できそうです。

NILMのパブリックデータセットの入手方法

前回の記事で紹介したNILMTKを使ってNILMのパブリックデータセットを扱う方法の記事です

データセットの入手先まとめ

NILMTKで使うためのデータ変換方法

NILMTK の nilmtk/dataset_converters によりNILMTKで取り扱う共通データ形式(.h5)に変換します。

例えば、REDDの場合だと

from nilmtk.dataset_converters import convert_redd
convert_redd('low_freq', 'redd.h5') 

というようにh5ファイルをあらかじめ生成し、その後は以下のようにそのファイルを読み込むことでNILMTK上でデータセットとして扱うことができます

train = DataSet('/data/redd.h5')
test = DataSet('/data/redd.h5')

REDD

入手先

http://redd.csail.mit.edu/

ベーシック認証のパスワードは redd / disaggregatetheenergy

前処理

データの解凍

$ tar jxvf low_freq.tar.bz2
コンバート
from nilmtk.dataset_converters import convert_redd
convert_redd('/path/to/low_freq', 'redd.h5')  # low_freqはlow_freq.tar.gz2を解凍したときのディレクトリ

REFIT

入手先

https://pureportal.strath.ac.uk/en/datasets/refit-electrical-load-measurements-cleaned

Access Dataset の CLEAN_REFIT_081116.7z をダウンロード

前処理

7zを解凍するツールのインストール

$ sudo apt-get install p7zip

解凍

$ 7zr x -orefit CLEAN_REFIT_081116.7z  # 解凍ディレクトリを指定 (refitとする)
コンバート
from nilmtk.dataset_converters import convert_refit
convert_refit('/path/to/refit', 'refit.h5')  # 解凍したディレクトリのrootパスを指定する

UK-DALE

入手先

https://data.ukedc.rl.ac.uk/browse/edc/efficiency/residential/EnergyConsumption/Domestic/UK-DALE-2017/UK-DALE-FULL-disaggregated

ukdale.zipをダウンロード

コンバート
from nilmtk.dataset_converters import convert_ukdale
convert_ukdale("/path/to/ukdale", "ukdale.h5")  # 解凍したディレクトリのrootパスを指定する

AMPds

入手先

http://ampds.org/

AMPds Version 2 ではなく、以下の Version 1: The original AMPds (R2013) をダウンロードする必要がある。

https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/MXB7VO

Access Dataset をクリックして Original Format ZIP を選択し、dataverse_files.zipをダウンロード

前処理

以下の4ファイルは削除する必要がある。 TSのキーがないためコンバート時にエラーになってしまう

$ rm ampds/FRG.csv
$ rm ampds/WHG.csv
$ rm ampds/WHW.csv
$ rm ampds/HTW.csv
コンバート
from nilmtk.dataset_converters import convert_ampds
convert_ampds("/path/to/ampds", "ampds.h5")  # 解凍したディレクトリのrootパスを入力する

iAWE

入手先

https://iawe.github.io/

Downloadのリンクをクリックし、

https://drive.google.com/drive/folders/1c4Q9iusYbwXkCppXTsak5oZZYHfXPmnp

からelectricity.tar.gzをダウンロード

コンバート
from nilmtk.dataset_converters import convert_iawe
convert_iawe("/path/to/electricity", "iawe.h5")  # 解凍したディレクトリのrootパスを指定

DRED

入手先

https://www.st.ewi.tudelft.nl/~akshay/dred/

DownloadsからNILMTK support の DRED H5 をクリック https://www.st.ewi.tudelft.nl/~akshay/dred/data/

All_data.csv をダウンロードする

コンバート
from nilmtk.dataset_converters import convert_dred
convert_dred("/path/to/All_data.csv", "dred.h5") # ディレクトリ名を指定ではなくファイル名を指定しないといけない

ECO

入手先

http://www.vs.inf.ethz.ch/res/show.html?what=eco-data

Downloadsのリンクをクリックし、続いて、 I accept termsをクリック

ダウンロードのアイコン(右上の2つのアイコンの左側)をクリックして Downloads All

前処理
unzip 01_plugs_csv.zip -d 01_plugs_csv
unzip 02_plugs_csv.zip -d 02_plugs_csv
unzip 03_plugs_csv.zip -d 03_plugs_csv
unzip 04_plugs_csv.zip -d 04_plugs_csv
unzip 05_plugs_csv.zip -d 05_plugs_csv
unzip 06_plugs_csv.zip -d 06_plugs_csv
unzip 01_sm_csv.zip -d 01_sm_csv
unzip 02_sm_csv.zip -d 02_sm_csv
unzip 03_sm_csv.zip -d 03_sm_csv
unzip 04_sm_csv.zip  # これだけそのまま
unzip 05_sm_csv.zip -d 05_sm_csv
unzip 06_sm_csv.zip -d 06_sm_csv
rm *.zip
rm -r __MACOSX/
コンバート
from nilmtk.dataset_converters import convert_eco
convert_eco("/path/to/IE594964/REP594965", "eco.h5", "Asia/Tokyo")

timezoneの引数として何のタイムゾーンを指定すべきなのか不明.. とりあえずAsia/Tokyoで変換成功することだけは確認

GREEND

入手先

https://sourceforge.net/projects/greend/

Downloadボタンをクリック

コンバート
from nilmtk.dataset_converters import convert_greend
convert_greend("/path/to/greend/GREEND_0-2_300615", "greend.h5", use_mp=False)

HIPE

入手先

https://www.energystatusdata.kit.edu/hipe.php

1 week または 3 months のリンクをクリックしてダウンロード

コンバート
$ cd nilmtk/nilmtk/dataset_converters/hipe
$ python convert_hipe.py /path/to/hipe hipe.h5  # hipeディレクトリの下に解凍した複数個のcsvがある

ちなみに、他のデータセットと同様のやり方

from nilmtk.dataset_converters.hipe.convert_hipe import convert_hipe
convert_hipe("/path/to/hipe", "hipe.h5") 

だとAssesionErrorで止まりました

IDEAL

入手先

https://datashare.ed.ac.uk/handle/10283/3647

から household_sensors.zip (14.77Gb) と room_and_appliance_sensors.zip (9.317Gb) をダウンロード

前処理
# ideal_datasetの下に2つのzipがある

$ unzip household_sensors.zip
$ mv sensordata/ household_sensordata
$ unzip room_and_appliance_sensors.zip
$ mv sensordata/ rooms_appliance_sensordata
コンバート
from nilmtk.dataset_converters import convert_ideal
convert_ideal("/path/to/ideal_dataset", "ideal.h5")

SMART

入手先

https://traces.cs.umass.edu/index.php/Smart/Smart

UMass Smart* Dataset - 2017 release の HomeA-electrical.tar.gz, HomeB-electrical.tar.gz, HomeC-electrical.tar.gz をダウンロード(Formに名前、所属、国名を入力する必要あり)

converterのコードを読む限り、HomeA, HomeB, HomeCにのみ対応しているっぽい

コンバート
from nilmtk.dataset_converters import convert_smart
convert_smart("/path/to/smart", "smart.h5")  # 絶対パスで指定する必要あり。smartの下に展開したHomeA, HomeB, HomeCディレクトリ

COMBED

入手先

https://combed.github.io/

Download RAW CSVをクリック

コンバート
from nilmtk.dataset_converters import convert_combed
convert_ideal("/path/to/combed", "combed.h5")  # combedの下にzipを展開したiiitdディレクトリがある

Dataport

入手先

https://dataport.pecanstreet.org/

ただし、学生じゃないとダウンロードするためのアカウントが発行してもらえない

商用ライセンスもあるがお高い.. https://www.pecanstreet.org/wp-content/uploads/2020/05/Pecan-Street-Data-Sheet-May-2020.pdf

DEDDIAG

入手先

https://figshare.com/articles/dataset/DEDDIAG_a_domestic_electricity_demand_dataset_of_individual_appliances_in_Germany/13615073

から house_08.zip, import.sh, create_tables_0.sql, create_tables_1.sql をダウンロード(?)

手元の環境では後続の処理の $ pip install deddiag-loader

でパッケージをインストールできず未確認

HES

ダウンロード先見つからず...

参考記事

NILMTKのインストール

NILMTKのインストール方法の記事です。 ニッチなジャンルの話でここに辿り着いた方はNILMが何かを知っていると思うのでNILMが何かについては省略です。。

うまくいかなかった方法

公式のドキュメントではAnacondaでのインストールを推奨していますがうまくいきませんでした..

nilmtkとnilmtk-contribをインストールしようとすると次のようなコマンドになりますが

$ conda install -c conda-forge -c nilmtk nilmtk nilmtk-contrib

Solving environment: がくるくるとなっている状態から一向に処理が進みません.. (MacBook Pro メモリ16GB)

結局、MacBook Proでのインストールは諦め、クラウドインスタンス上でメモリ40GBぐらいまで増やすとインストールができました。ただ、数時間単位で時間がかかりました

うまくいった方法

setup.pyを使ってインストールをするのが大量のメモリを必要とせず時間も短く良さそうでした。

ただ、そのままだと最新のtensorflowがインストールされてしまい、NILMTKの一部のコードが動かないのでsetup.pyを編集してやる必要があります。 その他、いくつかのパッケージもバージョン指定しないとエラーが出ました。

以下、順を追って手順を紹介します。

今回、試したPythonのバージョンは3.8でした。

$ conda create -n nilmtk-env python=3.8

1. nilmtkのインストール

対象リポジトリをcloneしてきて、

$ git clone https://github.com/nilmtk/nilmtk.git
$ cd nilmtk

setup.pyを次のように編集して、

    install_requires=[
        "pandas==0.25.3",
        "numpy >= 1.13.3, < 1.20.0",
        "networkx==2.1",
        "scipy",
        "tables",
        "scikit-learn>=0.21.2",
        "hmmlearn>=0.2.1",
        "pyyaml",
        "matplotlib==3.1.3",
        "jupyterlab",
        "nbconvert==6.5.0",  # 追加
    ],

インストール

$ python setup.py develop
$ cd ..

2. nilm_metadataのインストール

同様にインストールします。これはsetup.pyを編集しなくても問題なくインストールできます。

$ git clone https://github.com/nilmtk/nilm_metadata.git
$ cd nilm_metadata
$ python setup.py develop
$ cd ..
$ python -c "import nilmtk"

のコマンドでエラーが出なければここまでのインストールokです

3. nilmtk-contribのインストール

まず、リポジトリの取得

$ git clone https://github.com/nilmtk/nilmtk-contrib.git
$ cd nilmtk-contrib

nilmtkのとき同様に、setup.pyの編集をする必要があります。

    install_requires=[
        # 'nilmtk>=0.4',  # コメントアウト
        # 'tensorflow>=2.0', # コメントアウト
        'tensorflow==2.4.1',  # 追加
        'keras==2.4.0',  # 追加
        'protobuf==3.9.2',  # 追加
        'six==1.15.0',  # 追加
        'h5py==2.10.0',  # 追加
        'cvxpy>=1.0.0'
    ],

インストール

$ python setup.py develop

次のコードでエラーが出なければ無事にすべてがインストール完了です

$ python -c "import nilmtk_contrib"

コードの修正が必要な箇所

nilmtk-contribのアルゴリズムを動かすのにいくつかコード修正が必要だったので紹介しておきます

nilmtk-contrib/nilmtk_contrib/disaggregate/WindowGRU.py

 48             filepath = self.file_prefix + "-{}-epoch{}.h5".format(
 49                     # "_".join(appliance_name.split()),  # コメントアウトする
 50                     "_".join(app_name.split()),   # 変更後
 51                     current_epoch,
 52             )

nilmtk-contrib/nilmtk_contrib/disaggregate/resnet_classification.py

277                 ##################
278                 #PLOTTING
279                 # self.classification_output_plot(prediction_classification,appliance)   # コメントアウトする

参考記事

インストールに詰まってしまった人は実は少ない?!のかNILMTKのインストールの記事はWeb上でほとんど見つかりませんでした。。 私の場合、これらの記事内容をそのまま真似するだけではうまくいきませんでしたがもしかしたら役に立つかもしれないので参考記事としてリンクを載せておきます

seleniumをdockerで動かす

モチベーション

sanshonoki.hatenablog.com

約1年前、ジョブカンの工数入力するツールをseleniumを使って実装しました。

自分自身で今現在も使っていてかなり役立っていますが一つだけ課題がありました。

それは、chromeブラウザのバージョンがいつの間にか上がっていてchrome driverのバージョンと不適合ということでプログラムがエラーになり、その度にchrome driverをダウンロードし直す必要があるということでした。

ということで、その煩わしさから解放されるべくdockerコンテナを使ってseleniumを動かすことにしました。

最新版のchromeブラウザをインストールする場合

Dockerfileに下記2行でok

RUN apt-get -y update
RUN apt-get install -y chromium chromium-driver

chromedriver は /usr/bin/chromedriver にインストールされます

特定バージョンのchromeブラウザをインストールする場合

特定バージョンのchromeブラウザをインストールしたい場合は先程のように簡単にはいきません。 なぜなら apt-get install -y chromium だと最新版のchromeがインストールされてしまうからです

ただし、以下の記事で紹介されている Download older versions of Google Chrome for Windows, Linux and Mac というサイトから古いchromeブラウザのパッケージを入手して、インストール可能です。

qiita.com

Dockerfileは次のようになります。

RUN apt-get -y update

# Google Chrome dependencies
RUN apt-get install -y libasound2 libatk-bridge2.0-0 libatspi2.0-0 libdrm2 libgbm1 libgtk-3-0 libnspr4 libnss3 libxkbcommon0 libxshmfence1 xdg-utils fonts-liberation

# Google Chrome (specific version)
RUN wget -O /tmp/google-chrome-stable_current_amd64.deb https://www.slimjet.com/chrome/download-chrome.php?file=files%2F90.0.4430.72%2Fgoogle-chrome-stable_current_amd64.deb
RUN dpkg -i /tmp/google-chrome-stable_current_amd64.deb && rm /tmp/google-chrome-stable_current_amd64.deb

# ChromeDriver (specific version)
ADD https://chromedriver.storage.googleapis.com/90.0.4430.24/chromedriver_linux64.zip /opt/chrome/
RUN cd /opt/chrome/ && unzip chromedriver_linux64.zip

基本的に最新のセキュリティ対策がなされている最新バージョンを使うべきですが特定バージョンのchromeブラウザを使いたい場合は参考にしてください

headlessブラウザを使ったときのジョブカン固有の問題

これはジョブカンサイト固有の問題だと思われますが、headlessブラウザを使ったときになぜか挙動が違うという問題が発生しました。

通常ブラウザでのログイン後

headlessブラウザでのログイン後

画面から分かるようにheadlessブラウザだとメニューが畳まれた状態になってしまいます..

想定しているelementが見つからず element not interactable エラーが出ちゃいます

ググると以下のような記事が見つかったのでウインドウサイズの大きさを変えてみましたが変わりませんでした。 yuki.world

仕方ないのでメニューが畳まれているかを調べて、畳まれていたらメニューを開く操作をするようにしています。

コード

とりあえず、dockerベースでseleniumが動くようになり煩わしさから解放されました

github.com

別解?

毎回、MacOSchromeブラウザのバージョンを調べて、そのバージョンに合ったchromedriverを 公式ダウンロードサイト から自動でダウンロードしてくれば Docker使わなくてもいいんじゃ? と考えましたが難しそうでした

# chromeのバージョンを調べる
$ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version
Google Chrome 101.0.4951.64

ただし、すべてのchromeのバージョンに対応したdriverがあるわけではありません...

https://chromedriver.storage.googleapis.com/101.0.4951.64/chromedriver_mac64.zip

にブラウザでアクセスすると NoSuchKey のエラー画面になります

chromedriveのダウンロードページは

となっていて、パッチバージョンまでピッタリなリンクは存在しません

ダウンロードページをWebスクレイピングしてメジャーバージョンだけがマッチするリンクを取り出せば何とかやれそうですがとても面倒くさそうです

ミニマルな論文管理ツールを作る

年始休暇中に(自分向け)論文管理ツールを実装しました。

github.com

モチベーション

1年前ぐらいに Mendeley という論文管理ウェブサービスを試してみましたが 同じ論文が重複に登録されたりして何かイマイチだなぁと感じてその後は使っていませんでした

具体的には、フォルダ(Collection)間をドラッグアンドドロップして論文移動させると重複に存在するようになってしまうという現象が特にイマイチな感じでした。

デスクトップ版は今では重複チェック機能が実装されているらしい(参考記事)ですが、Web版は確認する限りではそのような機能はありません

Mendeley以外の選択肢としては、Google製の Paperpile というのが良いようなのですが有料サブスクリプションで $3/月 かかります。

$3/月なので高いとまでは思いませんがお金払ってまでまじめに論文管理したいわけでもないんだよなと思って自作ツールを開発することにしました

要件と仕様

要件

  • ファイル名から重複チェックができる
  • タイトル抽出してキーワードサーチができる(ファイル名だけだとpdfファイルを開くまで何の論文か分からないため)
  • 同様に、Abstract抽出して キーワードサーチができる

そして、仕様は「論文からファイル名、タイトル抽出、Abstract抽出してcsvファイルに保存する」というミニマム仕様です。

PDFファイルからの文字抽出

Pythonでpdfファイルから文字抽出するのは何種類かパッケージがあります。

上の記事の中で取り上げられてた PyPDF2 と PDFMiner を試してみましたが PDFMinerが良さそうです。 Qiitaで参考にした記事でも PDFMiner を使ってました。

タイトル抽出とAbstract抽出

基本的には タイトル抽出は 2番目のQiita記事で書かれているように Bouding Boxのheightが一番高い行を抽出してくれば大半の論文では抽出できます。

Abstract抽出は "Abstract" という行を抽出して、"Introduction" の行までを切り出せばokです。

f:id:sanshonoki:20220202222811p:plain

手こずったところ(タイトル抽出編)

Title行抽出で単純にはうまくいかずちょっと苦労したのがいくつかあったので紹介しておきます

Author行が一番大きいheightになっているパターン

上付き文字があると見た目の高さ以上のheightになっていて、そこがTitle行として判定されてしまいます

以下の図では数字がその行のheightを表しています

f:id:sanshonoki:20220202224819p:plain

Author行っぽいか / 所属機関行っぽいか を判定するルールを追加して対処しました。

複数行に渡るタイトルで heightの高さが揃ってない

ほとんどの論文では同じheightが複数行にわたるのでheightが同じ行が続くかどうかで判定すればよいのですがそうはならない論文も中にはあります。

f:id:sanshonoki:20220202230107p:plain

仕方ないので完全に同一のheightが続くという条件を少し緩和しました

手こずったところ(Abstract抽出編)

基本的には Abstract という行を抽出して、Introductionの行までを切り出せばokですがTitle抽出と同様にいくつかそれが当てはまらないケースがあり苦労しました。

Abstractの文字がないパターン

Abstractの文字をサーチして、Abstract行の先頭を特定できません

f:id:sanshonoki:20220202223348p:plain

先にIntroductionの行を抽出してそこからエリア推定します。

2カラム形式になっているパターン

Abstract行の直後にIntroductionが来るのでAbstractブロックを正しく抽出できません

下の図のような論文で BoundingBox抽出すると、Abstractの先頭行の後にIntroduction行が続き、その後にAbstractの本文が続きます。

f:id:sanshonoki:20220202231213p:plain

改行されていることを判定する(上のブロックとの位置関係から分かる)ことでうまく抽出できるようになりました。

開発してみて

もっと簡単にいくのかなと思っていたので意外と課題はありましたがそれらに対応したルールを1個ずつ追加してうまくできるようになりました。

とりあえず手元の420個程度の論文で試してみる限りは全体として99%程度うまくいっていて満足です。

1997年の古い論文とか日本語の論文とか、一部の論文ではうまく情報抽出できてませんでしたが2010年あたり以降の新しい論文は基本うまくいっています。

自分の手持ちの論文に完全にチューニングされているルール/パラメータになってますがarxiv機械学習系の論文だったらそれなりの精度で動く気はしてます。

Author行かどうかの判定に苦労したので 今後はBERTの固有表現抽出等を組み込んで判定できると面白いと思っています。

ジョブカンの工数入力を自動で行うスクリプト

会社員たるもの何人たりとも勤怠管理から逃れることができません.. 私が所属している会社では勤怠管理システムとしてジョブカンというものを利用していますが

毎日の工数入力がめんどくさい、苦痛

それ以外の何物でもありません。

f:id:sanshonoki:20210304220703p:plain (ジョブカンのホームページをキャプチャ。CMを見たことがある人も多いのでは?)

そこで、その苦痛を少しでも軽減するための入力サポートツールを作りました。 久しぶりに作りたいと思って作ったソフトウェアです。

github.com

仕組み

やっていることは 工数CSVファイルから読み出し、Selenium を使ってブラウザを操作して自動登録する というだけです。

ただ、これを実際に使ってみると非常にストレスが減り、また工数入力が自動で行われるのを見る様は楽しくさえもあります。(ちょっと言い過ぎかもしれないけど。。ただ、第三者に入力してもらっている感が心地良い。)

使い方

githubのReadmeに書いてますが ChromeDriver をダウンロードして、ジョブカンのログインに必要な情報を環境変数にセットして 工数CSVファイルに記入してスクリプトを走らせるだけです。

# Example (2021_03.csv)
date,プロジェクト1_タスクA,プロジェクト2_タスクB,プロジェクト2_タスクC
2021/03/01,1:00,6:00,1:00
2021/03/02,1:00,,7:00
2021/03/03,-1,1:00,1:00

のようなCSVファイルを用意し、

$ ./jobcan_auto_input.sh 2021_03.csv

だけ

工夫した点

いくつか工夫した点があってそれは以下になります。

  1. プロジェクトとプロジェクトに紐づくタスクは自動で取得してCSVの雛形を作ってくれる

      $ python generate_projects_and_tasks.py
    

    とすると、 f:id:sanshonoki:20210305224430p:plain
    この画面に自動遷移し、ドロップダウンリストの内容をチェックしてプロジェクトとタスクの一覧を取得します

  2. 毎月の工数入力を記録しておくCSVファイルは年と月を入力すると自動的に作成する

      $ python generate_monthly_csv.py 2021 3
    

    とすると、2021_03.csv を作成します

  3. 端数時間を自動的に計算して予め割り当てたタスクに割り振ってくれる

     2021/03/03,-1,1:00,1:00
    

    このように工数 -1 としておくと、例えば総労働時間が8:00の場合、余りの6:00を-1に割り当てて自動登録します

  4. 前月の入力をしたあとでも毎回、自動で前の月に戻ってくれる

4番目に関しては現状のジョブカンのシステム挙動では過去の月の入力を行うと毎回、最新の月に戻ってしまう謎仕様であり、その苦痛たるや筆舌に尽くしがたくその仕様を阻止するためのChrome拡張(GitHub - mob-sakai/JobcanExtensionForChrome)も存在しているぐらいです。

全国6万社のジョブカンユーザー(注:ホームページにそう書いてある)の皆さんの苦痛を少しでも軽減できたらと思います。

(使用される場合は、自己責任でお使いください)

オンラインPOGドラフト用のツール

今年度の仲間うちでのPOGドラフト会議は3密回避のためリモート開催となりました。

ローカルルール

  • 1~3位は入札方式。競合した場合はあみだくじで抽選
  • 4位以降はウェイバー方式で順繰りに指名

入札フェーズ: GoogleFormsの利用

GoogleFormsを利用しました。 以下のようなアンケートフォームを順位ごとに予め作成しておき、URLを参加者に共有し入力してもらいました。

f:id:sanshonoki:20200616052534p:plain

競合が出たときは、LINEのあみだくじ機能を使って決定しました。 グループのトークルーム上で幹事があみだくじを作って抽選します。

あみだくじ機能の使い方はコチラ guide.line.me

ウェイバーフェーズ: 独自開発アプリの利用

Vue.js + Firebaseの練習用として指名馬入力ツールを作りました。

参考にしたもの

グループの作成(オーナーの登録)

f:id:sanshonoki:20200618055434p:plain

馬名の選択

f:id:sanshonoki:20200618055509p:plain

部分一致で検索するので入力の手間はだいぶ楽でした。 すでに指名された馬は候補で出てこないように重複チェックも入れてます。

馬のマスターデータベースはnetkeiba.comからスクレイピングして(競走馬検索から年齢条件を2歳~2歳にして検索)、jsonファイルとしてもっておきそれを読み込みます。

const horse_catalogue = require("../assets/horse_catalogue.json")

指名馬のオーナーへの割当て

f:id:sanshonoki:20200618055817p:plain

割当て後 f:id:sanshonoki:20200618060040p:plain

登録結果のダウンロード

f:id:sanshonoki:20200618060551p:plain CSVでリストをダウンロードできます

downloadCSV: function() {
  let csv = '\ufeff' + 'order_no,owner_name,name,sire,mare,id\n'

  const sorted_horses = this.selected_horses.sort(function(a, b) {
    ...
  })

  sorted_horses.forEach(el => {
    const line = `${el['po_order_no']},${el['po_name']},${el['name']},${el['sire']},${el['mare']},${el['id']}\n`
    csv += line
  })
  const blob = new Blob([csv], { type: 'text/csv' })
  let link = document.createElement('a')
  link.href = window.URL.createObjectURL(blob)
  link.download = 'pog_result.csv'
  link.click()
},

ドラフトで使ってみた結果

今年度に関してはプロトタイプということでスマホ対応も含めてデザインがアレすぎますが機能面に関しては今のところ、1点を除いて問題なく使えました。

見つかった問題点(デザイン以外の改善点)
  • スマホ環境で事前にテストしていたとき、通信環境の問題からか Firebaseに同じ馬がなぜか重複して登録されることがあった..

来年度は Vuetify とか使ってデザインを改善しようと思います。 Vue.jsがオワコンになってなければよいけど。。

ちなみに、アプリは コチラ からアクセスできます。

自由に試せるのでもし興味ある方がいれば使ってみてください。 そのうちクローズしますがしばらくはopenしていると思います