Skip to content

Commit 3f85d53

Browse files
committed
Drive schedule from Feishu calendar
1 parent 19e2fb5 commit 3f85d53

14 files changed

Lines changed: 811 additions & 309 deletions

File tree

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ npm run dev
1414
飞书是动态课程内容的来源:
1515

1616
- Wiki 集合负责整棵讲义目录,Session 映射可继续覆盖单节讲义正文。
17-
- 课程共享日历负责标题、日期、时间、地点和取消状态。
17+
- 课程共享日历中的全部事件直接生成首页和 `/schedule` 活动日历,负责标题、
18+
说明、日期、时间、地点和状态。
1819
- `docs/.vitepress/data/program.ts` 保留 Topic、讲者、提纲等网站策划字段。
1920
- 腾讯会议链接默认不发布,只有将
2021
`content/feishu/sessions.json` 中的 `publishMeetingUrl` 显式设为
@@ -78,9 +79,14 @@ API Scope 之外,应用还必须拥有目标知识空间的资源权限。进
7879
`sessions` 列表用于为单节课程配置:
7980

8081
- `wikiNodeToken`:Wiki URL 中 `/wiki/` 后的 token。
81-
- `calendarEventId`:共享日历中对应日程的 ID。
82+
- `calendarEventId`:可选。填写后会把共享日历中的该事件链接到对应讲义页;
83+
不填写也不影响事件出现在活动日历中。
8284
- `pageTitle``description`:日历尚未配置时使用的页面元数据。
8385

86+
共享日历时间范围内的所有事件都会写入生成快照,并按日期与时间排序。定时
87+
事件、全天事件、描述、地点、待确认/已确认/已取消状态都会自动反映到网站;
88+
腾讯会议链接仍受 `publishMeetingUrl` 开关保护。
89+
8490
先查看应用可见的日历、日程 ID 和 Wiki 版本:
8591

