mcpField
E
education-cloud-course-catalog-migrate

education-cloud-course-catalog-migrate

github.com/forcedotcomデータ・データベース1.1k112 回インストール1 日前に更新
リポジトリ

education-cloud-course-catalog-migrate について

CSV、PDF、ウェブサイトのコースカタログを Education Cloud に取り込み、Learning と LearningCourse のレコードを作成する Agent スキルです。バッチ作成に加え、データの完全性やコース間の関係、既存レコードとの競合を確認します。

主な機能

  1. 01CSV・PDF・ウェブサイトからコース情報を抽出
  2. 02必須項目を検証し不足を特定
  3. 03講義と実験のコース関連を検出
  4. 04Learning と LearningCourse をバッチ作成
  5. 05競合を確認し解決方法を提示

活用シーン

  1. 01学部のコースカタログを Education Cloud に取り込み
  2. 02複数ソースのコース項目を整理
  3. 03取り込み前に既存コースと重複を確認

MCP 対応

Salesforce sObject REST API と Composite API でコースレコードを作成し、事前に API の通信経路を確認します。代替経路として他の Salesforce MCP やカスタム MCP の確認に触れています。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/forcedotcom/sf-skills --skill education-cloud-course-catalog-migrate

インストール後、Claude Code で/education-cloud-course-catalog-migrateを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
gotchas-detail.md
gotchas.md
large-catalog-handling.md
scripts/
batch_courses.py
detect_linked_courses.py
detect_modality_variants.py
resolve_api_version.py
split_course_code.py
validate_completeness.py

詳細

コースカタログをEducation Cloudに移行する

複数のソース(CSV、PDF、ウェブサイト)からコースカタログデータをインポートし、構造の検出とデータ品質の検証を行いながら、Learning Courseレコードを一括作成します。

対象範囲

  • 対象範囲:複数ソースからの取り込み(CSV、PDF、ウェブ)、sObject REST APIを使ったLearningおよびLearningCourseの一括作成(Compositeバッチは最大200件)、関連コースの検出(講義+実習はIsLinkedOnlyを使用)、単位時間の解析(Duration/DurationUnit)、コース種別のマッピング、データ品質の検証、競合の解決、不足項目の特定と修正
  • 対象外:Learning Courseレコードの個別作成(メタデータ生成スキルに委任)、Learning Programの作成(Learning.Type = 'LearningProgram'+別の子オブジェクト)、External Learningの作成(Learning.Type = 'ExternalLearning')、コースのセクション/開講科目(CourseOfferingオブジェクト)、履修登録/受講登録のワークフロー(専用スキルはまだありません。ユーザーに確認してください)、SIS同期(別の統合ワークフロー)

必須入力

作業を進める前に、次の情報を収集するか推定してください。

  • 対象組織:Education Cloudが有効なアクティブなSalesforce組織
  • 主なデータソース:コースカタログデータを含むCSVファイル、PDF文書、またはウェブサイトのURL
  • Business Account:コースを関連付けるAcademic Operations Business Account(例:College of Engineering)
  • 学期(任意):コンテキストとしてのみ使用し、Learning Courseには保存しません

追加のソース(ワークフロー中に収集):

  • 不足項目(説明、種別、マッピング)を補うための追加データソース
  • 複数構成のコースが検出された場合の講義と実習の対応付けファイル
  • 競合解決の判断(既存コースが見つかった場合)

特に指定がない場合の既定値:

  • コースのステータス:Active(LearningのIsActive = true)
  • バッチサイズ:Composite API呼び出し1回あたり25レコード(エージェントの場合。APIは200件をサポートしますが、制約となるのはツール呼び出しのペイロード上限です。Step 7を参照)
  • データ品質の検証:有効
  • Learning.Type:'LearningCourse'(コースカタログのエントリに必須)

実行時の通信経路

まずAPIバージョンを解決してください。ハードコードしないでください。 GET /services/data/(バージョンセグメントなし)は、{version, label, url}のリストではなく、組織の最大バージョンに解決済みのエンドポイントマップを返します。これを scripts/resolve_api_version.pyに渡すと{"version": "69.0"}が得られます。トランスポートの確認を含め、以下ではすべてvXX.Xを使用してください。

