🦇

python スプレッドシートへ出力

2022/11/02に公開

KingOFTimeの勤怠データをスプレッドシートに出力するというだけの処理です。
Uipathで実行していましたが、朝、昼、夜間と定期的に実行したいのと、UI操作もなく明らかにUipathである必要がないと感じていてpythonに書き換えましたので備忘録残します。

anacondaで行う処理

新しい仮想環境を作成

conda create -n 仮想環境名

作成した仮想環境へ移動する

conda activate 仮想環境名

必要なライブラリをインストールする

今回使用するライブラリは下記の通りです。
●requests・・・KingOfTimeのAPIを叩く
●math・・・端数の処理
●gspread・・・スプレッドシート連携
●oauth2client・・・認証処理

anacondaでインストールするので、コマンドは下記の通りです。

conda install ライブラリ名

ライブラリがconda提供されていない場合、リンク(https://anaconda.org/)が表示されるので、検索してコマンドを確認してインストールします。
pipコマンドは使わない方が良いみたいなのでcondaコマンドでインストールします。
pipコマンド使うと後で後悔したりします。

バージョン

python 3.10.6
conda 4.13.0
requests 2.28.1
gspread 5.6.2
oauth2client 4.1.3

KingOFTime準備

公式ドキュメントはこちらです→ https://developer.kingtime.jp/

管理者の設定画面でAPIトークンを設定し、IPアドレスも登録しておく必要があります。
また、KingOFTimeの打刻以外のAPIは8時半~10時までは利用できないので注意が必要です。

GCP認証情報設定

スプレッドシートに情報を出力するのに、色々と設定が必要です。
GCPの設定画面でサービスアカウントで認証するためのJson形式のファイルが取得できるので、使用するスコープ等を設定してからダウンロードしておきます。
下記、参考になりそうなサイトです。

https://www.ipentec.com/document/software-google-cloud-platform-get-service-account-key

書き込みたいスプレッドシートにサービスアカウントのメールアドレスは共有から追加しておきます。(これを忘れると書き込みに失敗し、403エラーが返ってきます。)

コード

KingOfTime.py
import requests
import math
import gspread
from oauth2client.service_account import ServiceAccountCredentials

#リクエストに必要な情報
TOKEN = "Bearer トークン"
URL = "https://api.kingtime.jp/v1.0/monthly-workings?additionalFields=currentDateEmployee"

header = {"Content-Type":"application/json","Authorization":TOKEN}
resp = requests.get(URL, headers=header)
JArrayResp = resp.json()

#列名定義
cloumn = ["所属","雇用区分","従業員グループ","名前","所定時間","所定外","残業時間","深夜所定","深夜所定外","深夜残業","休日所定","休日所定外","休日残業","休日深夜所定","休日深夜所定外","休日深夜残業","遅刻時間","早退時間","休憩時間","労働合計"]

#出力するための配列を定義する
newlist = []
#ヘッダーになる部分を配列に追加
newlist.append(cloumn)

#取得したデータから必要な情報を二次元配列に入れていく
for key in JArrayResp:
    #所属
    Affiliation = key['currentEmployee']['divisionName']
    #雇用区分
    EmploymentCategory = key['currentEmployee']['typeName']
    #従業員グループ
    employeeG = key['currentEmployee']['employeeGroups'][0]['name']
    #名前
    name = key['currentEmployee']['lastName']+key['currentEmployee']['firstName']
    #所定時間
    assigned = key['assigned']
    assigned = math.floor(assigned/60)+math.floor(assigned%60)/100
    #所定外
    unassigned = key['unassigned']
    unassigned = math.floor(unassigned/60)+math.floor(unassigned%60)/100
    #残業時間
    overtime = key['overtime']
    overtime = math.floor(overtime/60)+math.floor(overtime%60)/100
    #深夜所定
    night = key['night']
    night = math.floor(night/60)+math.floor(night%60)/100
    #深夜所定外
    nightUnassigned = key['nightUnassigned']
    nightUnassigned = math.floor(nightUnassigned/60)+math.floor(nightUnassigned%60)/100
    #深夜残業
    nightOvertime = key['nightOvertime']
    nightOvertime = math.floor(nightOvertime/60)+math.floor(nightOvertime%60)/100
    #休日所定
    holidayWorkNomal = key['holidayWork']['normal']
    holidayWorkNomal = math.floor(holidayWorkNomal/60)+math.floor(holidayWorkNomal%60)/100
    #休日所定外
    holidayWorkExtra = key['holidayWork']['extra']
    holidayWorkExtra = math.floor(holidayWorkExtra/60)+math.floor(holidayWorkExtra%60)/100
    #休日残業
    holidayWorkOvertime = key['holidayWork']['overtime']
    holidayWorkOvertime = math.floor(holidayWorkOvertime/60)+math.floor(holidayWorkOvertime%60)/100
    #休日深夜残業
    holidayWorkNight = key['holidayWork']['night']
    holidayWorkNight = math.floor(holidayWorkNight/60)+math.floor(holidayWorkNight%60)/100
    #休日深夜残業所定
    holidayWorkNightOvertime = key['holidayWork']['nightOvertime']
    holidayWorkNightOvertime = math.floor(holidayWorkNightOvertime/60)+math.floor(holidayWorkNightOvertime%60)/100
    #休日深夜残業所定外
    holidayWorkNightExtra = key['holidayWork']['nightExtra']
    holidayWorkNightExtra = math.floor(holidayWorkNightExtra/60)+math.floor(holidayWorkNightExtra%60)/100
    #遅刻時間
    late = key['late']
    late = math.floor(late/60)+math.floor(late%60)/100
    #早退時間
    earlyLeave = key['earlyLeave']
    earlyLeave = ((earlyLeave/60)+(earlyLeave%60))/100
    #休憩時間
    breakSum = key['breakSum']
    breakSum = math.floor(breakSum/60)+math.floor(breakSum%60)/100
    #労働時間合計
    total = assigned+unassigned+overtime+night+nightUnassigned+nightOvertime+holidayWorkNomal+holidayWorkExtra+holidayWorkOvertime+holidayWorkNight+holidayWorkNightOvertime+holidayWorkNightExtra-late-earlyLeave
    
    #行追加するために行配列に変える
    rowdate = [Affiliation,EmploymentCategory,employeeG,name,assigned,unassigned,overtime,night,nightUnassigned,nightOvertime,holidayWorkNomal,holidayWorkExtra,holidayWorkOvertime,holidayWorkNight,holidayWorkNightOvertime,holidayWorkNightExtra,late,earlyLeave,breakSum,total]
    newlist.append(rowdate)
    
# 使用するスコープ
scope = ['https://spreadsheets.google.com/feeds',
        'https://www.googleapis.com/auth/drive']
	
#出力するスプレッドシート情報
sheetID = "スプレッドシートのID"
sheetName = "シート名"

# jsonファイルの読み込みと認証
credentials = ServiceAccountCredentials.from_json_keyfile_name('jsonファイルのパス', scope)
gc = gspread.authorize(credentials)
wkb = gc.open_by_key(sheetID)
ws = wkb.worksheet(sheetName)

#アップデートする
resp = ws.update('A1', newlist)

kingOfTimeについて

今回は月毎の労働時間等のデータが欲しいとのことだったので、月別の勤怠データを取得しました。

サイト内にレスポンスの例がしっかりあるのでそれを見ながら必要な情報を抜き出しました。
当月分のデータだけで良いので、パラメータで月は指定せず、部署や氏名などの情報が取り出せるように「additionalFields」に「currentDateEmployee」を指定しています。

#リクエストに必要な情報
TOKEN = "Bearer トークン"
URL = "https://api.kingtime.jp/v1.0/monthly-workings?additionalFields=currentDateEmployee"

header = {"Content-Type":"application/json","Authorization":TOKEN}
resp = requests.get(URL, headers=header)
JArrayResp = resp.json()

また、レスポンスが分単位で、出力は時間単位がいいと言われているので、ちょっと計算します。

計算については以前の記事とやっていることが同じです。

https://zenn.dev/akaneiy/articles/2d170d5b484db7

最初はデータフレームで作成していましたが、gspreadのドキュメント見ているとデータフレームではない方がよさそうだったので、配列のまま行きました。
もしデータフレームで操作したいことなどがあれば、pandasをimportして、columnを最初に配列に追加するのを辞めて、配列が完成した後に、下記のようにしたらOKです。

df = pd.DataFrame(newlist,columns = column)

スプレッドシート操作

認証して書き込みを行っています。
公式(??)ドキュメントはこちら↓
https://docs.gspread.org/en/latest/user-guide.html#updating-cells

# 使用するスコープ
scope = ['https://spreadsheets.google.com/feeds',
        'https://www.googleapis.com/auth/drive']
	
#出力するスプレッドシート情報
sheetID = "スプレッドシートのID"
sheetName = "シート名"

# jsonファイルの読み込みと認証
credentials = ServiceAccountCredentials.from_json_keyfile_name('jsonファイルのパス', scope)
gc = gspread.authorize(credentials)
wkb = gc.open_by_key(sheetID)
ws = wkb.worksheet(sheetName)

#アップデートする
resp = ws.update('A1', newlist)

ワークブックの取得の仕方は、名前、ID、URLの三種類あります。
一意のものであるID、URLのうち短いIDを私は使いました。

wkb = gc.open_by_key(sheetID)
#他の書き方は下記の通りです。
wkb = gc.open('名前')
wkb = gc.open_by_url('URL')

ワークシートの取得は名前、番号、最初(sheet1のみ)の三種類あります。
番号はシートの追加でもおかしなことになってしまう・・という思いと、操作したいのがsheet1ではないので、シート名で取得しています。

ws = wkb.worksheet(sheetName)

範囲に書き込みはドキュメントに下記のように書いてあります。

A1セルから作成した配列を全て上書きしたいので、下記のような処理になります。

ws.update('A1', newlist)

所感

スプレッドシートの認証まわりがわかりにくいですが、一度やれば流用できますので楽になります。
gspreadに関してはバージョンアップ頻度が高いのか、前に書いた時と同じではいけなかったので、しっかりとドキュメント読むことの大切さを再認識しました。

UipathのAttendedライセンスしかなくてもpythonなら関係なく夜間バッチ処理できますし、Googleコラボ使えばUipathライセンスがなくても、anaconda環境がなくても実行できるし、いいことがたくさんあると感じます。(問題点は属人化でしょうか・・)
他にも書き換えられそうなものがあるので、引き続きUipath→pythonへの書き換えせっせと行いたいと思います。

Discussion