Skip to content

Commit 189772f

Browse files
authored
Merge pull request #381 from ma10/refactor-yaml2sheet-20250808
yaml2sheet: リファクタリングとテストの追加
2 parents 61c238f + 0a3395a commit 189772f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+10599
-910
lines changed

tools/scripts/yaml2sheet/README.md

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# yaml2sheet
2+
3+
YAMLファイルからGoogle Sheetsチェックリストを生成するPythonツールです。freee アクセシビリティ・ガイドラインのチェックリストを自動的にGoogleスプレッドシートに展開します。
4+
5+
## 主な機能
6+
7+
- **YAMLからGoogle Sheetsへの変換**: freee_a11y_glライブラリで処理されたYAMLデータを構造化されたGoogle Sheetsチェックリストに変換
8+
- **OAuth認証**: Google Sheets APIとの安全な連携
9+
- **柔軟な設定管理**: YAML形式の設定ファイルによる詳細なカスタマイズ
10+
- **複数スプレッドシート対応**: 開発時確認用と公開用のスプレッドシートを明示的に切り替えて出力可能
11+
- **多言語対応**: 日本語・英語のチェックリスト生成
12+
13+
## インストール
14+
15+
### 前提条件
16+
17+
- Python 3.8以上
18+
- Google Cloud Platformアカウント
19+
- Google Sheets APIの有効化
20+
21+
### パッケージのインストール
22+
23+
```bash
24+
pip install tools/scripts/yaml2sheet
25+
```
26+
27+
### 依存関係
28+
29+
- `google-api-python-client>=2.0.0`: Google Sheets API連携
30+
- `google-auth-oauthlib>=1.0.0`: OAuth認証
31+
- `tools/lib/freee_a11y_gl>=0.2.2`: YAMLデータ処理
32+
33+
## セットアップ
34+
35+
### 1. Google Cloud Platform設定
36+
37+
1. [Google Cloud Console](https://console.cloud.google.com/)でプロジェクトを作成
38+
2. Google Sheets APIを有効化
39+
3. OAuth 2.0認証情報を作成し、`credentials.json`としてダウンロード
40+
41+
### 2. 設定ファイルの作成
42+
43+
デフォルト設定ファイルを生成:
44+
45+
```bash
46+
yaml2sheet --create-config
47+
```
48+
49+
設定ファイルの標準のパスは、`~/.cconfig/freee_a11y_gl/yaml2sheet.yaml`です。
50+
51+
または手動で`yaml2sheet.yaml`を作成:
52+
53+
```yaml
54+
# yaml2sheet.yaml
55+
---
56+
credentials_path: credentials.json
57+
token_path: token.json
58+
development_spreadsheet_id: your-dev-spreadsheet-id-here
59+
production_spreadsheet_id: your-prod-spreadsheet-id-here
60+
sheet_editor_email: [email protected]
61+
log_level: INFO # DEBUG, INFO, WARNING, ERROR, CRITICAL
62+
basedir: /path/to/a11y-guidelines # ガイドラインプロジェクトのルートディレクトリ
63+
base_url: https://a11y-guidelines.freee.co.jp # ガイドラインのベースURL
64+
version_info_cell: A27 # バージョン情報を書き込む1枚目のシートのセル番地
65+
```
66+
67+
### 3. Googleスプレッドシートの準備
68+
69+
1. Googleスプレッドシートで新しいスプレッドシートを作成
70+
2. スプレッドシートのIDを設定ファイルに記録
71+
3. 必要に応じて共有設定を調整
72+
73+
## 使用方法
74+
75+
### 基本的な使用方法
76+
77+
```bash
78+
# 開発用スプレッドシートに出力
79+
yaml2sheet
80+
81+
# 公開用スプレッドシートに出力
82+
yaml2sheet --production
83+
84+
# 設定ファイルを指定
85+
yaml2sheet -c /path/to/config.yaml
86+
87+
# スプレッドシートを初期化(既存シートを削除)
88+
yaml2sheet --init
89+
90+
# 詳細ログ出力
91+
yaml2sheet --verbose
92+
```
93+
94+
### コマンドラインオプション
95+
96+
| オプション | 短縮形 | 説明 |
97+
|-----------|--------|------|
98+
| `--create-config` | - | デフォルト設定ファイルを作成して終了 |
99+
| `--config` | `-c` | 設定ファイルのパス(YAML形式のみ) |
100+
| `--init` | - | スプレッドシートを初期化(警告:既存シートを削除) |
101+
| `--production` | `-p` | 公開用のスプレッドシートを使用 |
102+
| `--basedir` | `-b` | ガイドライン・プロジェクトのルートディレクトリ |
103+
| `--url` | - | ドキュメントのベースURL |
104+
| `--verbose` | `-v` | 詳細ログ出力(設定ファイルのログレベルを上書き) |
105+
| `--help` | `-h` | ヘルプメッセージを表示 |
106+
107+
### Pythonモジュールとして実行
108+
109+
```bash
110+
# モジュールとして実行
111+
python -m yaml2sheet
112+
113+
# 設定ファイルを指定
114+
python -m yaml2sheet -c config.yaml
115+
```
116+
117+
## 設定ファイル詳細
118+
119+
### 設定ファイルの検索順序
120+
121+
1. `-c`オプション指定時:
122+
- 絶対パス:そのまま使用
123+
- 相対パス:カレントディレクトリから検索
124+
125+
2. `-c`オプション未指定時:
126+
- `${HOME}/.config/freee_a11y_gl/yaml2sheet.{yaml,yml}`
127+
- `./yaml2sheet.{yaml,yml}`
128+
129+
### 設定項目
130+
131+
#### 必須設定
132+
133+
| 項目 | 説明 ||
134+
|------|------|-----|
135+
| `development_spreadsheet_id` | 開発用のスプレッドシートID | `1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms` |
136+
| `production_spreadsheet_id` | 公開用のスプレッドシートID | `1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms` |
137+
| `sheet_editor_email` | 保護範囲の編集権限を持つGoogleアカウント | `[email protected]` |
138+
139+
#### オプション設定
140+
141+
| 項目 | デフォルト値 | 説明 |
142+
|------|-------------|------|
143+
| `credentials_path` | `credentials.json` | Google認証情報ファイルのパス |
144+
| `token_path` | `token.json` | Googleトークンファイルのパス |
145+
| `log_level` | `INFO` | ログレベル(DEBUG/INFO/WARNING/ERROR/CRITICAL) |
146+
| `basedir` | カレントディレクトリ | ガイドラインプロジェクトのルートディレクトリ |
147+
| `base_url` | `https://a11y-guidelines.freee.co.jp` | ドキュメントのベースURL |
148+
| `version_info_cell` | `A27` | バージョン情報を書き込むセル番地 |
149+
150+
## 開発・テスト
151+
152+
### テストの実行
153+
154+
```bash
155+
# 全テストの実行
156+
pytest
157+
158+
# カバレッジレポート付き
159+
pytest --cov=yaml2sheet --cov-report=html
160+
161+
# 特定のテストのみ
162+
pytest tests/unit/test_config_loader.py
163+
```
164+
165+
### 開発環境のセットアップ
166+
167+
```bash
168+
# 開発用依存関係のインストール
169+
pip install -r requirements-dev.txt
170+
171+
# パッケージを開発モードでインストール
172+
pip install -e .
173+
```

tools/scripts/yaml2sheet/pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "yaml2sheet"
7-
version = "0.1.4"
7+
version = "0.2.0"
88
description = "Generate checklist in Google Sheets from YAML data"
99
readme = "README.md"
1010
authors = [
@@ -13,7 +13,6 @@ authors = [
1313
dependencies = [
1414
"google-api-python-client>=2.0.0",
1515
"google-auth-oauthlib>=1.0.0",
16-
"toml>=0.10.0",
1716
"freee_a11y_gl>=0.2.2"
1817
]
1918
requires-python = ">=3.8"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[pytest]
2+
testpaths = tests
3+
python_files = test_*.py
4+
python_classes = Test*
5+
python_functions = test_*
6+
pythonpath = src
7+
addopts =
8+
-v
9+
--tb=short
10+
--strict-markers
11+
--disable-warnings
12+
--color=yes
13+
markers =
14+
unit: Unit tests
15+
integration: Integration tests
16+
slow: Slow running tests
17+
auth: Authentication related tests
18+
config: Configuration related tests
19+
sheet: Sheet generation related tests
20+
filterwarnings =
21+
ignore::DeprecationWarning
22+
ignore::PendingDeprecationWarning
Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
11
"""Generate checklist in Google Sheets from YAML data."""
22

3-
from .yaml2sheet import main
4-
from .config_loader import ApplicationConfig
5-
from .auth import GoogleAuthManager
6-
from .sheet_generator import ChecklistSheetGenerator
7-
8-
__version__ = "0.1.0"
3+
__version__ = "0.2.0"
Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,41 @@
11
import os.path
22
import logging
3-
from typing import Optional
43
from google.oauth2.credentials import Credentials
54
from google_auth_oauthlib.flow import InstalledAppFlow
65
from google.auth.transport.requests import Request
76
from google.auth.exceptions import RefreshError
87

98
logger = logging.getLogger(__name__)
109

10+
1111
class GoogleAuthManager:
1212
"""Manages Google API authentication and token management"""
13-
13+
1414
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
15-
16-
def __init__(self, credentials_path: str = 'credentials.json',
15+
16+
def __init__(self, credentials_path: str = 'credentials.json',
1717
token_path: str = 'token.json'):
1818
"""Initialize the auth manager with paths for credentials and token
19-
19+
2020
Args:
2121
credentials_path: Path to client secrets JSON file
2222
token_path: Path to save/load authentication token
2323
"""
2424
self.credentials_path = credentials_path
2525
self.token_path = token_path
26-
26+
2727
def get_credentials(self) -> Credentials:
2828
"""Retrieve Google API credentials, refreshing or creating if needed
29-
29+
3030
Returns:
3131
Credentials: Valid Google API credentials
32-
32+
3333
Raises:
3434
FileNotFoundError: If credentials file is missing
3535
Exception: If authentication fails
3636
"""
3737
creds = None
38-
38+
3939
# Try to load saved token
4040
if os.path.exists(self.token_path):
4141
try:
@@ -44,7 +44,7 @@ def get_credentials(self) -> Credentials:
4444
except Exception as e:
4545
logger.warning(f"Failed to load existing token: {e}")
4646
logger.warning("Will attempt to create a new token")
47-
47+
4848
# Handle invalid/expired token
4949
if not creds or not creds.valid:
5050
if creds and creds.expired and creds.refresh_token:
@@ -58,19 +58,19 @@ def get_credentials(self) -> Credentials:
5858
except Exception as e:
5959
logger.warning(f"Failed to refresh token: {e}")
6060
creds = None
61-
61+
6262
# Create new token if needed
6363
if not creds:
6464
if not os.path.exists(self.credentials_path):
6565
raise FileNotFoundError(
6666
f"Credentials file not found at {self.credentials_path}. "
6767
"Please download it from Google Cloud Console."
6868
)
69-
69+
7070
try:
7171
logger.info("Starting authorization flow")
7272
flow = InstalledAppFlow.from_client_secrets_file(
73-
self.credentials_path,
73+
self.credentials_path,
7474
self.SCOPES
7575
)
7676
logger.info("A browser window will open for authentication. Please log in to your Google account.")
@@ -79,7 +79,7 @@ def get_credentials(self) -> Credentials:
7979
except Exception as e:
8080
logger.error(f"Authentication failed: {e}")
8181
raise
82-
82+
8383
# Save new token
8484
try:
8585
with open(self.token_path, 'w') as token:
@@ -89,40 +89,40 @@ def get_credentials(self) -> Credentials:
8989
logger.error(f"Failed to save token: {e}")
9090
logger.warning("Authentication succeeded but token could not be saved. "
9191
"You may need to re-authenticate next time.")
92-
92+
9393
return creds
9494

9595
def revoke_token(self) -> bool:
9696
"""Revoke the current token and remove the token file
97-
97+
9898
Returns:
9999
bool: True if successfully revoked, False otherwise
100100
"""
101101
if not os.path.exists(self.token_path):
102102
logger.warning("No token file found to revoke")
103103
return False
104-
104+
105105
try:
106106
creds = Credentials.from_authorized_user_file(self.token_path, self.SCOPES)
107-
107+
108108
# Try to revoke token
109109
try:
110110
from google_auth_httplib2 import Request
111111
import httplib2
112-
112+
113113
http = httplib2.Http()
114114
creds.refresh(Request(http))
115115
http = httplib2.Http()
116116
creds.revoke(Request(http))
117117
logger.info("Token successfully revoked")
118118
except Exception as e:
119119
logger.warning(f"Failed to revoke token: {e}")
120-
120+
121121
# Remove token file regardless of revoke success
122122
os.remove(self.token_path)
123123
logger.info(f"Removed token file {self.token_path}")
124124
return True
125-
125+
126126
except Exception as e:
127127
logger.error(f"Error revoking token: {e}")
128128
return False

0 commit comments

Comments
 (0)