Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
173 changes: 173 additions & 0 deletions tools/scripts/yaml2sheet/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
# yaml2sheet

YAMLファイルからGoogle Sheetsチェックリストを生成するPythonツールです。freee アクセシビリティ・ガイドラインのチェックリストを自動的にGoogleスプレッドシートに展開します。

## 主な機能

- **YAMLからGoogle Sheetsへの変換**: freee_a11y_glライブラリで処理されたYAMLデータを構造化されたGoogle Sheetsチェックリストに変換
- **OAuth認証**: Google Sheets APIとの安全な連携
- **柔軟な設定管理**: YAML形式の設定ファイルによる詳細なカスタマイズ
- **複数スプレッドシート対応**: 開発時確認用と公開用のスプレッドシートを明示的に切り替えて出力可能
- **多言語対応**: 日本語・英語のチェックリスト生成

## インストール

### 前提条件

- Python 3.8以上
- Google Cloud Platformアカウント
- Google Sheets APIの有効化

### パッケージのインストール

```bash
pip install tools/scripts/yaml2sheet
```

### 依存関係

- `google-api-python-client>=2.0.0`: Google Sheets API連携
- `google-auth-oauthlib>=1.0.0`: OAuth認証
- `tools/lib/freee_a11y_gl>=0.2.2`: YAMLデータ処理

## セットアップ

### 1. Google Cloud Platform設定