次に、headless-360のディスパッチ経由でトランスポートの状態を確認します:GET /services/data/vXX.X/limits。2xxの場合は、すべての呼び出しをこの経路に通します。見つからない場合、または4xx/5xxの場合は、別のトランスポート(別のSalesforce MCP、カスタムMCP)を確認し、最初に正常と確認できたものを使用して、その旨を伝えます。正常なものが1つもない場合は停止し、接続を依頼してください。決して作り上げてはいけません。(SOQLの/queryはheadless-360でルーティング可能であることをライブテストで確認済みです。/tooling/queryと?fields=は未確認のため、describeとレコードの直接読み取りを使用してください。)

組織の認証情報を取得するために、sf CLIへ切り替えたり、シェルコマンドを実行したりしないでください。バッチの処理が遅い、またはサイズが大きすぎる場合は、トランスポートの障害ではなくバッチサイズの問題です(縮小してください。Step 7を参照)。また、CLIをシェルから実行すると、一部の環境では回避できない承認ゲートに阻まれます。組織でサポートされるバージョンより新しいバージョンを指定すると、正常なトランスポートでも404 NOT_FOUNDが返されます。その場合はGET /services/data/を再度実行し、組織の最大バージョンに下げてから再試行してください。トランスポートを切り替えてはいけません。どちらのケースについても、references/gotchas.mdを参照してください。


ワークフロー

すべてのステップを順番に実行してください。省略したり、順序を入れ替えたりしないでください。作業が進められない場合は停止し、不足している情報を確認してください。

フェーズ 1 — 主ソースの取り込み
  1. データソースの種類を特定する — 主ソースが CSV、PDF、ウェブサイトの URL のいずれかを判定します。まだ指定されていない場合は、ユーザーにソースの提供を依頼します。

  2. 主ソースを解析する — コースデータを抽出します。

    • CSV の場合:Read ツールを使用してファイルを読み込み、列を解析します
    • PDF の場合:Read ツールを使用してテキストを抽出します(PDF 抽出に対応)。20 ページを超える場合は、ページ範囲ごとに順番に読み取り、完全性を検証する前に解析した行を統合します — references/large-catalog-handling.md §1 を参照
    • ウェブサイトの場合:WebFetch ツールを使用して HTML を取得し、コース一覧を解析します

    次の項目を確認します:コースコード(識別子 — 教科略称とコース番号に分割します。手順 8/9 を参照)、コース名(Name)、単位時間(Duration + DurationUnit)、説明(Description)、コースの種類(講義/実習/試験/演習/ゼミ)、前提条件、共修条件、関連コースの指標(IsLinkedOnly)、提供形式(同じコースコードが異なる Duration/DurationUnit で繰り返される場合 — references/large-catalog-handling.md §2 を参照)。

  3. データの完全性を検証する — 解析したレコードを完全性チェック用ヘルパーに渡します。

    python3 scripts/validate_completeness.py --required "CourseCode,Name,Duration" --recommended "Description,CourseType" <<'JSON'
    [ { "CourseCode": "...", "Name": "...", ... }, ... ]
    JSON
    

    レポートには、フィールドごとの入力済み/未入力の件数が表示されます。任意項目(前提条件、共修条件、関連コース)はチェックされません。これらが未入力でも、処理が妨げられることはありません。

    必須フィールドに missing > 0 がある場合は、エラーを報告して停止します。推奨フィールドに未入力がある場合は、不足項目を記録してフェーズ 2 に進みます。

