pydomoを使ってDataSetの読み込み&新規作成をしたい！

## 記事を書こうと思ったきっかけ
以下の2点がきっかけです。

- **調べても使い方が全く分からん！**
- **もっと良い方法があったら教えて欲しい！**

いざpydomoを使おうとしたら日本語の記事が全然なく、[公式のビデオ（という名のトラップ）](https://www.domo.com/domopalooza/resources/pydomo-overview)も2021年に途中で更新が止まっているしでめっちゃ苦労しました...
復習と勉強を兼ねてコードを記載していこうと思います。
**もっと良い方法があれば**ビシバシご指導頂けますと幸いです。

## pydomoの前準備

### CLIENT_IDとCLIENT_SECRETの発行
[こちらのページ](https://developer.domo.com/portal/8ba9aedad3679-ap-is#step-1-create-client-id-and-secret:~:text=Use%20access%20token-,Step%201%3A%20Create%20client%20ID%20and%20secret,-In%20order%20to)のStep 1に記載してある通り以下の2つを取得して下さい。
**他の人に知られない様に注意！** くれぐれもGitHubにアップなんてしないようにお気を付けください。

- CLIENT_ID
- CLIENT_SECRET

### pydomoのインストール

conda install だとエラーになったのでpipでインストールしました。
[こちらのページ](https://domo-support.domo.com/s/article/360047400753?language=ja#:~:text=%E3%81%A6%E3%81%8F%E3%81%A0%E3%81%95%E3%81%84%E3%80%82-,%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%97%E3%81%A6%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B,-Jupyter%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9)を見る限り、DOMO上のjupyterワークスペースでの環境構築はconda推し...？
環境構築に関しては**自己責任でお願い致します。**

## コード

### どの関数を使うか

主に使う関数は「[pydomo/\_\_init\_\_.py](https://github.com/domoinc/domo-python-sdk/blob/master/pydomo/__init__.py)」に纏まっているので確認してみて下さい。
ここに記載されている関数を使用すれば良い感じに動いてくれます。
公式のビデオに記載で紹介されている「[pydomo/\_\_init\_\_.py](https://github.com/domoinc/domo-python-sdk/tree/master/pydomo/datasets)」は使わない方が無難です。
私があまり理解できていませんがAPI向け（？）らしく、非常に煩雑なコード記載が必要になります。
具体的には作成するDataSetのスキーマ作成が必要です。

### config_logging.ini

jupyterワークスペースの標準時刻がUTCかつ変更できなさそうなのでご注意ください。
ipynbファイル単位ごとにrootが設定されるそうなのでjupyterなら直接編集もありかも...？

```txt
[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とシークレットキーはベタ打ちです。

```python
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

```python
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に対して何かの処理をします。

```python
def do_something:
    pass
```

### pydomo_template

**【注意】以下のコードを実行する毎にDataSetが作成されます**
IDでDataSetを管理しているっぽいので、同じDataSet名でも作成できます。

```python
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の場合のみ必要。主キーとして使用する。
    )
```

### 記載途中のコード

メンテナンス性も兼ねて以下のクラスのイメージ？
 ```python
 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](https://github.com/domoinc/domo-python-sdk/blob/master/pydomo/__init__.py)」の該当部分

```python
# 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()とかの戻り値の解説とかも足したいです！

## 参考サイト
- [domoinc/domo-python-sdk](https://github.com/domoinc/domo-python-sdk)
- [Domo-Central](https://www.domo.com/domo-central)
- [logging.handlers --- ロギングハンドラ](https://docs.python.org/ja/3/library/logging.handlers.html?highlight=timedrotatingfilehandler#logging.handlers.TimedRotatingFileHandler)
- [【良く分かる】Python loggerの 使い方と注意点](https://resanaplaza.com/2021/07/23/%e3%80%90%e8%89%af%e3%81%8f%e5%88%86%e3%81%8b%e3%82%8b%e3%80%91python-logger%e3%81%ae-%e4%bd%bf%e3%81%84%e6%96%b9%e3%81%a8%e6%b3%a8%e6%84%8f%e7%82%b9/)

## 協力
- [ChatGPT](https://openai.com/chatgpt)
- [GitHub Copilot](https://github.com/features/copilot)