# Orangeapple Class Report

> 橘子蘋果課後學習表現報告完整流程。從 corp.orangeapple.co 抓取梯次學生名單、修復課堂錄音逐字稿中的語音辨識姓名誤植、從 exam.orangeapple.co 測驗平台抓取各單元成績、最後產出給家長的三明治回饋學習報告，全程使用 Chrome DevTools 與 Obsidian CLI 工具，並將各階段結果存入 Obsidian 筆記。當使用者提到「橘子蘋果課堂報告」、「梯次資訊」、「逐字稿修復」、「課堂測驗成績」、「家長學習報告」時使用此 skill。

- Skill: `mz038197/orangeapple-class-report` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mz038197/orangeapple-class-report`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mz038197/orangeapple-class-report/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- Author: mz038197 (https://skillmd.com/u/mz038197)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mz038197/orangeapple-class-report

---


# 橘子蘋果課後學習表現報告

## 工具需求

- **Chrome DevTools MCP**：抓取網頁資料
- **Obsidian CLI**：讀寫筆記（`obsidian` 指令，需確認 vault 已開啟）
- **Obsidian Vault**：執行 `obsidian vault` 確認當前 vault 路徑

---

## 開始前：向使用者確認五個必填值

**在執行任何階段前，必須先向使用者確認以下五個值，全部缺一不可，不得自行猜測或沿用對話中出現過的舊值：**

| 變數 | 說明 | 用途 |
|---|---|---|
| `{stage_id}` | corp 網站的梯次 ID（URL 末段數字，例如 `6755`） | 階段一抓取梯次資訊的目標 URL |
| `{classroom_id}` | 測驗平台的教室 ID（URL 末段字串，例如 `DA5nmYlmnqKKy5usp06e`） | 階段三抓取測驗成績的目標 URL |
| `{開課日期}` | 這個梯次的**課程開始日期**（MMDD 格式） | 梯次資訊筆記的前綴 |
| `{上課日期}` | **本次上課的日期**（MMDD 格式） | 逐字稿、測驗、學習表現筆記的前綴 |
| `{測驗項目清單}` | 本次要抓取的測驗名稱（可 1 個或多個，使用者指定） | 階段三的篩選與成績抓取範圍 |

> 兩個日期可能相同（第一堂課），也可能不同（例如第二堂課：開課日期是 0322，上課日期是 0329）。
> **`stage_id` 與 `classroom_id` 的取得優先順序**：
> 1. 先嘗試讀取已存在的 `{開課日期}梯次資訊` 筆記，若 frontmatter 中有 `stage_id` / `classroom_id`，直接使用，不需再詢問使用者
> 2. 若筆記不存在或 frontmatter 缺少這兩個值，再向使用者詢問
> 若使用者未提供且筆記中也無記錄，必須停下來詢問，不得自行猜測或沿用對話中出現過的舊值。
> **測驗項目清單必須每次都重新詢問**（不同上課日可能不同），不得沿用上一次課程的項目。

---

## 完整流程（四階段）

```
進度追蹤：
- [ ] 階段一：確認 "{開課日期}梯次資訊" 筆記（已存在則核對欄位；不存在則抓取並新建）
- [ ] 階段二：修復逐字稿，存入 "{上課日期}修復逐字稿" 筆記
- [ ] 階段三：抓取測驗成績，存入 "{上課日期}課堂測驗" 筆記
- [ ] 階段四：彙整學習表現，存入 "{上課日期}學習表現" 筆記
```

---

## 階段一：抓取梯次資訊

**URL 格式**：`https://corp.orangeapple.co/stages/{stage_id}`

### 步驟一：檢查筆記是否已存在

```bash
obsidian read "file={開課日期}梯次資訊"
```

**若筆記已存在** → 確認以下欄位是否齊全（對照必要欄位清單）：
- 若有欄位遺漏，補齊後更新筆記
- 若資料完整，跳過本階段，直接進入階段二

**若筆記不存在** → 進行步驟二，從網頁抓取並建立新筆記

### 步驟二：從網頁抓取資料

使用 Chrome DevTools 導航至 URL，取得快照後提取以下欄位：

| 必要欄位 | 說明 |
|---|---|
| 課程名稱 | 梯次所屬課程 |
| 狀態 | 例如 full、開放中 |
| 說明 | 上課時間、堂數、停課資訊 |
| 課程開始日期 / 結束日期 | |
| 線上教室會議室名稱 | |
| 教室名稱 / 地址 | |
| 佈告欄連結 | |
| 學習教練 | |
| 學生名單 | 姓名、年級 |

### 步驟三：建立筆記

筆記內容除了以上網頁資料外，**必須在最前方加入 ID 記錄區塊**，格式如下：

```markdown
---
stage_id: {stage_id}
classroom_id: {classroom_id}
---
```

> 記錄 stage_id 與 classroom_id 後，下次執行此流程時可直接從筆記讀取，**不需要再向使用者詢問這兩個值**。

存入路徑：vault 根目錄下的 `{開課日期}梯次資訊.md`。

> 學生名單是後續所有階段的**姓名對照基準**，務必完整保留。

---

## 階段二：修復逐字稿 → 學生互動分類

從 Obsidian 讀取原始逐字稿：

```bash
obsidian read "file={上課日期}原始逐字稿"
```

### 姓名修復原則

逐字稿為語音轉文字，姓名幾乎全部誤植。對照梯次資訊的學生名單，依**同音字**規則修復：

| 常見誤植規律 | 說明 |
|---|---|
| 單字同音替換 | 「晨→陳」「鈞→軍」→ 趙晨鈞 變成「陳軍」 |
| 姓省略 / 字序顛倒 | 「辜理紗」→「李莎」（理→李、姓省略） |
| 整體音近 | 「蔡牧燊」→「穆森/木生/穆斯恩」 |

