paloma blog

NWエンジニアやってます。主に自宅環境のお遊びを書きます。Pythonもちょっと。タイトルは好きなカクテルから。

PayPayの支払いデータを自分で集計したい2

前回PayPayの日毎の集計をSQLで出力させました。
最終目標はスマホアプリとして操作させるということで

  1. ツール単体で集計
  2. 集計結果を可視化
  3. スマホアプリ用にビルド

の1-2としてpythonでの出力をやります。

コード

早速ですが全体。
CSVを引数で読んでsqliteに格納してからsql文で出力します。
dbファイルが作成されるのもなんだかなということでいったんインメモリで動かすようにしました。

sql分を直で書くのはよろしくないかもしれませんが前回の文を流用したのでさくっとできました。
変数埋め込みの書き方が都合上バラけてしまったのが悔しいです。

import sys
import sqlite3
import datetime


def subDateisoformat(d):
    df = datetime.datetime.strptime(d, '%Y/%m/%d')
    sub_y, sub_m, sub_d = df.year, df.month, df.day

    return datetime.date(sub_y, sub_m, sub_d).isoformat()


def main():
    # Delete BOM in file
    with open(sys.argv[1], 'r', encoding='utf-8-sig') as f:
        csvfile = f.readlines()

    con = sqlite3.connect(':memory:')
    cur = con.cursor()

    for n, c in enumerate(csvfile):
        field = c.split(',')
        if n == 0:
            headers = (field[0], field[1], field[6])
            # Create sql statement becouse can't used qmark style
            headers = f'CREATE TABLE pay ({field[0]} TEXT,{field[1]} TEXT, \
                       {field[6]} TEXT)'
            cur.execute(headers)
        else:
            # Delete double quotes
            field[0] = field[0].replace("\"","")
            field[1] = field[1].replace("\"","")
            field[6] = field[6].replace("\"","")

            field[0] = subDateisoformat(field[0])
            insert_row = (field[0], field[1], field[6])
            cur.execute('INSERT INTO pay VALUES (?,?,?)', insert_row)

    cur.execute('SELECT "利用日/キャンセル日", "利用店名・商品名", sum("支払総額"), \
                 count("利用店名・商品名") FROM pay GROUP BY "利用日/キャンセル日", \
                 "利用店名・商品名" ORDER BY "利用日/キャンセル日"')

    for x in cur.fetchall():
        print(*x)

    cur.execute('SELECT sum("支払総額") FROM pay')
    # Use 'format' for use astarisk
    print('Total: {}'.format(*cur.fetchone()))

    con.close()

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print('Be specify csvfile.')
        sys.exit(1)

    main()

解説

難しいことはやっていないので2つだけ。

BOM付きファイル読み込み

PayPayの利用明細はBOM付きでした。
エンコードを指定して解決。

  • 指定なし
>>> with open('detail202311(5569).csv', 'r') as f:
...     csvfile = f.readlines()
... 
>>> csvfile[0]
'\ufeff"利用日/キャンセル日","利用店名・商品名","利用者","支払区分","利用金額","手数料","支払総額","当月支払金額","翌月以降繰越金額","調整額","当月お支払日"\n'
>>> with open('detail202311(5569).csv', 'r', encoding='utf-8-sig') as f:
...     csvfile = f.readlines()
... 
>>> csvfile[0]
'"利用日/キャンセル日","利用店名・商品名","利用者","支払区分","利用金額","手数料","支払総額","当月支払金額","翌月以降繰越金額","調整額","当月お支払日"\n'
日付の修正

明細はyyyy/mm/ddで出るのでISOフォーマットに直します。
1桁日付も0埋めしました。

>>> subDateisoformat('2023/12/1')
'2023-12-01'
>>> subDateisoformat('2023/12/11')
'2023-12-11'

動かしてみる

先月分出してみます。

❯ python3 --version
Python 3.10.12
❯ python3 paycalc.py detail202311\(5569\).csv
2023-09-30 ヤフージャパン 508 1
2023-10-04 PayPay 町かど酒場XXXX 3200 6
2023-10-06 PayPay XXXX Dining 1900 1
2023-10-07 PayPay 町かど酒場XXXX 2150 4
2023-10-08 PayPay 町かど酒場XXXX 2700 6
2023-10-09 PayPay 町かど酒場XXXX 1900 4
2023-10-12 PayPay 町かど酒場XXXX 2300 5
2023-10-13 PayPay 町かど酒場XXXX 1150 2
2023-10-14 PayPay 町かど酒場XXXX 3700 8
2023-10-14 PayPay XXXX Dining 2000 1
2023-10-15 PayPay 町かど酒場XXXX 4000 8
2023-10-16 PayPay 町かど酒場XXXX 1450 2
2023-10-18 PayPay 町かど酒場XXXX 950 2
2023-10-20 PayPay 町かど酒場XXXX 2200 4
2023-10-21 PayPay 町かど酒場XXXX 3500 6
2023-10-22 PayPay 町かど酒場XXXX 3900 7
2023-10-26 PayPay 町かど酒場XXXX 3950 8
2023-10-27 PayPay 町かど酒場XXXX 950 2
2023-10-28 PayPay 町かど酒場XXXX 2700 5
2023-10-29 PayPay ハリケーン 3020 1
2023-10-29 PayPay 町かど酒場XXXX 500 1
Total: 48628

同じく店名マスクしました。
出力もsqliteと同じで日付修正したのでソートも効いているしOKですね。

合計も出すようにしたのですがほぼ飲み屋で5万弱...ちょっと飲みすぎたね。

ちなみに7月の支払時。

❯ python3 paycalc.py detail202307\(5569\).csv
2023-05-31 ヤフージャパン 508 1
2023-06-03 PayPay 町かど酒場XXXX 2200 4
2023-06-03 PayPay XXXX Dining 1800 1
2023-06-09 PayPay 町かど酒場XXXX 900 2
2023-06-10 PayPay XX商店 755 1
2023-06-10 PayPay 町かど酒場XXXX 700 1
2023-06-11 PayPay 町かど酒場XXXX 1000 2
2023-06-18 PayPay 町かど酒場XXXX 2700 5
2023-06-19 PayPay 町かど酒場XXXX 2750 5
2023-06-25 PayPay XXXX Dining 2100 1
2023-06-30 PayPay 町かど酒場XXXX 1350 3
Total: 16763

少ないですね。使う金額はそんなに変わらないですが通いのペースが上がってしまったのがわかりますw
XXXX Diningというのも同じ街にあってたまに行くバーなので一応マスクしてますw

あとはこのコードをKivyに埋め込んで完成です。
が、見た目も少し良くしたいので一回グラフ化もさせます。