8692
```bash

docs/.vitepress/config.mts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ const sessionSidebar = topics.map((topic) => ({
2222
text: `Topic ${topic.number} · ${topic.title}`,
2323
collapsed: topic.number !== '01',
2424
items: sessions
25-
.filter((session) => session.topic === topic.key)
25+
.filter((session) => session.topic === topic.key && session.href)
2626
.map((session) => ({
2727
text: `${session.id} · ${session.title}`,
28-
link: session.href || `/schedule#session-${session.id}`
28+
link: session.href!
2929
}))
30-
}))
30+
})).filter(({ items }) => items.length > 0)
3131
const wikiSidebarItems = (parentWikiNodeToken: string | null): any[] =>
3232
(wiki?.pages ?? [])
3333
.filter((page) => page.parentWikiNodeToken === parentWikiNodeToken)
@@ -83,7 +83,7 @@ export default defineConfig({
8383
siteTitle: 'AI Infra Seminars',
8484
nav: [
8585
{ text: '课程介绍', link: '/' },
86-
{ text: '课程日程', link: '/schedule' },
86+
{ text: '活动日历', link: '/schedule' },
8787
...(wiki ? [{ text: wiki.title, link: '/wiki/' }] : []),
8888
...(firstPublishedSession
8989
? [
@@ -103,7 +103,7 @@ export default defineConfig({
103103
text: session.title,
104104
link: session.href!
105105
})),
106-
{ text: '完整课程日程', link: '/schedule#full-schedule' }
106+
{ text: '完整活动日历', link: '/schedule#full-schedule' }
107107
]
108108
}
109109
],

docs/.vitepress/data/generated/feishu.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,42 @@
1111
}
1212
}
1313
},
14+
"calendar": {
15+
"timezone": "Asia/Shanghai",
16+
"range": {
17+
"start": "2026-07-01",
18+
"end": "2026-10-01"
19+
},
20+
"events": [
21+
{
22+
"eventId": "3b4d3e17-ee6c-43c0-a088-229be6043c12_0",
23+
"summary": "测试日程1",
24+
"date": "2026-07-24",
25+
"endDate": "2026-07-24",
26+
"startAt": "2026-07-23T23:00:00.000Z",
27+
"endAt": "2026-07-24T03:15:00.000Z",
28+
"allDay": false,
29+
"timeLabel": "07:00–11:15",
30+
"timezone": "Asia/Shanghai",
31+
"status": "tentative",
32+
"sourceUrl": "https://applink.feishu.cn/client/calendar/event/detail?calendarId=7666077798931500213&key=3b4d3e17-ee6c-43c0-a088-229be6043c12&originalTime=0&startTime=1784847600"
33+
},
34+
{
35+
"eventId": "c74ecf79-0ac2-489d-8417-086eea9a947a_0",
36+
"summary": "测试日程2",
37+
"description": "内容测试内容测试内容测试内容测试内容测试内容测试内容测试内容测试内容测试内容测试内容测试内容测试内容测试内容测试内容测试",
38+
"date": "2026-07-25",
39+
"endDate": "2026-07-26",
40+
"startAt": "2026-07-25",
41+
"endAt": "2026-07-26",
42+
"allDay": true,
43+
"timeLabel": "全天",
44+
"timezone": "Asia/Shanghai",
45+
"status": "tentative",
46+
"sourceUrl": "https://applink.feishu.cn/client/calendar/event/detail?calendarId=7666077798931500213&key=c74ecf79-0ac2-489d-8417-086eea9a947a&originalTime=0&startTime=1784937600"
47+
}
48+
]
49+
},
1450
"wiki": {
1551
"title": "AI Infra Wiki",
1652
"rootNodeToken": "SURAw8B4riKkhRkUKPYc43k7nHf",

docs/.vitepress/data/program.ts

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import feishuSnapshot from './generated/feishu.json'
22

33
export type TopicKey = 'kernel' | 'comm' | 'serving' | 'rl'
4+
export type CalendarStatus = 'tentative' | 'confirmed' | 'cancelled'
45

56
export interface Topic {
67
key: TopicKey
@@ -28,7 +29,27 @@ export interface Session {
2829
timeLabel?: string
2930
location?: string
3031
meetingUrl?: string
31-
calendarStatus?: 'tentative' | 'confirmed' | 'cancelled'
32+
calendarStatus?: CalendarStatus
33+
}
34+
35+
export interface CalendarEvent {
36+
eventId: string
37+
summary: string
38+
description?: string
39+
date: string
40+
endDate: string
41+
dateLabel: string
42+
startAt: string
43+
endAt: string
44+
allDay: boolean
45+
timeLabel: string
46+
timezone: string
47+
status: CalendarStatus
48+
location?: string
49+
sourceUrl?: string
50+
meetingUrl?: string
51+
sessionId?: string
52+
href?: string
3253
}
3354

3455
export const topics: Topic[] = [
@@ -286,21 +307,78 @@ const staticSessions: Session[] = [
286307

287308
interface SyncedSession {
288309
calendar?: {
310+
eventId?: string
289311
summary?: string
290312
date?: string
291313
startAt?: string
292314
endAt?: string
293315
timeLabel?: string
294316
location?: string
295317
meetingUrl?: string
296-
status?: 'tentative' | 'confirmed' | 'cancelled'
318+
status?: CalendarStatus
297319
}
298320
document?: {
299321
wikiNodeToken: string
300322
}
301323
}
302324

303-
const syncedSessions = feishuSnapshot.sessions as Record<string, SyncedSession>
325+
interface SyncedCalendarEvent {
326+
eventId: string
327+
summary: string
328+
description?: string
329+
date: string
330+
endDate: string
331+
startAt: string
332+
endAt: string
333+
allDay: boolean
334+
timeLabel: string
335+
timezone: string
336+
status: CalendarStatus
337+
location?: string
338+
sourceUrl?: string
339+
meetingUrl?: string
340+
}
341+
342+
interface SyncedCalendar {
343+
timezone: string
344+
range?: {
345+
start: string
346+
end: string
347+
}
348+
events: SyncedCalendarEvent[]
349+
}
350+
351+
interface FeishuSnapshot {
352+
sessions: Record<string, SyncedSession>
353+
calendar?: SyncedCalendar
354+
}
355+
356+
const snapshot = feishuSnapshot as FeishuSnapshot
357+
const syncedSessions = snapshot.sessions
358+
359+
export const calendarTimezone =
360+
snapshot.calendar?.timezone || 'Asia/Shanghai'
361+
export const calendarRange = snapshot.calendar?.range
362+
363+
export const calendarEvents: CalendarEvent[] = (
364+
snapshot.calendar?.events ?? []
365+
).map((event) => {
366+
const linkedSession = Object.entries(syncedSessions).find(
367+
([, session]) => session.calendar?.eventId === event.eventId
368+
)?.[0]
369+
370+
return {
371+
...event,
372+
summary: event.summary || '未命名活动',
373+
dateLabel: event.date.slice(5).replace('-', '.'),
374+
...(linkedSession
375+
? {
376+
sessionId: linkedSession,
377+
href: `/sessions/${linkedSession}`
378+
}
379+
: {})
380+
}
381+
})
304382

305383
export const sessions: Session[] = staticSessions.map((session) => {
306384
const synced = syncedSessions[session.id]

docs/.vitepress/theme/components/HomePage.vue

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import ScheduleTable from './ScheduleTable.vue'
1515
从 GPU 编程、分布式通信到模型推理与强化学习系统,一起理解一次 API 调用背后的计算。
1616
</p>
1717
<div class="hero-actions">
18-
<a href="#schedule">查看课程日程 <span>↓</span></a>
18+
<a href="#schedule">查看活动日历 <span>↓</span></a>
1919
<a :href="withBase('/sessions/01')">阅读 Session 01 <span>→</span></a>
2020
</div>
2121

@@ -118,13 +118,16 @@ import ScheduleTable from './ScheduleTable.vue'
118118
<div class="section-heading section-heading-row">
119119
<div>
120120
<span>SCHEDULE</span>
121-
<h2>课程日程</h2>
121+
<h2>活动日历</h2>
122122
</div>
123123
<p>
124-
日程与讲者可能根据准备情况微调。会议入口、作业与评测平台随各 Session 发布
124+
以下内容直接来自 AI Infra 共享飞书日历,时间、地点、说明与活动状态会自动更新
125125
</p>
126126
</div>
127-
<ScheduleTable />
127+
<ScheduleTable compact :limit="4" />
128+
<p class="schedule-more-link">
129+
<a :href="withBase('/schedule')">查看完整活动日历 →</a>
130+
</p>
128131
</section>
129132

130133
<section class="join-section" id="registration">
Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,55 @@
11
<script setup lang="ts">
22
import { withBase } from 'vitepress'
3+
import {
4+
calendarEvents,
5+
calendarTimezone
6+
} from '../../data/program'
37
import ScheduleTable from './ScheduleTable.vue'
8+
9+
function shortDate(value: string) {
10+
return value.slice(5).replace('-', '.')
11+
}
12+
13+
const eventRange =
14+
calendarEvents.length > 0
15+
? `${shortDate(calendarEvents[0].date)} — ${shortDate(
16+
calendarEvents.at(-1)!.date
17+
)}`
18+
: '等待日程'
19+
const calendarTimezoneLabel =
20+
{
21+
'Asia/Shanghai': '北京时间',
22+
'Asia/Singapore': '新加坡时间'
23+
}[calendarTimezone] || calendarTimezone
424
</script>
525

626
<template>
727
<main class="course-site schedule-page-shell">
8-
<header class="inner-page-hero">
28+
<header class="inner-page-hero calendar-page-hero">
929
<span>AI INFRA SEMINARS</span>
10-
<h1>完整课程日程</h1>
30+
<h1>活动日历</h1>
1131
<p>
12-
2026 年 7 月 23 日至 9 月 6 日,共七周、四个 Topic、十四场
13-
Session。日期、时间和地点由课程共享飞书日历自动同步
32+
本页直接同步 AI Infra 共享飞书日历。新建活动、修改时间与地点、
33+
补充说明或取消日程后,网站会自动更新
1434
</p>
35+
<dl class="calendar-sync-summary">
36+
<div>
37+
<dt>已同步</dt>
38+
<dd>{{ calendarEvents.length }} 项</dd>
39+
</div>
40+
<div>
41+
<dt>当前范围</dt>
42+
<dd>{{ eventRange }}</dd>
43+
</div>
44+
<div>
45+
<dt>显示时区</dt>
46+
<dd>{{ calendarTimezoneLabel }}</dd>
47+
</div>
48+
</dl>
1549
<a :href="withBase('/')">← 返回课程首页</a>
1650
</header>
17-
<ScheduleTable id="full-schedule" />
51+
<section id="full-schedule">
52+
<ScheduleTable />
53+
</section>
1854
</main>
1955
</template>

0 commit comments

Comments
 (0)