修復時以梯次資訊中的**完整正式姓名**為準。

### 互動紀錄提取規則

- **保留**：老師點名、稱讚、提問、個別指導、師生對話的內容
- **忽略**：純粹講課內容、未涉及特定學生的說明

### 輸出格式（存入 "{上課日期}修復逐字稿"）

```markdown
## {學生姓名}

1. {互動紀錄描述}
2. {互動紀錄描述}
```

若某學生無個別互動，仍建立該學生的區塊並說明（例如：「僅確認出席，無個別互動紀錄」）。

---

## 階段三：抓取測驗成績

**URL 格式**：`https://exam.orangeapple.co/classrooms/{classroom_id}`

### 先決條件（必做）

- 開始階段三前，**一定要先詢問使用者本次要抓取的測驗項目清單**。
- 若使用者尚未明確提供 `{測驗項目清單}`，**不得開始抓取成績**。
- 僅抓取使用者指定的項目，不得自行加抓或省略。

詢問範例：

```text
請提供本次要抓取的測驗項目清單（可多個），例如：
- 【ITS Python 語法基礎教材】Unit1 資料型態
- 【ITS Python 語法基礎教材】Unit2 變數
```

### Chrome DevTools 操作注意事項

測驗平台使用 **Material UI Chip** 作為篩選按鈕，無法用標準 CSS selector 直接點擊。使用以下腳本操作：

```javascript
// 點擊指定測驗篩選 Chip
() => {
  const chips = document.querySelectorAll('.MuiChip-root');
  const target = Array.from(chips).find(c => c.textContent.includes('Unit1 資料型態'));
  if (target) { target.click(); return 'clicked: ' + target.textContent.trim(); }
  return 'not found';
}
```

### 提取成績資料

每次切換 Unit 後，用以下腳本從 `[role="alert"]` 元素提取學生成績：

```javascript
() => {
  const alerts = document.querySelectorAll('[role="alert"]');
  const results = [];
  for (const alert of alerts) {
    const text = alert.textContent;
    if (text.includes('作答者') && text.includes('成績')) {
      const studentMatch = text.match(/作答者：(.+?)成績/);
      const scoreMatch = text.match(/成績：(\d+)/);
      if (studentMatch && scoreMatch) {
        results.push({ student: studentMatch[1].trim(), score: parseInt(scoreMatch[1]) });
      }
    }
  }
  return results;
}
```

依 `{測驗項目清單}` 對每個指定項目執行，收集完所有資料後，依學生分類整理成績表格，存入 `{上課日期}課堂測驗` 筆記。

---

## 階段四：彙整家長學習表現報告

同時讀取修復逐字稿與課堂測驗兩份筆記：

```bash
obsidian read "file={上課日期}修復逐字稿"
obsidian read "file={上課日期}課堂測驗"
```

### 三明治回饋法結構

每位學生的報告依序包含：

1. **測驗成績表格**（必須放在最前面）
2. **正面肯定**：具體描述課堂中表現好的地方（成績、行為、態度）
3. **改進建議**：點出分數偏低的單元，說明該單元的核心觀念是什麼，以及如何補強
4. **鼓勵收尾**：依據整體趨勢給出有根據的期許，不誇飾、不悲觀

### 撰寫原則

- 口吻自然，像人在寫，不使用制式的「非常傑出」「令人印象深刻」等套語
- 若低分有客觀原因（如操作失誤、麥克風問題），在報告中如實說明，避免家長誤解
- 若某學生無課堂互動紀錄，報告內容以成績趨勢為主，不憑空捏造互動
- 建議內容要具體（說明是哪個概念需要補強，而不只是「需要加強」）

### 成績趨勢判斷

| 趨勢 | 描述方式 |
|---|---|
| 逐步上升 | 說明在課堂中持續吸收，越學越上手 |
| 起伏不定 | 指出哪個單元相對弱，建議針對該單元複習 |
| 整體偏低 | 說明第一堂課內容量大，消化需要時間，鼓勵課後補練 |

---

## 筆記命名規範

| 筆記 | 日期變數 | 命名格式 | 範例（第二堂課） |
|---|---|---|---|
| 梯次資訊 | `{開課日期}` | `{開課日期}梯次資訊` | `0322梯次資訊` |
| 原始逐字稿 | `{上課日期}` | `{上課日期}原始逐字稿` | `0329原始逐字稿` |
| 修復逐字稿 | `{上課日期}` | `{上課日期}修復逐字稿` | `0329修復逐字稿` |
| 課堂測驗 | `{上課日期}` | `{上課日期}課堂測驗` | `0329課堂測驗` |
| 學習表現 | `{上課日期}` | `{上課日期}學習表現` | `0329學習表現` |

> **第一堂課**：`{開課日期}` 與 `{上課日期}` 相同，例如都是 `0322`。
> **後續堂課**：梯次資訊筆記不重新建立（已存在），只需建立當天的逐字稿、測驗、學習表現三份筆記，並從現有的 `{開課日期}梯次資訊` 讀取學生名單。

---

## 寫入 Obsidian 的方式

由於筆記內容通常包含 Markdown 表格（含 `|` 符號），**不建議用 `obsidian create content=` 直接傳遞內容**（特殊字元易出錯）。

正確做法：
1. 直接用 `Write` 工具將 `.md` 檔案寫入 vault 路徑
2. 再用 `obsidian open "file={筆記名稱}" newtab` 開啟確認

Vault 路徑查詢：
```bash
obsidian vault info=path
```

