2021/01/02(土)書き初め

2021/01/02 20:36

2021年最初のプログラムを書きました。100行にも満たないプログラムですが、仕事でやっている言語を離れて好きな言語を使える喜びに満ちた、とても楽しい時間を過ごすことができました。

私は作業時間のトラッキングにToggl Trackを利用しており、各エントリのClientにその時間が消費、投資、浪費のどれに当たるかを指定しています。より厳密には、消費、投資、浪費にぶら下げる形で各Projectを作り、それをエントリに割り当てています。

今日作成したのは、一日の時間の使い方を集計するプログラムです。入力が20210101であれば、2021年1月1日の0時から2日の0時までの24時間を対象に、Clientごとの合計時間を集計します。日をまたいだエントリについては、前日分、翌日分を破棄し、当日24時間の中に入っている部分のみを集計します。

もともとAPIの戻り値にはdurationという作業時間の項目があるのですが、今回は当該エントリの開始時刻から次のエントリの開始時刻までを作業時間としています。アプリの操作やサーバー同期の都合上、どうしても終了→即座に次のエントリ開始というわけには行かず、数秒の空白の時間帯が発生してしまうからです。

from itertools import groupby
from datetime import datetime, timedelta, timezone
from functools import cache
import sys
import time
import requests

API_TOKEN = '<YOUR_API_TOKEN>'
END_POINT = 'https://api.track.toggl.com/api/v8/'
WAIT = 1

def main(target):
    target_date = datetime.strptime(target, '%Y%m%d').replace(tzinfo=timezone(timedelta(hours=9), 'JST'))
    start_date = target_date + timedelta(days=-1)
    end_date = target_date + timedelta(days=1)

    params = {
        "start_date": start_date.isoformat(timespec='seconds'),
        "end_date": end_date.isoformat(timespec='seconds'),
    }
    response = requests.get(END_POINT + 'time_entries', params, auth=(API_TOKEN, 'api_token')).json()

    entries = list(map(
        lambda x: {
            'pid': x['pid'],
            'start': max(datetime.fromisoformat(x['start']), target_date),
        },
        filter(lambda x: datetime.fromisoformat(x['stop']) > target_date, response)
    ))

    durations = [(
        fetch_project(start['pid'])[0],
        stop['start'] - start['start']
    ) for start, stop in zip(entries, entries[1:] + [{'pid': 0, 'start': end_date}])]

    result = [(
        k,
        sum([duration[1] for duration in v], timedelta())
    ) for k, v in groupby(sorted(durations, key=lambda x: x[0]), lambda y: y[0])]

    for k, v in result:
        print(k, format_timedelta(v))

@cache
def fetch_project(pid):
    time.sleep(WAIT)
    response = requests.get(END_POINT + 'projects/' + str(pid), auth=(API_TOKEN, 'api_token')).json()
    return fetch_client(response['data']['cid']), response['data']['name']

@cache
def fetch_client(cid):
    time.sleep(WAIT)
    response = requests.get(END_POINT + 'clients/' + str(cid), auth=(API_TOKEN, 'api_token')).json()
    return response['data']['name']

def format_timedelta(td):
    hours, remainder = divmod(td.seconds, 3600)
    minutes, seconds = divmod(remainder, 60)
    return f"{hours:02}:{minutes:02}:{seconds:02}"

if __name__ == '__main__':
    main(sys.argv[1])

2021/01/01(金)謹賀新年

2021/01/01 21:09

あけましておめでとうございます。本年もよろしくお願いいたします。

2004年から書き始めたこのブログも、何度かの移転を経て足掛け18年目に突入しました。最近はチラシの裏のようなエントリが増えていますが、そもそもこのサイトはブログというより古き良き時代のWeb日記の親戚だと思っています。今後も私の興味のおもむくまま、埼玉西武ライオンズやクイズマジックアカデミーやPythonについて熱く語ったり語らなかったりする予定です。

このサイトをご覧のみなさまにとって、2021年が良い年となりますように。

2020/12/31(木)大晦日

2020/12/31 19:32

本年も残すところ4時間半ほどとなりました。

去年の大晦日はコミケにサークル参加し、帰宅後にぐったりしながらブログに「来年もよろしくお願いします」と書いたのですが、その来年がこんな年になるとは夢にも思いませんでした。

昨年はアウトプットを頑張れた一年だったと思っているのですが、今年はアウトプットの機会もほとんどなく、春のオンラインイベントの技術書典応援祭と年末のBaseball Play Studyくらいでしょうか。出したアウトプットそのものはいいものになったと思うのですが、ちょっと寂しいところではあります。来年は少しブログで技術的なことを書くことを考えたほうがいいかもしれません。

もう完全に元の世界に戻ることはないのでしょうが、来年はもう少し落ち着いて暮らせることを願っています。それではみなさま、良いお年を。

2020/12/30(水)マイナンバーカード

2020/12/30 19:19

今さらなのですが、マイナンバーカードの交付の申請をしてきました。別にマイナンバーカードでなくても良かったのですが、近い将来に写真付きの身分証明書が必要になることが分かっていて、一番手軽に取得できそうなのがマイナンバーカードだったので。

こういう話をすると「免許持ってないの?」と聞かれるのですが、大学時代に教習所に通って仮免許までは行きましたが、路上教習で左膝の激痛に意識が飛びそうになったことがあり、自分には運転は無理だと悟って教習所をやめました。なんでみんな平気な顔で半クラッチができるのだ(当たり前です)。オートマ限定であれば最後まで行けたのかもしれませんが、20年以上前に男性がオートマ限定を選ぶというのは社会的にはありえない選択肢でしたからねぇ。

というか、日本の本人認証の仕組みは、たかが運転のライセンス証に対して依存しすぎだと思うんだ。

……そんな愚痴はおいておいて、証明写真機でマイナンバーカードの交付申請までできてしまうんですね。面倒な書類を書かなくてもいいとは、便利な時代になったものです。

2020/12/29(火)先行きは不透明だけれど

2020/12/29 22:31

来年のイベントに向けてAndroid本を作っているのですが、今回は組版にこれまでのRe:VIEWではなくVivliostyleを使う予定です。その結果、ある程度デザインをいじることができる目処が立ったので、今回はデザイナーさんを入れて本文も今までよりかっこ良くできたらと思っています。

一応GWのコミケには参加申し込みをしていますが、抽選に通るか、また、そもそも開催されるかも不透明な部分があります。技術書典11はオンラインとオフライン両方で開催との話もありますが、こちらもまだ詳細はわからない状況です(そもそもまだ技術書典10の開催中ですし)。

どのイベントに参加するかははっきり言えない状況ではあるのですが、イベントに参加できなくても何らかの形でみなさまにお届けできれるような形にするつもりです。このあたりは今から気に病んでも仕方がないので、年末年始休暇、そして来年最初の3連休あたりでがっつりと原稿を進められればと思っています。