フェーズ 2 — 不足項目の特定と追加ソースの依頼
  1. データ品質を報告する — 入力済みのフィールドと不足しているフィールドを示す概要表を出力します。例:

    Data Quality Report:
    • Course codes: 120/120 OK
    • Course names: 120/120 OK
    • Credit hours: 120/120 OK
    • Descriptions: 115/120 (5 missing)
    • Course types: 118/120 (2 missing)
    
  2. 補足ソースを依頼する — 重要なフィールド(説明、種類など)が不足している場合は、ユーザーに「追加のソース(ウェブサイト、学部の文書、マッピングファイルなど)はありますか?」と尋ねます。

    • ある場合:追加ソースを受け取り、解析して主ソースのデータと統合します
    • ない場合:プレースホルダー値を使ってコースを作成するか、完全なデータがそろうまで停止して待機するかを提案します

    すべての不足項目が埋まるか、不完全なデータで処理を進めることをユーザーが確認するまで、この手順を繰り返します。

    教科と Business-Account の対応関係が曖昧な場合は、曖昧なケースをすべて一度に解決し(ソース文書 + 公開中のカタログ + コースのレベル/番号のパターン)、1 件ずつではなくまとめて提示します(references/gotchas.md を参照)。

  3. 複数コンポーネントのコースを検出する — コースコードを講義/実習ペアリング用ヘルパーに渡します。

    python3 scripts/detect_linked_courses.py --suffixes "L,LAB" <<'JSON'
    [ "BIO-201", "BIO-201L", ... ]
    JSON
    

    返された各 {"lecture", "lab"} ペアは、複数コンポーネントのコース候補です。

    • ペアが見つかった場合は処理を一時停止し、次のように説明します:「講義と実習を含む複数コンポーネントのコースが X 件見つかりました。(1)独立した別々のコースとして構成するか、(2)関連コース(IsLinkedOnly フラグを設定し、学生が両方を履修する必要がある)として構成しますか?」

    ユーザーが「関連コース」を選択した場合は、マッピングファイルを依頼するか、命名パターンが信頼できることを確認します。

    提供形式の重複を検出する — 解析した行をコースコードごとにグループ化します。同じコードでも Duration/DurationUnit/Description が異なる場合は、重複ではなく提供形式のバリエーション(例:オンラインと対面)です。暗黙のうちに重複排除または統合してはいけません。表現方法はコースごとではなく、実行ごとに一度だけ決定します。references/large-catalog-handling.md §2 を参照してください。

