記事を書こうと思ったきっかけ
以下の2点がきっかけです。
- 調べても使い方が全く分からん!
- もっと良い方法があったら教えて欲しい!
いざpydomoを使おうとしたら日本語の記事が全然なく、公式のビデオ(という名のトラップ)も2021年に途中で更新が止まっているしでめっちゃ苦労しました...
復習と勉強を兼ねてコードを記載していこうと思います。
もっと良い方法があればビシバシご指導頂けますと幸いです。
pydomoの前準備
CLIENT_IDとCLIENT_SECRETの発行
こちらのページのStep 1に記載してある通り以下の2つを取得して下さい。
他の人に知られない様に注意! くれぐれもGitHubにアップなんてしないようにお気を付けください。
- CLIENT_ID
- CLIENT_SECRET
pydomoのインストール
conda install だとエラーになったのでpipでインストールしました。
こちらのページを見る限り、DOMO上のjupyterワークスペースでの環境構築はconda推し...?
環境構築に関しては自己責任でお願い致します。
コード
どの関数を使うか
主に使う関数は「pydomo/__init__.py」に纏まっているので確認してみて下さい。
ここに記載されている関数を使用すれば良い感じに動いてくれます。
公式のビデオに記載で紹介されている「pydomo/__init__.py」は使わない方が無難です。
私があまり理解できていませんがAPI向け(?)らしく、非常に煩雑なコード記載が必要になります。
具体的には作成するDataSetのスキーマ作成が必要です。
config_logging.ini
jupyterワークスペースの標準時刻がUTCかつ変更できなさそうなのでご注意ください。
ipynbファイル単位ごとにrootが設定されるそうなのでjupyterなら直接編集もありかも...?
[loggers]
keys=root
[handlers]
keys=myStreamHandler,myFileHandler
[formatters]
keys=myformat
[logger_root]
level=DEBUG
handlers=myFileHandler,myStreamHandler
[handler_myStreamHandler]
class=StreamHandler
level=INFO
formatter=myformat
args=(sys.stdout,)
[handler_myFileHandler]
class=handlers.TimedRotatingFileHandler
level=INFO
formatter=myformat
args=('logs/log', 'midnight', 1, 0)
[formatter_myformat]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=%Y-%m-%d %H:%M:%S
fixed_value.py
DOMO上のjupyterワークスペースで作業する前提なので、IDとシークレットキーはベタ打ちです。
CLIENT_ID = 発行したやつ
CLIENT_SECRET = 発行したやつ
API_HOST = 'api.domo.com' # デフォルト値
LOG_FOLDER_NAME = "logs"
Regex:
StaffTable = r"^DOMOにはフォルダの概念がないので正規表現でファイルを取得すると良いかも?$"
DataSetID
Master = "連結ファイルなど、定期更新が必要なDataSetのIDは記載しておく"
pydomo_related_library.py
import os
import logging
from logging import config
from pydomo import Domo
from fixed_value import CLIENT_ID, CLIENT_SECRET, API_HOST, LOG_FOLDER_NAME
def call_pydomo_client() -> pydomo.Domo:
if not os.path.exists(LOG_FOLDER_NAME):
os.makedirs(LOG_FOLDER_NAME)
config.fileConfig(config_logging.ini)
return Domo(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
api_host=API_HOST,
logger_name="pydomo_logger",
log_level=logging.DEBUG
)
do_something.py
実際はここでdfに対して何かの処理をします。
def do_something:
pass
pydomo_template
【注意】以下のコードを実行する毎にDataSetが作成されます
IDでDataSetを管理しているっぽいので、同じDataSet名でも作成できます。
from pydomo_related_library import call_pydomo_client
from fixed_value import Regex
from do_something import do_something
pydomo_client = call_pydomo_client()
# dfでDateSetのリストが返ってくる
df_all_datasets = pydomo_client.ds_list()
df_extracted_datasets = df_all_datasets[df_all_datasets["name"].str.contains(Regex.StaffTable)]
for _, srs in df_extracted_datasets.iterrows():
df = pydomo_client.ds_get(srs["name"]) # 引数がdataset_idのみ。自動で読み込まれるのでparse_dateとかは使えない。
do_something()
pydomo_client.ds_create(
df_up=df,
name="新しいDataSet名",
description='ここに記載するなら別資料に纏めた方が幸せになれるかもしれない',
update_method='REPLACE',
# key_column_names=[] # update_methodがUPSERTの場合のみ必要。主キーとして使用する。
)
記載途中のコード
メンテナンス性も兼ねて以下のクラスのイメージ?
from typing import Union
import domojupyter as domo
import pandas as pd
from transform_dataframe import transform_dataframe
class ManageDomoDatasets(object):
def __init__(
self,
is_create_individual_dataset: bool = True,
how_update_concat_dataset: Union[str, None] = None,
concat_dataset_name: Union[str, None] = None,
):
self.pydomo_client = self.call_pydomo_client()
self.is_create_individual_dataset = is_create_individual_dataset
self.how_update_concat_dataset = (
how_update_concat_dataset # ["REPLACE", "UPSERT", None]
)
self.concat_dataset_name = concat_dataset_name
self.df_extracted_datasets = pd.DataFrame()
def extract_datasets_list_as_df(
self, extract_regex: str, is_remove_already_exists: bool = True
):
df_all_datasets = self.pydomo_client.ds_list()
df_extracted_datasets = df_all_datasets[
df_all_datasets["name"].str.contains(extract_regex)
]
df_extracted_datasets.loc[:, "already_exists"] = df_extracted_datasets[
"name"
].isin(df_all_datasets["name"].astype(int))
if is_remove_already_exists:
self.df_extracted_datasets = df_extracted_datasets[
df_extracted_datasets["already_exists"] == 1
]
else:
self.df_extracted_datasets = df_extracted_datasets
def _create_individual_dataset(
self, transformed_df: pd.DataFrame, srs_df_info: pd.Series
):
self.pydomo_client.ds_create(
df_up=transformed_df, name=srs_df_info["named"], description=""
)
def _update_concat_dataset(
self, transformed_df: pd.DataFrame, concat_dataset_name: str
):
if self.how_update_concat_dataset == "REPLACE":
ans = input("データセットの置き換えを行いますか?(y/n)")
if ans == "y":
domo.create_dataframe(
df_up=transformed_df,
name=self.concat_dataset_name,
update_method="REPLACE",
)
elif self.how_update_concat_dataset == "UPSERT":
domo.create_dataframe(
df_up=transformed_df,
name=concat_dataset_name,
description="",
update_method="UPSERT",
key_column_names=["key_for_upsert"],
)
def main(self, transform_df_func: callable):
if self.df_extracted_datasets.empty:
raise ValueError("加工対象のデータセットが存在しません。")
df_concat = pd.DataFrame()
for _, srs in self.df_extracted_datasets.iterrows():
df = transform_df_func(self.pydomo_client.ds_get(srs["name"]))
df_transformed = transform_dataframe(df)
if self.is_create_individual_dataset:
self._create_individual_dataset(df_transformed, srs)
if self.how_update_concat_dataset == "UPSERT":
self._update_concat_dataset(df_transformed, self.concat_dataset_name)
if self.how_update_concat_dataset == "REPLACE":
df_concat = pd.concat([df_concat, df_transformed])
if self.how_update_concat_dataset == "REPLACE":
self._update_concat_dataset(df_concat, self.concat_dataset_name)
分かる方がいれば教えて下さい
DataSetの読み込み時に、全てのobject列にto_datetimeを掛ける処理のせいでworningが止まりません...
何か良い方法があれば是非教えて下さい!
「pydomo/__init__.py」の該当部分
# pydomo/__init__.pyにて
for col in df.columns:
if df[col].dtype == 'object':
try:
df[col] = to_datetime(df[col])
except ValueError:
pass
except TypeError:
pass
振り返り
久々に記事を書いてなんだか晴れやかな気分です。
説明のためにpydomoのデータ型やds_list()とかの戻り値の解説とかも足したいです!