1. [Google Cloud Console](https://console.cloud.google.com/)でプロジェクトを作成
2. Google Sheets APIを有効化
3. OAuth 2.0認証情報を作成し、`credentials.json`としてダウンロード

### 2. 設定ファイルの作成

デフォルト設定ファイルを生成:

```bash
yaml2sheet --create-config
```

設定ファイルの標準のパスは、`~/.cconfig/freee_a11y_gl/yaml2sheet.yaml`です。

または手動で`yaml2sheet.yaml`を作成:

```yaml
# yaml2sheet.yaml
---
credentials_path: credentials.json
token_path: token.json
development_spreadsheet_id: your-dev-spreadsheet-id-here
production_spreadsheet_id: your-prod-spreadsheet-id-here
sheet_editor_email: [email protected]
log_level: INFO # DEBUG, INFO, WARNING, ERROR, CRITICAL
basedir: /path/to/a11y-guidelines # ガイドラインプロジェクトのルートディレクトリ
base_url: https://a11y-guidelines.freee.co.jp # ガイドラインのベースURL
version_info_cell: A27 # バージョン情報を書き込む1枚目のシートのセル番地
```

### 3. Googleスプレッドシートの準備

1. Googleスプレッドシートで新しいスプレッドシートを作成
2. スプレッドシートのIDを設定ファイルに記録
3. 必要に応じて共有設定を調整

## 使用方法

### 基本的な使用方法

```bash
# 開発用スプレッドシートに出力
yaml2sheet

# 公開用スプレッドシートに出力
yaml2sheet --production

# 設定ファイルを指定
yaml2sheet -c /path/to/config.yaml

# スプレッドシートを初期化(既存シートを削除)
yaml2sheet --init

# 詳細ログ出力
yaml2sheet --verbose
```

### コマンドラインオプション

| オプション | 短縮形 | 説明 |
|-----------|--------|------|
| `--create-config` | - | デフォルト設定ファイルを作成して終了 |
| `--config` | `-c` | 設定ファイルのパス(YAML形式のみ) |
| `--init` | - | スプレッドシートを初期化(警告:既存シートを削除) |
| `--production` | `-p` | 公開用のスプレッドシートを使用 |
| `--basedir` | `-b` | ガイドライン・プロジェクトのルートディレクトリ |
| `--url` | - | ドキュメントのベースURL |
| `--verbose` | `-v` | 詳細ログ出力(設定ファイルのログレベルを上書き) |
| `--help` | `-h` | ヘルプメッセージを表示 |

### Pythonモジュールとして実行

```bash
# モジュールとして実行
python -m yaml2sheet

# 設定ファイルを指定
python -m yaml2sheet -c config.yaml
```

## 設定ファイル詳細

### 設定ファイルの検索順序

1. `-c`オプション指定時:
- 絶対パス:そのまま使用
- 相対パス:カレントディレクトリから検索

2. `-c`オプション未指定時:
- `${HOME}/.config/freee_a11y_gl/yaml2sheet.{yaml,yml}`
- `./yaml2sheet.{yaml,yml}`

### 設定項目

#### 必須設定

| 項目 | 説明 | 例 |
|------|------|-----|
| `development_spreadsheet_id` | 開発用のスプレッドシートID | `1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms` |
| `production_spreadsheet_id` | 公開用のスプレッドシートID | `1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms` |
| `sheet_editor_email` | 保護範囲の編集権限を持つGoogleアカウント | `[email protected]` |

#### オプション設定

| 項目 | デフォルト値 | 説明 |
|------|-------------|------|
| `credentials_path` | `credentials.json` | Google認証情報ファイルのパス |
| `token_path` | `token.json` | Googleトークンファイルのパス |
| `log_level` | `INFO` | ログレベル(DEBUG/INFO/WARNING/ERROR/CRITICAL) |
| `basedir` | カレントディレクトリ | ガイドラインプロジェクトのルートディレクトリ |
| `base_url` | `https://a11y-guidelines.freee.co.jp` | ドキュメントのベースURL |
| `version_info_cell` | `A27` | バージョン情報を書き込むセル番地 |

## 開発・テスト

### テストの実行

```bash
# 全テストの実行
pytest

# カバレッジレポート付き
pytest --cov=yaml2sheet --cov-report=html

# 特定のテストのみ
pytest tests/unit/test_config_loader.py
```

### 開発環境のセットアップ

```bash
# 開発用依存関係のインストール
pip install -r requirements-dev.txt

# パッケージを開発モードでインストール
pip install -e .
```
3 changes: 1 addition & 2 deletions tools/scripts/yaml2sheet/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "yaml2sheet"
version = "0.1.4"
version = "0.2.0"
description = "Generate checklist in Google Sheets from YAML data"
readme = "README.md"
authors = [
Expand All @@ -13,7 +13,6 @@ authors = [
dependencies = [
"google-api-python-client>=2.0.0",
"google-auth-oauthlib>=1.0.0",
"toml>=0.10.0",
"freee_a11y_gl>=0.2.2"
]
requires-python = ">=3.8"
Expand Down
22 changes: 22 additions & 0 deletions tools/scripts/yaml2sheet/pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
pythonpath = src
addopts =
-v
--tb=short
--strict-markers
--disable-warnings
--color=yes
markers =
unit: Unit tests
integration: Integration tests
slow: Slow running tests
auth: Authentication related tests
config: Configuration related tests
sheet: Sheet generation related tests
filterwarnings =
ignore::DeprecationWarning
ignore::PendingDeprecationWarning
7 changes: 1 addition & 6 deletions tools/scripts/yaml2sheet/src/yaml2sheet/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
"""Generate checklist in Google Sheets from YAML data."""

from .yaml2sheet import main
from .config_loader import ApplicationConfig
from .auth import GoogleAuthManager
from .sheet_generator import ChecklistSheetGenerator

__version__ = "0.1.0"
__version__ = "0.2.0"
42 changes: 21 additions & 21 deletions tools/scripts/yaml2sheet/src/yaml2sheet/auth.py
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
import os.path
import logging
from typing import Optional
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.auth.exceptions import RefreshError

logger = logging.getLogger(__name__)


class GoogleAuthManager:
"""Manages Google API authentication and token management"""

SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
def __init__(self, credentials_path: str = 'credentials.json',

def __init__(self, credentials_path: str = 'credentials.json',
token_path: str = 'token.json'):
"""Initialize the auth manager with paths for credentials and token

Args:
credentials_path: Path to client secrets JSON file
token_path: Path to save/load authentication token
"""
self.credentials_path = credentials_path
self.token_path = token_path

def get_credentials(self) -> Credentials:
"""Retrieve Google API credentials, refreshing or creating if needed

Returns:
Credentials: Valid Google API credentials

Raises:
FileNotFoundError: If credentials file is missing
Exception: If authentication fails
"""
creds = None

# Try to load saved token
if os.path.exists(self.token_path):
try:
Expand All @@ -44,7 +44,7 @@ def get_credentials(self) -> Credentials:
except Exception as e:
logger.warning(f"Failed to load existing token: {e}")
logger.warning("Will attempt to create a new token")

# Handle invalid/expired token
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
Expand All @@ -58,19 +58,19 @@ def get_credentials(self) -> Credentials:
except Exception as e:
logger.warning(f"Failed to refresh token: {e}")
creds = None

# Create new token if needed
if not creds:
if not os.path.exists(self.credentials_path):
raise FileNotFoundError(
f"Credentials file not found at {self.credentials_path}. "
"Please download it from Google Cloud Console."
)

try:
logger.info("Starting authorization flow")
flow = InstalledAppFlow.from_client_secrets_file(
self.credentials_path,
self.credentials_path,
self.SCOPES
)
logger.info("A browser window will open for authentication. Please log in to your Google account.")
Expand All @@ -79,7 +79,7 @@ def get_credentials(self) -> Credentials:
except Exception as e:
logger.error(f"Authentication failed: {e}")
raise

# Save new token
try:
with open(self.token_path, 'w') as token:
Expand All @@ -89,40 +89,40 @@ def get_credentials(self) -> Credentials:
logger.error(f"Failed to save token: {e}")
logger.warning("Authentication succeeded but token could not be saved. "
"You may need to re-authenticate next time.")

return creds

def revoke_token(self) -> bool:
"""Revoke the current token and remove the token file

Returns:
bool: True if successfully revoked, False otherwise
"""
if not os.path.exists(self.token_path):
logger.warning("No token file found to revoke")
return False

try:
creds = Credentials.from_authorized_user_file(self.token_path, self.SCOPES)

# Try to revoke token
try:
from google_auth_httplib2 import Request
import httplib2

http = httplib2.Http()
creds.refresh(Request(http))
http = httplib2.Http()
creds.revoke(Request(http))
logger.info("Token successfully revoked")
except Exception as e:
logger.warning(f"Failed to revoke token: {e}")

# Remove token file regardless of revoke success
os.remove(self.token_path)
logger.info(f"Removed token file {self.token_path}")
return True

except Exception as e:
logger.error(f"Error revoking token: {e}")
return False
Loading