フェーズ 3 — Learning および LearningCourse レコードの一括作成
  1. 一括作成の計画を準備する — 解析したコースレコードを一括処理用ヘルパーに渡します。Composite API では 1 回の呼び出しにつき最大 200 件のレコードを受け付けますが、エージェントのツール呼び出しペイロードには、実用上の上限(約 56KB/150 件)があります。エージェントとして実行する場合は --batch-size 25 を使用してください(呼び出し環境がより大きなペイロードに対応できることを確認できた場合に限り、最大 200 まで増やします)。

    python3 scripts/batch_courses.py --batch-size 25 <<'JSON'
    [ { "Name": "...", ... }, ... ]
    JSON
    

    返された配列の各要素は、1 回の Composite API 呼び出しに含めるレコード一式です。次のように出力します:「コースカタログのインポートを開始します。Composite API を使用してレコードを一括作成します。合計:X 件のコース。」 正常に動作しているディスパッチトランスポートを通じてバッチをインラインで処理できない場合は、トランスポートを切り替えるのではなく、--batch-size を小さくして(例:10)再実行してください。ディスパッチとフォールバックの件数を記録し、Phase 5 のサマリーで両方を報告してください。

    200 コース超(8 バッチ以上):最初にバッチの総数を明示し、バッチごとに(最後だけでなく)Batch N complete の行を出力してください。また、サーフェスに並列タスク/サブエージェント用ツールがある場合に限り、references/large-catalog-handling.md §3-4 のファンアウトルールに従ってください。その機能があると決めつけてはいけません。既定ではバッチを順次処理してください。

  2. 既存コースを確認する — Batch 1 を作成する前に、確認対象をこのバッチ独自のコースコードに限定してください。全件取得はしないでください(組織内に無関係なカタログエントリがすでにあっても、競合にはなりません)。

    まず、このバッチのコースコードを分割してください(scripts/split_course_code.py)。確認にはそれらの識別フィールドを使い、CipCode は使わないでください(多くの組織では null のままです。references/gotchas.md を参照)。

    Headless-360 に関する注意:SOQL /query は headless-360 ディスパッチツール経由でルーティング可能です(ライブテストで確認済み)。各分割済み {SubjectAbbreviation, CourseNumber} の組み合わせに完全一致する LearningCourse を照会してください(SOQL にはタプルの IN がないため、各ペアを OR でつなぎます):SELECT Id, LearningId, Name, SubjectAbbreviation, CourseNumber FROM LearningCourse WHERE (SubjectAbbreviation = 'BIO' AND CourseNumber = '201') OR ...。Id が不明で、SOQL による照会もできない場合は、ユーザーに Id(または以前のインポートマニフェスト)の提示を依頼してください。Bash/CLI 経由では絶対に行わないでください。references/gotchas.md を参照してください。

    結果が返された場合(つまり、このバッチ内の SubjectAbbreviation+CourseNumber の組み合わせが組織内にすでに存在する場合)は、一時停止して競合について説明してください。

    • 既存コースの詳細と新しいインポートデータを並べて表示する
    • 既存コースに依存関係(コース開講、履修登録)があるか確認する
    • 次の選択肢を提示する:(1)既存コースを更新する、(2)既存コースをスキップする、(3)キャンセルして手動で確認する

    先に進む前に、ユーザーの判断を待ってください。

  3. Batch 1 を作成する — 各コースについて、次の 2 段階で処理します。

    フィールドの配置(describe で確認済み):CipCode、Duration、DurationUnit、ProviderId は Learning のみで作成可能です。子レコードの ProviderId は INVALID_FIELD_FOR_INSERT_UPDATE エラーになります。LearningCourse が受け取るのは Name、LearningId、CourseType、IsLinkedOnly、SubjectAbbreviation、CourseNumber、Description、CourseLevelDescription、FieldOfStudy です。LearningId を介してアカウントの関連付けを継承します。リクエストボディを作成する前に describe で確認してください。

    CipCode は任意です。 これは連邦の CIP 分類フィールドであり、コースの識別情報ではありません。ソースに実際の CIP コードが記載され、ユーザーが確認した場合を除き、設定しないでください。

    ステップ 1:Composite API で Learning(親)レコードを作成する:

    POST /services/data/vXX.X/composite/sobjects
    {
      "allOrNone": false,
      "records": [
        {
          "attributes": {"type": "Learning", "referenceId": "learning1"},
          "Name": "<course-name>",
          "Type": "LearningCourse",
          "Description": "<description>",
          "ProviderId": "<account-id>",
          "Duration": <hours>,
          "DurationUnit": "Credit Hours",
          "IsActive": true
        },
        ...
      ]
    }
    

    返された各 Id を取得してください(順序は records 配列と一致します)。ステップ 2 で LearningId として使用します。

    ステップ 2:Composite API で LearningCourse(子)レコードを作成する:

    POST /services/data/vXX.X/composite/sobjects
    {
      "allOrNone": false,
      "records": [
        {
          "attributes": {"type": "LearningCourse"},
          "Name": "<course-record-name>",
          "LearningId": "<learning-id-from-step-1>",
          "SubjectAbbreviation": "<subject-prefix>",
          "CourseNumber": "<course-number>",
          "CourseType": "<parsed-course-type>",
          "IsLinkedOnly": false
        },
        ...
      ]
    }
    

    各レコードについて、解析/マッピングしたコース種別から CourseType を設定してください(値のマッピングは Gotchas の表を参照)。固定値を指定しないでください。実習レコードには CourseType: "Lecture" ではなく CourseType: "Lab" を設定する必要があります。

    CourseType の選択リスト値(確認済み):Lecture、Lab、Exam、Drill、Recitation。

    restrictedPicklist: true は、今日送信できるのが現在の値だけであることを意味し、値の集合が固定されているという意味ではありません。 ソースの値(例:DurationUnit = "Program Points")が describe の一覧にない場合、黙って削除したり別の値にマッピングしたりしないでください。マッピング変更またはプレースホルダーに加え、メタデータ経由で選択リスト値を追加する方法も提示してください(references/gotchas.md を参照)。

    進捗を出力してください:Batch 1 complete — X Learning + X LearningCourse records created。

  4. 複数コンポーネントで構成されるコースを処理する — ステップ 6 でユーザーが「linked courses」を選択した場合:

    • CourseType: "Lecture"、IsLinkedOnly = false を指定して講義コースを作成します
    • CourseType: "Lab"、IsLinkedOnly = true を指定してラボコースを作成します
    • detect_linked_courses.py のペア(またはユーザーが指定したマッピングファイル)を使って、どのラボがリンク専用かを判定します
    • X linked lab courses created (IsLinkedOnly = true) と出力します
  5. 残りのバッチで繰り返す — すべてのコースが作成されるまで、バッチ 2、バッチ 3 などについてステップ 8~10 を続けます。

