コマンドラインで、手元のCSV一式からGoogleスプレッドシートを作成・更新する

ローカル環境にあるCSVファイルをディレクトリ単位で一式、Google Drive内の指定したフォルダにGoogleスプレッドシートとして変換・アップロードするCLIツール「**csv2gsheets**」を作成しました。

https://github.com/ttsukagoshi/csv2gsheets

指定したDriveフォルダ内に、同じファイル名の既存スプレッドシートがあれば、それを上書き更新する（ファイルIDを変更することなく内容を更新する）こともできるので、**Googleスプレッドシートを簡易データベースとして使いたい場合など、データの流れを自動化することができます**。

こんな感じ：

```
c2g convert --config-file-path /Users/username/path/to/c2g.config.json --browse --dry-run
```
Node.js/npm環境での動作なので、バージョン管理も簡単です。以下では[csv2gsheets v1.1.0](https://github.com/ttsukagoshi/csv2gsheets/releases/tag/v1.1.0)をベースにその初期設定と使用方法をご紹介します。

## インストールなどの初期設定
### Node.js
[Node.js](https://nodejs.org/ja)の[Active以上のリリース](https://github.com/nodejs/release#release-schedule)（2023年9月末時点では`v18.x.x`または`v20.x.x`）がインストールされていることを確認してください。

```
node -v
```

### csv2gsheetsのインストール

```
npm install -g csv2gsheets
```
:::note info
以下ではグローバル環境での実行を想定していますので、インストールにあたって`-g`フラグをつけています。
:::

試しに実行してみます：

```
c2g --help
```
実行コマンドは短縮形`c2g`でも実行できるようにしてあります。以下、全て`c2g`で記載していますが`csv2gsheets`に置き換え可能です。

### Google Cloudプロジェクトの作成

:::note warn
Google Cloudプロジェクトを立てたことがない人にとっては、ここが面倒に感じるかもしれません...が、一度だけやればいいものなので、ご辛抱ください！分かりにくい記述があれば遠慮なくコメントでお知らせください。
:::

csv2gsheetsでGoogle Drive APIを使うためには、Google Cloudプロジェクトを作成し、Drive APIを有効にした上で、そのプロジェクトに紐づく認証情報（Credentials）をJSON形式のファイルとしてローカル環境に保存する必要があります。無料のGmailアカウントでも進めることができます。

以下その手順を、Google for Developersの公式案内「[Node.js クイックスタート](https://developers.google.com/drive/api/quickstart/nodejs?hl=ja)」に沿って、スクリーンショットを交えながらご紹介します。

#### Google Cloudプロジェクトを作成する
公式の「[Google Cloud プロジェクトを作成する](https://developers.google.com/workspace/guides/create-project?hl=ja)」のとおりに新規プロジェクトを作成します。プロジェクト名は以下のスクショのとおり`My Project`でも何でも大丈夫です：

![Google Cloudプロジェクトを新規作成する画面のスクリーンショット画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726523/f7389711-15a2-957a-07df-c93eeaefc906.png)

プロジェクト作成が完了すれば、[Node.js クイックスタートの「Prerequisites（前提）」](https://developers.google.com/drive/api/quickstart/nodejs?hl=ja#prerequisites)が準備できたことになりますので、次の項目に移ります。

#### Drive APIを有効にする
クイックスタートの「[APIの有効化](https://console.cloud.google.com/flows/enableapi?apiid=drive.googleapis.com)」ボタンを押して、Drive APIを有効にします。

![Google Drive APIを有効にする画面のスクリーンショット画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726523/3cea30e4-db19-e8e3-9abe-36b8fb3dd112.png)

#### OAuth同意画面を設定
APIを有効にしたGoogle Cloud Consoleからそのまま「[OAuth同意画面](https://console.cloud.google.com/apis/credentials/consent?hl=ja)」に移動して、必須項目を設定していきます。

![OAuth同意画面を作成する画面のスクリーンショット画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726523/2c7b4d5d-4e4b-ebc6-d899-0a2c5e82be51.png)

:::note info
ユーザの種類（User Type）には「**内部（Internal）**」（＝組織内での利用に限る）または「**外部（External）**」（＝組織外のユーザも使用できるようにする）の選択肢があります。Google Workspaceのアカウントを使っているのであれば「内部」としておくのがおすすめです。後述のテストユーザを設定せずに済みます。
:::

:::note warn
無料のGmailアカウントを使用しているのであれば「外部」しか選択できないようになっています。
:::

OAuthのスコープでは`https://www.googleapis.com/auth/drive`を選択してください。

もしOAuth画面作成時に「外部」を選んだのであれば、テストユーザとしてご自身のアカウント（例：`hogehoge@gmail.com`）を登録しておいてください。

![OAuth画面の設定でテストユーザを追加する画面のスクリーンショット画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726523/aaeefdc2-7e09-05cd-db03-eb83d044c828.png)

#### OAuthクライアント認証情報を作成

「[認証情報](https://console.cloud.google.com/apis/credentials?hl=ja)」ページに移動して「**認証情報を作成**」>「**OAuthクライアントID**」をクリックします.

![認証情報を作成する画面のスクリーンショット画像](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726523/a0e946d9-5e1a-88d6-95cc-fb11741ee613.png)

- 「**アプリケーションの種類**」では「**デスクトップ アプリ**」を選択します
- 「**名前**」は、このコンソール内で自分が理解できるものにすればいいです（例：`csv2gsheets`）

認証情報を作成すると以下のような画面が出てきます：

![05_save_credentials_json.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/726523/408e9254-a5cd-77ab-2af7-00aed8487a59.png)

「**DOWNLOAD JSON**」から認証情報のJSONファイルを`c2g.creds.json`として端末のホームディレクトリに保存してください。

:::note info
「ホームディレクトリに保存」...
- Windows端末では`C:\Users\ユーザ名\c2g.creds.json`
- macOS端末では`/Users/ユーザ名/c2g.creds.json`
:::

:::note alert
**`c2g.creds.json`はGitにコミットしたり、不用意に他人と共有しないようにしてください！**
Drive APIへのリクエスト回数制限など、Googleによる各種制約はGoogle Cloudプロジェクト単位で適用されます。先のOAuth同意画面の設定でユーザの種類を「内部」としていても、この制約含めて共有して問題の無い同僚（同じ部署の人、など）限りとするのがいいでしょう。
:::

以上で「Google Cloudプロジェクトの作成」が完了です！

## 実行してみる

### Googleにログインする

Google Drive内でGoogleスプレッドシートを作成・更新するために、まずはGoogleにログインします：

```
c2g login
```

前項のGoogle Cloudプロジェクト作成がうまくいっていれば、いつものGoogleのログイン画面に続いて、先ほど設定したOAuth同意画面が出てきます。

:::note info
同意が完了するとホームディレクトリに`.c2grc.json`というファイルが生成されます。いわゆるOAuth tokenです。
:::

:::note alert
**`.c2grc.json`は誰とも共有しないでください！** ログインしたGoogleアカウントのID/パスワードと同義です。
:::

一度ログインすれば、

```
c2g logout
```

でログアウトするか、OAuth token内のrefresh tokenが無効になるまで有効であり続けます。「あれ？いまどのアカウントでログインしていたっけ？」というときは`login`コマンドに`--status`（`-s`）オプションをつけてください：

```
c2g login --status
```

:::note info
余談ですが、GoogleのOAuth tokenの失効に関しては、こちら↓の記事がわかりやすいです：
https://qiita.com/croquette0212/items/8da7ccaa09abf89d4f51
:::

### 設定ファイルの作成
csv2gsheetsは、必要な変数を設定ファイル（`c2g.config.json`）に格納して、実行時にはその設定ファイルを見にいくようにしています。

<details><summary>設定項目やそのデフォルト値について詳しく知る（クリックして展開）</summary>

| プロパティ | 型 | 意味 | デフォルト値 |
| --- | --- | --- | --- |
| `sourceDir` | `string` | Googleスプレッドシートに変換したいCSVファイル一式が保存されているディレクトリ。ここで指定したディレクトリ直下にあるCSVファイルが全て、csv2gsheetsの対象となる。**サブディレクトリ以下は対象とならない**。 | csv2gsheetsを実行している作業ディレクトリ |
| `targetDriveFolderId` | `string` | CSVごとのGoogleスプレッドシートを作成するGoogle DriveフォルダID。フォルダIDとは、URL「`https://drive.google.com/drive/folders/*****`」の`*****`の部分。マイドライブ直下を指定したい場合は、IDの代わりに`root`とする。 | `root` |
| `targetIsSharedDrive` | `boolean` | `targetDriveFolderId`で指定したDriveフォルダが共有ドライブ内のものである場合は`true`とする。（参考：[共有ドライブとは - Google Workspace ラーニング センター](https://support.google.com/a/users/answer/7212025?hl=ja)） | `false` |
| `updateExistingGoogleSheets` | `boolean` | `targetDriveFolderId`で指定したDriveフォルダ内に、変換するCSVと同じ名前のスプレッドシートが既に存在する時に、それを上書きする場合は`true`とする。`false`であれば、同じファイル名のスプレッドシートが新規作成される。（Google Driveの場合、ひとつのフォルダ内に同じファイル名がいくつも共存できる） | `false` |
| `saveOriginalFilesToDrive` | `boolean` | 元のCSVファイルもGoogle Driveに保存したい場合は`true`にする。`true`とした場合、`targetDriveFolderId`で指定したDriveフォルダ内の子フォルダ「csv」内に、そのCSVがアップロードされる。子フォルダ「csv」が存在しなければ、新規作成される。 | `false` |

</details>

設定ファイルもコマンドラインで作成できます。

```
cd <設定ファイルを作成したいディレクトリ>
c2g init
```
設定値を入力するようプロンプトされますので、順に値を指定していきます。何も指定せずにEnterを押すとデフォルト値となります。

:::note info
この時点でログインがまだでしたら`--login`（`-l`）オプションを使用して、設定ファイルの作成とGoogleへのログイン・承認を同時に済ませられます：
```
c2g init --login
```
これは
```
c2g init

# 各種設定後
c2g login
```
と順に実行するのと同じことです。
:::

### CSVをGoogleスプレッドシートに変換する

一番シンプルなパターンが
```
c2g convert
```
です。csv2gsheetsは作業ディレクトリ内にある設定ファイル`c2g.config.json`を見つけて、その内容に従ってDrive内でのスプレッドシート作成・更新を行います。

:::note info
CSVファイルと、設定ファイル`c2g.config.json`は別々のディレクトリにあっても問題ありません。Windowsのバッチ処理で、「ネットワークドライブ割り当て」などで設定した別ドライブ内のCSVファイル一式を取り扱いたい時に、設定ファイルが`C:\hoge\hoge`に、CSVファイルが `Z:\fuga\fuga`にある、という状況にも対応可能ということです。
:::

`c2g.config.json`の場所を明示したい場合は`--config-file-path`（`-c`）オプションを使います：

```
c2g convert -c /Users/foo/bar/c2g.config.json
```

本番実行前には`--dry-run`（`-d`）オプションで、実際のファイル操作を行うことなく、対象ファイルや設定値を確認することができます：

```
c2g convert --dry-run
```

:::note warn
`--dry-run`では基本的にファイル・フォルダに対する実際の操作は行われませんが、一点だけ例外があります。Driveフォルダ内の「csv」フォルダの作成です。

<details><summary>詳しく知る</summary>

元のCSVファイルもDriveにアップロードする設定となっている場合（つまり`c2g.config.json`で`saveOriginalFilesToDrive`が`true`となっている場合）、`convert`実行中に、CSVアップロード先である「csv」フォルダのURLも`console.info`で出力されるようになっています。

このようにアップロード先がユーザ側にも明示されることを優先させている都合上、「もし対象Driveフォルダ内に『csv』サブフォルダが存在しない場合は、作成して、そのURLを返す」という処理は`--dry-run`が有効であっても実行される、という事情です。

</details>
:::

`convert`実行後にGoogle Driveの対象フォルダをブラウザで開きたい場合は`--browse`（`-b`）オプションをつけます：

```
c2g convert --browse
```

オプションは組み合わせることができます：

```
c2g convert -c /Users/foo/bar/c2g.config.json -d -b
```

## 留意点

### Drive APIの使用量上限
csv2gsheetsの処理には、Googleが定める[Drive APIの使用量上限](https://developers.google.com/drive/api/guides/limits?hl=ja)が適用されます

### 既存スプレッドシート更新であっても、シートIDは変わる

本ツールでは、既存スプレッドシートの更新にあたってDrive APIの[`files.update`](https://developers.google.com/drive/api/reference/rest/v2/files/update)を使っていますが、このメソッドは

- ファイルIDを変更せずに、内容を完全に上書き更新
- シートIDは変わる

という仕様になっています。取り扱っている元データがCSVファイルなので、更新後のスプレッドシートは必然的に1ファイル1シートとなります。つまり**定期的に更新されることが前提のスプレッドシートについては、下流での処理を進めるにあたって次の点に留意**する必要があります：

### 編集や集計は別のスプレッドシートで行う
変換されたスプレッドシートを直接編集してしまうと、更新時にその作業が消えてしまいます。

例えば「別シートでちょっとした集計」などはExcel感覚でやりたくなりますが、集計を含めて表計算として残していきたい場合はスプレッドシートの[`IMPORTRANGE`関数](https://support.google.com/docs/answer/3093340?hl=ja)などの利用をお勧めします。

### シートは名前で参照する
更新時にシートIDが変わってしまうので、Google Apps Scriptなど参照方法を選べる場合は極力、シート名での参照としたほうが手数を少なくできます。

<details><summary>GASの例</summary>

```javascript
// ファイルIDは変わらないので、ID指定できる：
const targetSpreadsheet = SpreadsheetApp.openById('ファイルID'); 

// シートを取得するときは、シート名で指定する：
const targetSheet = targetSpreadsheet.getSheetByName('シート名');

// 1ファイル1シートとなるので、これでも一意の指定ができる：
const targetSheet2 = targetSpreadsheet.getSheets()[0];
```
</details>

:::note info
csv2gsheetsで作成・更新されるスプレッドシートのシート名は、ファイル名と同じになります。例えば元ファイルが`test.csv`であれば、ファイル名・シート名ともに`test`となります。
:::

:::note info
現時点ではDrive APIのみを使っているcsv2gsheetsですが、[Sheets API](https://developers.google.com/sheets/api/reference/rest)も組み合わせればシートIDが変わらない形での上書き更新が可能な気がしています。その辺はAPIリクエスト回数や処理速度とのトレードオフになりそうなので、追々。機能追加のご要望などはぜひ[GitHubのIssues](https://github.com/ttsukagoshi/csv2gsheets)を立てていただければと思います。もちろん日本語で大丈夫です。
:::

## 終わりに

CSVをGoogleスプレッドシートに変換するためのツールや方法は検索すれば色々ありますが、

- コマンドラインで完結する
- 業務で展開するためにバージョン管理が容易＝パッケージ化してdistributeされている
- 必要最小限の機能（CSV→Sheetsは欲しいけれど、別にSheetsから情報を取得して色々したいわけではない）

といった要件を満たすものが見当たらなかったため、今回csv2gsheetsを作ってみました。

これが個人開発でのコマンドラインツール2作目ということで、まだまだ至らぬ点も多いかと思います。よろしければ本記事へのコメントや、GitHubレポジトリへのIssue立てを通じてご指摘ください。GitHubのほうが英語で書きましたが、日本語での投稿ももちろん大丈夫です:flag_jp:

https://github.com/ttsukagoshi/csv2gsheets