Consolidate Python ignore rules into root gitignore
This commit is contained in:
+26
@@ -0,0 +1,26 @@
|
||||
# GitHub Copilot Instructions
|
||||
|
||||
## 位置づけ
|
||||
|
||||
- 共通ルールの正本は、リポジトリルートの `AGENTS.md` とする。
|
||||
- このファイルには、共通ルールを長文で再掲しない。
|
||||
- Python、DB、FastAPI、Streamlit、Worker、Logging、テスト、フォルダ構成などの基本方針は `AGENTS.md` に従う。
|
||||
- 技術領域ごとの詳細指示がある場合は、対象ファイルに応じて専用の指示ファイルも参照する。
|
||||
- DDL作成、Worker分離、Streamlitページ改修など、特定作業の詳細手順が必要な場合は `.agents/skills/` 配下の `SKILL.md` を参照する。
|
||||
- このファイルと `AGENTS.md` の内容が矛盾する場合は、`AGENTS.md` を優先する。
|
||||
|
||||
## 作業方針
|
||||
|
||||
- 変更前に、既存コード、既存ドキュメント、既存の命名規則、既存のディレクトリ構成を確認する。
|
||||
- 大きな変更は、小さな単位に分けて提案・実装する。
|
||||
- 既存の設計を無視して、新しい構成や新しい仕組みを勝手に作らない。
|
||||
- 既存の類似実装がある場合は、その配置、命名、責務分離に合わせる。
|
||||
- 変更によりロジックが変わる場合は、テストの追加・修正を検討する。
|
||||
- 変更後の確認コマンドは、`AGENTS.md` の「品質チェック / テスト」に従う。
|
||||
|
||||
## GitHub Copilot 向け補足
|
||||
|
||||
- GitHub Copilot は、まず `AGENTS.md` の共通方針に従う。
|
||||
- ファイル種別・技術領域ごとの詳細指示がある場合は、`.github/instructions/` 配下の `*.instructions.md` を参照する。
|
||||
- このファイルには、Copilot固有の運用方針だけを書く。
|
||||
- Cursor向けの `.cursor/rules/project.mdc` と、補足内容が大きくズレないようにする。
|
||||
@@ -0,0 +1,332 @@
|
||||
---
|
||||
applyTo: "**/api/**/*.py,**/routers/**/*.py,**/routes/**/*.py,**/schemas/**/*.py,**/services/**/*.py,**/repositories/**/*.py,**/main.py,**/app.py"
|
||||
---
|
||||
|
||||
# FastAPI Instructions
|
||||
|
||||
## 基本方針
|
||||
|
||||
- FastAPI はバックエンドAPIとして使用する。
|
||||
- API層、Service層、Repository層を分離する。
|
||||
- FastAPIでDBアクセスを行う場合は、原則としてSQLAlchemyを使用する。
|
||||
- 単純なCRUDはSQLAlchemy ORMを優先し、複雑なJOIN・集計・N+1問題の回避でORM記述が複雑になる場合は、`sqlalchemy.text()` または `.sql` ファイルのSQLを使用する。
|
||||
- エンドポイント関数にビジネスロジック、DBアクセス、ファイルI/Oを直接書かない。
|
||||
- 既存の設計、命名、ディレクトリ構成を優先する。
|
||||
- 新規ファイルを作成する場合は、既存の類似機能と同じ配置・命名に合わせる。
|
||||
- 詳細なフォルダ構成は `docs/architecture/folder-structure.md` を参照する。
|
||||
|
||||
## 標準フォルダ構成
|
||||
|
||||
FastAPI関連コードは、原則として以下の配置を優先する。
|
||||
|
||||
```text
|
||||
src/app/main.py
|
||||
src/app/api/dependencies.py
|
||||
src/app/api/routers/<feature>.py
|
||||
src/app/schemas/<feature>.py
|
||||
src/app/services/<feature>_service.py
|
||||
src/app/repositories/<feature>_repository.py
|
||||
tests/api/test_<feature>.py
|
||||
```
|
||||
|
||||
既存の構成がある場合は、新規構成を勝手に作らず、既存構成を優先する。
|
||||
|
||||
## 構成
|
||||
|
||||
- ルーティングは `APIRouter` を使用する。
|
||||
- ルーターは機能単位で分割する。
|
||||
- `main.py` または `app.py` では FastAPI アプリ作成、middleware、router登録を中心に書く。
|
||||
- 共通依存関係は `dependencies.py` または `api/dependencies.py` に分離する。
|
||||
- リクエスト・レスポンスのPydanticモデルは `schemas.py` または `schemas/` に分離する。
|
||||
- 業務ロジックは `services/` に分離する。
|
||||
- DBアクセスは `repositories/` に分離する。
|
||||
|
||||
## Router
|
||||
|
||||
- `APIRouter` を使用する。
|
||||
- `prefix`、`tags` を適切に設定する。
|
||||
- ルーターは機能単位で分割する。
|
||||
- エンドポイント関数は薄く保つ。
|
||||
- エンドポイントでは以下のみを行う。
|
||||
- リクエストデータの受け取り
|
||||
- `Depends` による依存関係の受け取り
|
||||
- Serviceの呼び出し
|
||||
- レスポンスの返却
|
||||
- DB処理はRepository層に書く。
|
||||
- 業務ロジックはService層に書く。
|
||||
- ファイルI/O、外部システム連携、重い処理をエンドポイントに直接書かない。
|
||||
|
||||
## Request / Response
|
||||
|
||||
- リクエストボディは Pydantic モデルで定義する。
|
||||
- レスポンスは Pydantic モデルで定義する。
|
||||
- 可能な限り `response_model` を指定する。
|
||||
- DBモデル、DB行、内部オブジェクトをそのままAPIレスポンスとして返さない。
|
||||
- APIレスポンスには、外部に公開してよい項目だけを含める。
|
||||
- 入力値の制約は、可能な範囲でPydantic側に定義する。
|
||||
- Pydantic v2 を前提にする場合は、ORMオブジェクト変換に `ConfigDict(from_attributes=True)` と `model_validate()` を使用する。
|
||||
|
||||
## Service
|
||||
|
||||
- 業務ロジックはService層に書く。
|
||||
- ServiceはRepositoryを呼び出して必要なデータを取得・保存する。
|
||||
- ServiceはFastAPI固有の `Request` や `Response` に依存しすぎない。
|
||||
- Serviceはテストしやすいように、入力と出力を明確にする。
|
||||
- Service内で例外を握りつぶさない。
|
||||
- 必要に応じて独自例外に変換して呼び出し元へ伝える。
|
||||
|
||||
## Repository
|
||||
|
||||
- DBアクセスはRepository層に書く。
|
||||
- DBアクセスは既存のDB接続クラス・Repository層を優先する。
|
||||
- FastAPIでDBアクセスを行う場合は、原則としてSQLAlchemyを使用する。
|
||||
- SQLAlchemyは可能な限り2.x系の記述スタイルを優先する。
|
||||
- RepositoryはSQLAlchemyの `Session` またはDB接続を受け取り、Router層から直接DBへアクセスさせない。
|
||||
- SQL Server用RepositoryとOracleDB用Repositoryを分離する。
|
||||
- SQL Server と OracleDB の接続処理、Repository、SQLファイルは分離する。
|
||||
- OracleDBは参照専用とし、原則 `SELECT` のみ実行する。
|
||||
- OracleDBに対して `INSERT` / `UPDATE` / `DELETE` / `MERGE` / `CREATE` / `ALTER` / `DROP` は実行しない。
|
||||
- SQL Serverへの保存・更新・削除はSQL Server用Repositoryで行う。
|
||||
- SQLは可能な限り `.sql` ファイルに分離する。
|
||||
- SQL Server用SQLとOracleDB用SQLを混在させない。
|
||||
- 文字列連結でSQLを組み立てない。
|
||||
- パラメータ付きSQLを使用する。
|
||||
- 認証情報、接続文字列、ユーザー名、パスワードをコードに直書きしない。
|
||||
|
||||
## SQLAlchemy / SQL
|
||||
|
||||
- 単純なCRUD、主キー検索、単純な条件検索はSQLAlchemy ORMを優先する。
|
||||
- ORMモデルはDBテーブル構造を表すものとして扱い、APIレスポンスにはPydanticモデルを使用する。
|
||||
- ORMのリレーションを使用する場合は、N+1問題に注意する。
|
||||
- N+1問題が発生する可能性がある場合は、以下のいずれかで対策する。
|
||||
- `selectinload()` を使用する。
|
||||
- `joinedload()` を使用する。
|
||||
- 明示的な `join()` を使用する。
|
||||
- `sqlalchemy.text()` または `.sql` ファイルに分離したSQLを使用する。
|
||||
- 複雑なJOIN、集計、ウィンドウ関数、帳票用SQL、大量データ取得、性能要件が強い処理は、無理にORMだけで書かない。
|
||||
- ORMで記述すると可読性や性能が悪くなる場合は、`sqlalchemy.text()` を使用してSQLを明示的に記述する。
|
||||
- 長いSQL、再利用するSQL、DBごとに差があるSQLは `.sql` ファイルに分離する。
|
||||
- `text()` を使用する場合も、ユーザー入力値をSQL文字列へ直接埋め込まない。
|
||||
- `text()` では必ずバインドパラメータを使用する。
|
||||
- RepositoryはDBアクセス結果をServiceが扱いやすい形で返す。
|
||||
- レスポンス生成時に遅延ロードが発生しないよう、Repository層で必要なデータを取得しきる。
|
||||
- トランザクション境界はService層またはRepository層で明確に管理し、成功時はcommit、失敗時はrollbackする。
|
||||
|
||||
### ORMを優先する例
|
||||
|
||||
```python
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.job import Job
|
||||
|
||||
|
||||
class JobRepository:
|
||||
def __init__(self, session: Session) -> None:
|
||||
self._session = session
|
||||
|
||||
def fetch_job(self, job_id: int) -> Job | None:
|
||||
stmt = select(Job).where(Job.job_id == job_id)
|
||||
return self._session.scalars(stmt).first()
|
||||
```
|
||||
|
||||
### text() を使用する例
|
||||
|
||||
```python
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
|
||||
class JobRepository:
|
||||
def __init__(self, session: Session) -> None:
|
||||
self._session = session
|
||||
|
||||
def fetch_job_summary(self, job_id: int) -> dict[str, object] | None:
|
||||
stmt = text("""
|
||||
SELECT
|
||||
J.JOB_ID,
|
||||
J.STATUS,
|
||||
COUNT(S.SLOT_ID) AS SLOT_COUNT
|
||||
FROM dbo.SAW6_JOB_QUEUE AS J
|
||||
LEFT JOIN dbo.SAW6_JOB_QUEUE_SLOT AS S
|
||||
ON S.JOB_ID = J.JOB_ID
|
||||
WHERE J.JOB_ID = :job_id
|
||||
GROUP BY
|
||||
J.JOB_ID,
|
||||
J.STATUS
|
||||
""")
|
||||
row = self._session.execute(stmt, {"job_id": job_id}).mappings().first()
|
||||
return dict(row) if row is not None else None
|
||||
```
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
- FastAPI の `Depends` を使用する。
|
||||
- Settings、SQLAlchemy `Session`、DB接続、Repository、Service は依存関係として注入できる形にする。
|
||||
- 認証・認可・DB接続・設定取得などの共通処理は依存関係として共通化する。
|
||||
- テストで差し替えやすいように、依存関係は関数化する。
|
||||
- グローバル変数に直接依存する実装を避ける。
|
||||
- SQLAlchemy `Session` はリクエスト単位で生成・終了する。
|
||||
- Repository生成時にSQLAlchemy `Session` を渡し、Repository内部でグローバルなSessionを直接参照しない。
|
||||
|
||||
## Error Handling
|
||||
|
||||
- 入力不正は `HTTPException` で適切なHTTPステータスコードを返す。
|
||||
- リソースが存在しない場合は `404` を返す。
|
||||
- 未認証は `401`、権限不足は `403` を返す。
|
||||
- 内部例外の詳細をAPIレスポンスにそのまま返さない。
|
||||
- 内部エラーは `logging` で記録する。
|
||||
- クライアント向けレスポンスと内部ログを分離する。
|
||||
- 例外を握りつぶさない。
|
||||
- 必要に応じて独自例外をService層で発生させ、Router層でHTTPレスポンスへ変換する。
|
||||
|
||||
## Async / Sync
|
||||
|
||||
- `async def` と `def` は処理内容に応じて使い分ける。
|
||||
- 同期DBドライバを使う場合は、無理に `async def` にしない。
|
||||
- ブロッキングI/Oを `async def` の中で直接実行しない。
|
||||
- 長時間処理はAPIリクエスト内で完結させず、WorkerやJob Queueへの分離を検討する。
|
||||
- CSV出力待ち、ログ検索、重い集計、外部プログラム待ちなどはAPIから分離する。
|
||||
|
||||
## Testing
|
||||
|
||||
- FastAPI のAPIテストでは `TestClient` を使用する。
|
||||
- DB、外部API、ファイルI/Oはmockまたはdependency overrideで差し替える。
|
||||
- 正常系、異常系、バリデーションエラー、権限エラーをテストする。
|
||||
- Integration Test は `@pytest.mark.integration` を付けて通常実行から分離する。
|
||||
- テスト名は `test_<対象>_<条件>_<期待結果>` にする。
|
||||
- dependency override を使用した場合は、テスト後に `app.dependency_overrides.clear()` を実行する。
|
||||
|
||||
## 作成・修正時の出力方針
|
||||
|
||||
FastAPI APIを作成・修正する場合は、必要に応じて以下をセットで検討する。
|
||||
|
||||
1. Router
|
||||
2. Schema
|
||||
3. Service
|
||||
4. Repository
|
||||
5. Dependency
|
||||
6. Test
|
||||
7. 関連するSQLファイル
|
||||
8. 実行・確認コマンド
|
||||
|
||||
## Example
|
||||
|
||||
### Router
|
||||
|
||||
```python
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
|
||||
from app.api.dependencies import get_job_service
|
||||
from app.schemas.job import JobResponse
|
||||
from app.services.job_service import JobService
|
||||
|
||||
router = APIRouter(prefix="/jobs", tags=["jobs"])
|
||||
|
||||
|
||||
@router.get("/{job_id}", response_model=JobResponse)
|
||||
def get_job(
|
||||
job_id: int,
|
||||
service: JobService = Depends(get_job_service),
|
||||
) -> JobResponse:
|
||||
job = service.get_job(job_id)
|
||||
|
||||
if job is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Job not found",
|
||||
)
|
||||
|
||||
return JobResponse.model_validate(job)
|
||||
```
|
||||
|
||||
### Schema
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
|
||||
class JobResponse(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
job_id: int
|
||||
status: str
|
||||
```
|
||||
|
||||
### Dependency
|
||||
|
||||
```python
|
||||
from collections.abc import Generator
|
||||
|
||||
from fastapi import Depends
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import Session, sessionmaker
|
||||
|
||||
from app.repositories.job_repository import JobRepository
|
||||
from app.services.job_service import JobService
|
||||
|
||||
engine = create_engine("mssql+pyodbc://...", pool_pre_ping=True)
|
||||
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
|
||||
|
||||
|
||||
def get_db_session() -> Generator[Session, None, None]:
|
||||
session = SessionLocal()
|
||||
try:
|
||||
yield session
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def get_job_repository(
|
||||
session: Session = Depends(get_db_session),
|
||||
) -> JobRepository:
|
||||
return JobRepository(session=session)
|
||||
|
||||
|
||||
def get_job_service(
|
||||
repository: JobRepository = Depends(get_job_repository),
|
||||
) -> JobService:
|
||||
return JobService(repository=repository)
|
||||
```
|
||||
|
||||
### Service
|
||||
|
||||
```python
|
||||
from app.repositories.job_repository import JobRepository
|
||||
|
||||
|
||||
class JobService:
|
||||
def __init__(self, repository: JobRepository) -> None:
|
||||
self._repository = repository
|
||||
|
||||
def get_job(self, job_id: int) -> object | None:
|
||||
return self._repository.fetch_job(job_id)
|
||||
```
|
||||
|
||||
### Test
|
||||
|
||||
```python
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.api.dependencies import get_job_service
|
||||
from app.main import app
|
||||
|
||||
|
||||
class FakeJobService:
|
||||
def get_job(self, job_id: int) -> dict[str, int | str]:
|
||||
return {"job_id": job_id, "status": "DONE"}
|
||||
|
||||
|
||||
def test_get_job_valid_id_returns_job():
|
||||
app.dependency_overrides[get_job_service] = lambda: FakeJobService()
|
||||
|
||||
try:
|
||||
client = TestClient(app)
|
||||
|
||||
response = client.get("/jobs/1")
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json() == {"job_id": 1, "status": "DONE"}
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
applyTo: "**/*.py,config/**/*.toml"
|
||||
---
|
||||
|
||||
# Logging Instructions
|
||||
|
||||
## Basic Rules
|
||||
|
||||
- `print` は使用しない。
|
||||
- `logging` を使用する。
|
||||
- logging設定は TOML で管理する。
|
||||
- logger は用途別に分離する。
|
||||
- 例外発生時は `logger.exception(...)` を優先する。
|
||||
- ユーザー向けメッセージと内部ログを分離する。
|
||||
|
||||
## Logger Separation
|
||||
|
||||
必要に応じて、以下のように logger を分ける。
|
||||
|
||||
- アプリ全体: `app`
|
||||
- SQL / DB: `sql`
|
||||
- バッチ / Worker: `worker`
|
||||
- 外部I/O: `io`
|
||||
|
||||
## Log Level
|
||||
|
||||
- 開発環境では `DEBUG` 以上を出力する。
|
||||
- 本番環境では必要に応じて `INFO` 以上を基本にする。
|
||||
- エラー原因の調査に必要な情報は内部ログに残す。
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
applyTo: "**/oracle/**/*.py,**/repositories/**/*oracle*.py,**/db/**/*oracle*.py,**/sql/oracle/**/*.sql"
|
||||
---
|
||||
|
||||
# OracleDB Instructions
|
||||
|
||||
- OracleDB は外部システムからデータを取得するための参照専用DBとして扱う。
|
||||
- OracleDB に対しては原則 `SELECT` のみ実行する。
|
||||
- `INSERT` / `UPDATE` / `DELETE` / `MERGE` / `CREATE` / `ALTER` / `DROP` は実行しない。
|
||||
- OracleDB用SQLは `sql/oracle/` または `docs/sql/oracle/` に分離する。
|
||||
- SQL Server用SQLとOracleDB用SQLを混在させない。
|
||||
- OracleDB用RepositoryとSQL Server用Repositoryを分離する。
|
||||
- OracleDBから取得したデータを加工・保存する場合、保存先はSQL Server側Repositoryで扱う。
|
||||
- 認証情報、接続文字列、ユーザー名、パスワードをコードに直書きしない。
|
||||
- パラメータ付きSQLを使用し、文字列連結でSQLを組み立てない。
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
applyTo: "**/*.py"
|
||||
---
|
||||
|
||||
# Pandas Instructions
|
||||
|
||||
## Basic Rules
|
||||
|
||||
- DataFrame操作は関数に分離する。
|
||||
- 可能な限りベクトル化された操作を使用する。
|
||||
- 不要な for ループは避ける。
|
||||
- index の不一致による NaN 混入に注意する。
|
||||
- concat / merge / join の後は、必要に応じて index と列を検証する。
|
||||
- 入力DataFrameを破壊的に変更する場合は、関数名またはdocstringで明示する。
|
||||
|
||||
## Validation
|
||||
|
||||
- 必須列が存在するか確認する。
|
||||
- 行数、index、列名の整合性を確認する。
|
||||
- 欠損値、0、空文字、型不一致を考慮する。
|
||||
|
||||
## Visualization
|
||||
|
||||
- 可視化は原則 matplotlib を使用する。
|
||||
- seaborn は明示的に依頼された場合のみ使用する。
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
applyTo: "tests/**/*.py"
|
||||
---
|
||||
|
||||
# Pytest Instructions
|
||||
|
||||
## Basic Rules
|
||||
|
||||
- pytest を使用する。
|
||||
- AAA パターンで書く。
|
||||
- 1テスト1責務にする。
|
||||
- 1テスト1Actにする。
|
||||
- テスト名は `test_<対象>_<条件>_<期待結果>` にする。
|
||||
|
||||
## Arrange / Act / Assert
|
||||
|
||||
- Arrange: 前提データ、fixture、mockを準備する。
|
||||
- Act: 対象処理を1回だけ実行する。
|
||||
- Assert: 結果を検証する。
|
||||
|
||||
## Fixtures
|
||||
|
||||
- fixture は前提条件の名前にする。
|
||||
- fixture には assert を書かない。
|
||||
- fixture にテストロジックを書かない。
|
||||
- Arrange が長くなる場合は fixture 化する。
|
||||
|
||||
## Parametrize
|
||||
|
||||
- 分岐、境界値、異常系は `pytest.mark.parametrize` を優先する。
|
||||
- テスト内で if/else を増やしすぎない。
|
||||
|
||||
## Exception Test
|
||||
|
||||
- 例外テストでは `pytest.raises(..., match=...)` を使用する。
|
||||
- 例外の型だけでなく、必要に応じてメッセージも検証する。
|
||||
|
||||
## Mock / Integration
|
||||
|
||||
- Unit TestではDB、ファイルI/O、現在時刻、外部APIをmockする。
|
||||
- Integration Testには `@pytest.mark.integration` を付ける。
|
||||
- 通常実行では以下を使用する。
|
||||
|
||||
```bash
|
||||
uv run pytest -m "not integration"
|
||||
```
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
applyTo: "**/*.py"
|
||||
---
|
||||
|
||||
# Python Instructions
|
||||
|
||||
## Version / Environment
|
||||
|
||||
- Python 3.13 を前提にする。
|
||||
- パッケージ管理と仮想環境管理には `uv` を使用する。
|
||||
|
||||
## Type Hints
|
||||
|
||||
- すべての関数に引数と戻り値の型ヒントを付ける。
|
||||
- `Any` は必要最小限にする。
|
||||
- `dict` / `list` は可能な限り具体的に型指定する。
|
||||
- 戻り値がない関数は `-> None` を明示する。
|
||||
|
||||
## Path / File
|
||||
|
||||
- `pathlib.Path` を使用する。
|
||||
- `os.path` は使用しない。
|
||||
- ファイル読み書きでは `encoding="utf-8"` を明示する。
|
||||
- パスを受け取る関数では、可能な限り `Path` 型を使用する。
|
||||
|
||||
## Error Handling
|
||||
|
||||
- 例外は握りつぶさない。
|
||||
- 原因を失わないように `raise ... from e` を使用する。
|
||||
- ユーザー表示用エラーと内部エラーを分離する。
|
||||
- 内部エラーは `logging` で記録する。
|
||||
|
||||
## Design
|
||||
|
||||
- UI、DB、I/O、ビジネスロジックを分離する。
|
||||
- 副作用のある処理は関数名から意図が分かるようにする。
|
||||
- magic number は定数化する。
|
||||
- 1つの関数に複数の責務を持たせない。
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
applyTo: "**/settings.py,**/config/**/*.py,**/.env.example,**/*.env.example"
|
||||
---
|
||||
|
||||
# Settings Instructions
|
||||
|
||||
## Basic Rules
|
||||
|
||||
- 設定は `pydantic_settings.BaseSettings` で一元管理する。
|
||||
- 開発環境は `.env.dev` を使用する。
|
||||
- テスト環境と本番環境はOS環境変数を使用する。
|
||||
- パス系の設定値は `pathlib.Path` で扱う。
|
||||
- DB接続情報、APIキー、パスワードをコードに直書きしない。
|
||||
|
||||
## Naming
|
||||
|
||||
- 環境変数のprefixはプロジェクト単位で統一する。
|
||||
- 例: `APP_`, `XXX1_`, `WEB_XXX1_`
|
||||
|
||||
## Validation
|
||||
|
||||
- 必須設定が不足した場合は、明確なValidationErrorとして扱う。
|
||||
- 設定の不足をデフォルト値で隠さない。
|
||||
- 本番用の秘密情報を `.env` ファイルに残さない。
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
applyTo: "**/*.sql,**/db/**/*.py,**/repository/**/*.py,**/repositories/**/*.py,**/models/**/*.py"
|
||||
---
|
||||
|
||||
# SQL Server Instructions
|
||||
|
||||
## Basic Rules
|
||||
|
||||
- SQL Server 用の構文で書く。
|
||||
- DB接続は `pyodbc` または既存のDB接続クラスを使用する。
|
||||
- テーブル名・カラム名は既存DDLに合わせる。
|
||||
- SQLは可能な限り `.sql` ファイルに分離する。
|
||||
- 認証情報、接続文字列、パスワードをコードに直書きしない。
|
||||
|
||||
## Transaction
|
||||
|
||||
- INSERT / UPDATE / DELETE では commit / rollback を明示する。
|
||||
- 例外発生時は rollback する。
|
||||
- 例外は握りつぶさず、ログに記録して再raiseまたは独自例外に変換する。
|
||||
|
||||
## DDL
|
||||
|
||||
- 主キーは原則 `IDENTITY(1,1)` を使用する。
|
||||
- 日時型は特別な理由がなければ `DATETIME2(0)` を使用する。
|
||||
- 作成日時は `CREATED_AT DATETIME2(0) NOT NULL DEFAULT SYSDATETIME()` を基本にする。
|
||||
- 検索条件に使う列には INDEX を検討する。
|
||||
- 重複防止が必要な組み合わせには UNIQUE 制約を検討する。
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
applyTo: "app.py,pages/**/*.py,**/streamlit/**/*.py"
|
||||
---
|
||||
|
||||
# Streamlit Instructions
|
||||
|
||||
## Basic Rules
|
||||
|
||||
- UIとロジックを分離する。
|
||||
- Streamlitページには画面表示、入力受付、結果表示を中心に書く。
|
||||
- DBアクセス、ファイルI/O、集計処理は `src/` 側に分離する。
|
||||
- `st.session_state` は専用クラスまたは専用関数で管理する。
|
||||
- ユーザー向けエラーと内部エラーを分離する。
|
||||
|
||||
## Error Handling
|
||||
|
||||
- ユーザーに表示するメッセージは分かりやすくする。
|
||||
- 詳細な例外情報は logging に出力する。
|
||||
- 例外を握りつぶさない。
|
||||
- 既存のユーザー向け例外クラスがある場合は、それを優先する。
|
||||
|
||||
## Long Running Task
|
||||
|
||||
- 長時間処理をStreamlitのリクエスト内で直接実行しない。
|
||||
- 時間がかかる処理はWorker、Job Queue、外部プロセスへの分離を検討する。
|
||||
- Streamlit側はジョブ登録とステータス表示を担当する。
|
||||
Reference in New Issue
Block a user