フェーズ 4 — データ品質の検証と競合の解決
  1. 不完全なレコードをスキャンする — すべてのバッチが完了したら、作成レスポンスから取得した Id を使って、新しく作成した各レコードを読み戻します:

    GET /services/data/vXX.X/sobjects/Learning/<id>
    GET /services/data/vXX.X/sobjects/LearningCourse/<id>
    

    Headless-360 に関する注意:SOQL /query は headless-360 のディスパッチツール経由でルーティングできます(実際のテストで確認済み)。CreatedDate = TODAY クエリを使えば、取得した Id を使って各レコードを直接 GET する代わりにできます。

    完全性チェックのヘルパーは、sObject ごとに別々に実行します。CourseType は LearningCourse にのみ存在し、Description は Learning にのみ存在します。両方の形状を 1 回の呼び出しにまとめると、すべての親レコードで CourseType が欠落しているという誤検知が発生します:

    python3 scripts/validate_completeness.py --required "Name" --recommended "Description" <<'JSON'
    [ { "Name": "...", "Description": "...", ... }, ... ]   # Learning の読み戻しのみ
    JSON
    

    --recommended "CourseType" を指定して、LearningCourse の読み戻しに対しても繰り返します。説明が欠落しているコース(Learning レポート)または種類が欠落しているコース(LearningCourse レポート)を特定します。ステップ 4 と同じ形式でデータ品質レポートを出力します。

  2. データの競合を検出する — 複数の情報源で同じコースの値が異なる場合は、競合として報告します。例:「CHEM-301:ウェブサイトでは X、PDF では Y と記載されています。どちらを使用しますか?」ユーザーの判断を待ち、競合しているコースを更新します。

  3. 不足情報を補完する — ユーザーが不足情報を埋めるための追加の情報源を提示した場合は、それを解析して不完全なコースを更新します:

    PATCH /services/data/vXX.X/sobjects/Learning/<id>
    {"Description": "..."}
    
    PATCH /services/data/vXX.X/sobjects/LearningCourse/<id>
    {"CourseType": "<parsed-course-type>"}
    
フェーズ 5 — 概要と検証
  1. インポート概要を生成する — 最終レポートを出力します:

    コースカタログのインポートが完了しました!
    概要:
    • X 件の Learning レコードと X 件の LearningCourse レコードを作成しました
    • Y 件の単独コース、Z 件のリンク済みラボコース(IsLinkedOnly)
    • すべてのコースを <Business Account name> にリンクし、Active に設定しました
    使用したデータソース:1. <primary> 2. <supplementary 1> 3. <supplementary 2>
    

    補足ソースから不足分を補完した場合(手順 5/14 を実行した場合)、最終レポートには次の内容もこの順序で必ず含めてください:

    • データ品質レポート — 「初期データ(<primary source> から)」と「補足ソースの適用後(<supplementary source>)」の 2 つの表。それぞれに、手順 4 のすべてのフィールドと、その入力済み数/総数を記載します。最終的な件数だけでなく、適用前と適用後の両方を示します。
    • 不足分の補完 — フィールド別(例:「説明が不足していたコース(<source> から補完)」)に分類し、補完したコースすべてを、受け取った値とともに番号付きリストで示します。補完したフィールドごとにリストを 1 つ作成します。
  2. 検証を提案する — ユーザーに次のように尋ねます:「特定のコースを照会して、データを検証しますか? たとえば、LearningCourse 子レコードとともにコースを表示したり、残っているデータ品質の問題を確認したりできます。」

  3. 実行中のユーザーが、作成したデータを実際に参照できることを確認する — 問題があると指摘するだけでなく、実際に確認します(解決中のユーザー + FLS/RecordTypeVisibility のパターンについては references/gotchas.md を参照)。対象は、処理したすべてのオブジェクト(Person Accounts が関係する場合は Learning、LearningCourse、Account)です。問題と該当するプロファイルを明示し、具体的な修正方法を提案したうえで、Profile/PermissionSet メタデータを書き込む前にユーザーの確認を待ちます。OWD/共有設定がデフォルトで非公開の場合、その設定は組織全体に適用され、ユーザーごとの設定ではありません。変更を提案せず、その旨を伝えてください。


ルール/制約

