# バッチ設計書 14-update_news_feed

## 概要

本ドキュメントは、InvenTreeシステムにおけるニュースフィード更新バッチ処理について記述します。このバッチは、外部ニュースフィード（RSSフィード）を取得・パースし、新しいニュースエントリをデータベースに保存することで、ユーザーに最新のInvenTree関連情報を提供します。

### 本バッチの処理概要

**業務上の目的・背景**：InvenTreeは、ユーザーに対してプロジェクトの最新情報やリリースノート、重要なお知らせを提供する機能を持っています。外部のニュースフィードから情報を取得し、システム内に保存することで、ユーザーはInvenTreeのダッシュボードから最新情報にアクセスできます。この機能により、ユーザーはシステムの更新情報や重要なアナウンスを見逃すことなく把握できます。

**バッチの実行タイミング**：日次（毎日1回）。Django-Qスケジューラによりスケジュールされ、バックグラウンドワーカーによって実行されます。

**主要な処理内容**：
1. 設定されたニュースフィードURLの存在確認
2. 外部URLからRSSフィードを取得（HTTPリクエスト）
3. feedparserを使用してフィードをパース
4. 既存エントリとの重複チェック
5. 新規エントリをデータベースに保存
6. 相対リンクをベースURLで補完

**前後の処理との関連**：本バッチは独立して動作し、他のバッチとの直接的な依存関係はありません。取得したニュースはダッシュボード表示機能から参照されます。

**影響範囲**：common_newsfeedentryテーブルに対して挿入操作を行います。既存のニュースエントリの更新・削除は行いません。

## バッチ種別

データ連携

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 日次 |
| 実行時刻 | Django-Qスケジューラによる自動実行 |
| 実行曜日 | 毎日 |
| 実行日 | - |
| トリガー | cron（Django-Qスケジューラ） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| Django-Qワーカー稼働中 | バックグラウンドワーカーが稼働している必要がある |
| データベース接続可能 | NewsFeedEntryテーブルへのアクセスが可能であること |
| INVENTREE_NEWS_URL設定済み | settings.pyでニュースフィードURLが設定されていること |
| 外部ネットワーク接続可能 | RSSフィードURLへのHTTPアクセスが可能であること |

### 実行可否判定

- INVENTREE_NEWS_URLが未設定または文字列でない場合は処理をスキップ
- AppRegistryNotReady例外発生時はスキップしてログ出力

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| なし | - | - | - | 本バッチはパラメータを取らない |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| settings.INVENTREE_NEWS_URL | URL | RSSフィードのURL |
| 外部RSSフィード | XML/RSS | ニュースフィードデータ |
| common_newsfeedentry | DB | 既存ニュースエントリ（重複チェック用） |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| common_newsfeedentry | DB | 新規ニュースエントリの保存 |
| ログ出力 | テキスト | 同期完了メッセージ |

### 出力ファイル仕様

ファイル出力なし

## 処理フロー

### 処理シーケンス

```
1. バッチ開始
   └─ NewsFeedEntryモデルのインポート確認
2. ニュースURL設定確認
   └─ INVENTREE_NEWS_URLが未設定の場合は終了
3. RSSフィード取得
   └─ requests.get()でフィードを取得（タイムアウト: 30秒）
4. フィードパース
   └─ feedparser.parse()でXMLをパース
5. 既存エントリID取得
   └─ 重複チェック用のID一覧を取得
6. 新規エントリ処理
   └─ 各エントリについて:
      ├─ 既存IDの場合はスキップ
      ├─ 相対リンクをベースURLで補完
      └─ NewsFeedEntry.objects.create()で保存
7. バッチ終了
   └─ ログ出力: "update_news_feed: Sync done"
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B{NewsFeedEntry<br/>インポート成功?}
    B -->|失敗| C[ログ出力して終了]
    B -->|成功| D{INVENTREE_NEWS_URL<br/>設定あり?}
    D -->|なし| E[処理終了]
    D -->|あり| F[RSSフィード取得]
    F --> G{取得成功?}
    G -->|失敗| H[警告ログ出力して終了]
    G -->|成功| I[feedparserでパース]
    I --> J[既存エントリID一覧取得]
    J --> K[各エントリを処理]
    K --> L{既存ID?}
    L -->|はい| M[スキップ]
    L -->|いいえ| N[リンクURL補完]
    N --> O[DBに保存]
    O --> P{次のエントリ?}
    M --> P
    P -->|あり| K
    P -->|なし| Q[ログ出力: Sync done]
    Q --> R[バッチ終了]
    C --> R
    E --> R
    H --> R
```

## データベース操作仕様

### 操作別データベース影響一覧

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 既存ID取得 | common_newsfeedentry | SELECT | 重複チェック用ID一覧取得 |
| 新規エントリ保存 | common_newsfeedentry | INSERT | 新しいニュースエントリの作成 |

### テーブル別操作詳細

#### common_newsfeedentry

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | feed_id | 全レコード | 重複チェック用 |
| INSERT | feed_id | entry.id | フィードのエントリID |
| INSERT | title | entry.title | エントリのタイトル |
| INSERT | link | entry.link | エントリへのリンクURL |
| INSERT | published | entry.published | 公開日時 |
| INSERT | author | entry.author | 著者名 |
| INSERT | summary | entry.summary | 概要テキスト |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | AppRegistryNotReady | Djangoアプリ未起動 | ログ出力後にスキップ |
| - | RequestException | フィード取得失敗 | 警告ログ出力後に処理終了 |
| - | ParseError | フィードパース失敗 | 警告ログ出力後に処理終了 |
| - | IntegrityError | 重複エントリ挿入 | 例外を無視して継続 |
| - | OperationalError | データベースエラー | 例外を無視して継続 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Django-Qのデフォルト設定に依存 |
| リトライ間隔 | Django-Qのデフォルト設定に依存 |
| リトライ対象エラー | 一時的なネットワークエラー |

### 障害時対応

1. ログを確認してエラー原因を特定
2. ネットワーク接続状態を確認
3. RSSフィードURLの有効性を確認
4. 必要に応じて手動で再実行

## トランザクション仕様

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | レコード単位 |
| コミットタイミング | 各エントリ保存時 |
| ロールバック条件 | 個別エントリ保存失敗時（他のエントリには影響しない） |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 数件〜数十件/日 |
| 目標処理時間 | 1分以内 |
| メモリ使用量上限 | 特に制限なし |

## 排他制御

- 同時実行可能（重複チェックにより同一エントリの二重登録は防止）
- ただし、通常は日次1回の実行のため同時実行は想定しない

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 情報ログ | App未準備時 | "Could not perform 'update_news_feed' - App registry not ready" |
| 警告ログ | フィード取得失敗時 | "update_news_feed: Error parsing the newsfeed" |
| 情報ログ | 処理完了時 | "update_news_feed: Sync done" |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| タスク失敗 | 1回 | システム管理者 |
| 処理時間 | 2分超過 | システム管理者 |
| フィード取得エラー | 連続3回 | システム管理者 |

## 備考

- HTTPリクエストのタイムアウトは30秒に設定
- feedparserライブラリを使用してRSS/Atomフィードをパース
- 相対リンク（/で始まるリンク）はINVENTREE_BASE_URLで補完
- IntegrityErrorやOperationalErrorは無視して処理を継続（データベース起動時のエラー対策）
- OpenTelemetryによるトレーシングに対応
