# ICS → Discourse Importer

**URL:** https://meta.discourse.org/t/ics-discourse-importer/381646
**Category:** Extras
**Tags:** events, rest-api
**Created:** [2025 年 9 月 4 日午後 9:53 UTC](https://meta.discourse.org/t/ics-discourse-importer/381646 "2025-09-04T21:53:54Z")
**Posts on this page:** 1
**Showing post:** 12

<div class="post-metadata">

### Author: ![Ethsim2](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ethsim2/32/522255_2.png) [@Ethsim2](https://meta.discourse.org/u/Ethsim2)
#### Post date: [2025 年 9 月 11 日午後 3:32 UTC](https://meta.discourse.org/t/ics-discourse-importer/381646/12 "2025-09-11T15:32:26Z")

</div>

### `ics_to_discourse.py` テストからの動作に関する注記

このスクリプト（`--time-only-dedupe` あり・なし両方）で一連のテストを実行したので、更新/採用フローを詳細に文書化しておくと便利だと思いました。

* * *

#### 1. 一意性の決定方法

- **デフォルトモード:** 採用には **開始 + 終了 + 場所** が完全に一致する必要があります。
- **`--time-only-dedupe` を使用した場合:** 採用には **開始 + 終了** のみが必要です。場所は「十分近い」として扱われます。

これらのルールに一致する既存のトピックがない場合、新しいトピックが作成されます。

* * *

#### 2. UID マーカーの役割

- すべてのイベントトピックには、最初の投稿に非表示の HTML マーカーが付けられます。

```plaintext
  <!-- ICSUID:xxxxxxxxxxxxxxxx -->

```

- 後続の実行で、スクリプトはこのマーカーを最初に検索します。
- 見つかった場合、そのトピックは UID マッチと見なされ、DESCRIPTION テキストがどれほどノイズが多くても古くても、直接更新されます。
- これにより、UID が **真の識別子キー** になります。表示される説明フィールドはマッチングに影響しません。

* * *

#### 3. UID マッチによる更新フロー

1. スクリプトは最初の投稿を取得し、マーカーを削除します。

```python
 old_clean = strip_marker(old_raw)
 fresh_clean = strip_marker(fresh_raw)

```

1. old\_clean == fresh\_clean の場合: 更新なし（変更による乱雑さを回避）。
2. 異なる場合: 変更が「意味のある」ものかどうかを確認します。

```python
meaningful = (
    _norm_time(old_attrs.get("start")) != _norm_time(new_attrs.get("start"))
    or _norm_time(old_attrs.get("end")) != _norm_time(new_attrs.get("end"))
    or _norm_loc(old_attrs.get("location")) != _norm_loc(new_attrs.get("location"))
)

```

- meaningful = True → 更新してバンプ（トピックが「最新」で上位に表示される）。

- meaningful = False → 静かに更新（bypass\_bump=True → リビジョンのみ、バンプなし）。

* * *

1. UID マッチなしによる更新フロー
  1. スクリプトは採用を試みます。  
• 開始/終了/場所（または --time-only-dedupe では開始/終了のみ）の候補トリプルを構築します。  
• 一致する属性を持つ既存のイベントを /search.json および /latest.json で検索します。  
• 見つかった場合 → そのトピックを採用し、UID マーカーとタグを後付けします（この段階では本文は変更されません）。  
• 見つからなかった場合 → マーカーとタグを持つまったく新しいトピックを作成します。
  2. 採用または作成されると、将来のすべての同期は UID によって直接解決されます。

⸻

1. 実用上の結果  
• 時間の変更  
• デフォルト: 採用失敗（時間が異なる）→ 新しいトピックが作成されます。  
• `--time-only-dedupe` を使用した場合: 同様に採用失敗。新しいトピックが作成されます。  
• 場所の変更  
• デフォルト: 採用失敗（場所が異なる）→ 新しいトピックが作成されます。  
• `--time-only-dedupe` を使用した場合: 採用成功（時間が一致する）が、場所の違いは「意味のある」ものとしてフラグ付けされ、バンプ付きで更新されます。  
• 説明の変更  
• DESCRIPTION テキストが変更されたが、開始/終了/場所は変更されなかった場合:  
• 本文は静かに更新されます（bypass\_bump=True）。  
• トピックのリビジョンが作成されますが、「最新」でのバンプはありません。  
• DESCRIPTION が変更されていない（または正規化されて削除される Last Updated: のようなノイズのみの場合）、更新はまったく行われません。  
• UID マーカー  
• 将来の同期で確実にマッチングが行われます。  
• DESCRIPTION が古かったりノイズが多くても、正しいトピックが見つかることを保証します。

⸻

1. DESCRIPTION が「同じまま」になることがある理由  
スクリプトは UID マーカーを除いた本文全体を比較します。  
Last Updated: のような揮発性の行のみが異なる場合でも、正規化されて（例: 空白、改行、Unicode）削除されると、old\_clean と fresh\_clean は同一に見えます → 更新は行われません。  
これは、フィードのノイズによる変更を避けるための意図した動作です。

⸻

概要  
• 時間が一意性を定義します（時間が変更されると常に新しいトピックが作成されます）。  
• 場所の変更 → 目に見えるバンプ（ユーザーが会場の更新に気づくように）。  
• 説明の変更 → 静かな更新（リビジョンはあるがバンプはない）。  
• UID マーカー = 信頼性の高い識別子キー。DESCRIPTION が古かったりノイズが多くても、常に正しいトピックが見つかることを保証します。

これにより、重要な変更は「最新」に表示され、重要でない変更は表示されないという良いバランスが取れています。

---

_[View the full topic](https://meta.discourse.org/t/ics-discourse-importer/381646)._