制約理由
Composite API を使用し、エージェントによる実行では 1 回の呼び出しにつき 25 件のレコードをまとめて処理します(API は最大 200 件に対応)H360 MCP の sObject REST 標準では、150 レコードのペイロード(約 56 KB)は一般的なツール呼び出しの上限を超えます
先に Learning を作成し、その後に LearningCourse を作成しますLearningCourse.LearningId で親レコードが先に存在している必要があります
常に Learning.Type = 'LearningCourse' を設定しますLearning は多態型であり、Type でコースとプログラムを区別します
作成前に、既存のコースを必ず照会します重複を防ぎ、競合がある場合にユーザーが判断できるようにします
複数のコンポーネントで構成されるコースについては、確認のために処理を一時停止しますIsLinkedOnly フラグは登録の動作に影響するため、ユーザーが構成を確認する必要があります
次に進む前に、データ品質の不足を報告します組織に確定内容を書き込む前に、ユーザーが不足のないデータを提供できるようにします
データが競合する場合は、ユーザーの判断を待ちますどのソースが正しいかをエージェントが判断することはできません
不足しているデータを捏造しませんプレースホルダー値を使用する場合は、ユーザーの明示的な承認が必要です
親 Learning のみに ProviderId(Business Account)を設定しますLearning では作成可能ですが、LearningCourse では作成できません。手順 9 のフィールド配置に関する注記を参照してください
既定では、すべてのコースのステータスを Active(IsActive = true)に設定しますカタログ検索/登録に表示するには Active である必要があります
組織への呼び出しには CLI コマンドではなく sObject REST API を使用しますH360 MCP スキルには、組織にアクセスするための実行環境がありません。scripts/ ヘルパーはローカルでデータを変換するもの(組織への呼び出しは行いません)です。Bash ツールが利用できない場合は、処理を省略せず、同等の確認をその場で実行してください
特定のツールに依存しない表現を使用しますこのスキルは Claude Code 以外にも、複数の AI サーフェスで利用されます
コースが 200 件を超える場合、またはソースページが 20 件以上ある場合は、場当たり的に分割せず、references/large-catalog-handling.md に従います場当たり的な分割では、ページの分断によるデータ損失や重複作成が発生するおそれがあります

注意事項

既知の失敗要因と解決方法(解析時の問題、Business Account の欠落、選択リストのマッピング、Composite の一部失敗など)については、references/gotchas.md を参照してください。


出力の想定

成果物:

  • 作成された X 件の Learning レコード(Type = 'LearningCourse')
  • 作成された X 件の LearningCourse 子レコード(LearningId 参照)
  • フィールドごとの完全性を示すデータ品質レポート
  • 使用したすべてのデータソースを記載したインポート概要
  • 任意:レコードの直接読み取りまたは SOQL /query によるコースの検証(どちらも headless-360 経由でルーティングできます)

ファイルは作成されません。すべての出力は、sObject REST API 経由で作成された組織のレコードです。


スキル間の連携

必要な作業委任先
個別の Learning Course を手動で作成するPlatform メタデータ生成スキル
Learning Program を作成する専用スキルはまだありません。ユーザーに確認してください
コースのセクション/提供を設定する専用スキルはまだありません。ユーザーに確認してください
登録資格ルールを設定する専用スキルはまだありません。ユーザーに確認してください

参照ファイルの一覧

  • references/gotchas.md — 既知の失敗パターンとその解決方法
  • references/gotchas-detail.md — 発生頻度の低い注意事項。gotchas.md からリンクされています
  • references/large-catalog-handling.md — 200 件を超えるコース、または 20 ページを超えるソースドキュメントのルール:ページ分割読み取り、提供形態が重複するコースの検出、チェックポイント処理、条件付き並列展開(手順 2、6、7)
  • scripts/resolve_api_version.py — headless-360 エンドポイントマップから組織の最大 API バージョンを抽出します(実行トランスポート)
  • scripts/validate_completeness.py — 必須/推奨フィールドの有無をチェックします(手順 3、12)
  • scripts/detect_linked_courses.py — 講義/実習コースのコードの組み合わせを検出します(手順 6)
  • scripts/detect_modality_variants.py — Duration/DurationUnit/Description が異なる CourseCode 行にフラグを付けます。large-catalog-handling.md §2 向けです
  • scripts/batch_courses.py — Composite API のバッチ分割(手順 7)
  • scripts/split_course_code.py — SubjectAbbreviation/CourseNumber の分割(手順 9)

コメント