小爱课程表课程导入开发实践

使用人数

MIUI 的小爱同学内置小爱课程表,可以从学校的教务系统自动导入课程信息,非常实用。可惜我们学校没有适配,索性我就自己写了一下,顺便练练手。

代码已经上传到 Github 了,只有三个文件,开发者文档有详细说明,这里就贴一下代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function scheduleHtmlProvider(
iframeContent = "",
frameContent = "",
dom = document
) {
await loadTool("AIScheduleTools");
// 使用它们的时候务必带上await,否则没有系统alert的时停效果
await AIScheduleAlert(
"开始导入……\n完成后请手动修改开学时间\n如导入出错或有其他建议请联系QQ:390602272"
);
let iframe = dom.getElementsByTagName("iframe")[0].contentDocument;
let table = iframe.getElementsByClassName("xfyq_area mt10")[0].outerHTML;
// console.info(table)
return table;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
function scheduleHtmlParser(html) {
/**
* 提取课程周数
* @param {string} str 包含周数的字符串,如`[1-9,11]`、`12,14`
* @returns {Array<number>} 包含`str`中所有周数的数组
*/
function getWeeks(str) {
let weeks = [];
str = str.split("[")[1].split("]")[0];
let weekArray = str.split(",");
for (let i in weekArray) {
const begin = weekArray[i].split("-")[0];
let end = weekArray[i].split("-")[1];
if (!end) {
end = begin;
}
for (let j = parseInt(begin); j <= parseInt(end); j++) {
weeks.push(j);
}
}
return weeks;
}
/**
* 提取课程节数
* @param {string} str 包含节数的字符串,如`第8,9节`
* @returns {Array<number>} 包含`str`中所有节数的数组
*/
function getSections(str) {
let sections = [];
str = /第(.+?)节/g.exec(str)[0].replace("第", "").replace("节", "");
let sectionArray = str.split(",");
for (let i in sectionArray) {
sections.push(parseInt(sectionArray[i].replace(/[^0-9]/gi, "")));
}
return sections;
}
/**
* 获取单节课程信息
* @param {string} lessonName 课程名
* @param {number} day 星期
* @param {string} info 课程信息字符串
* @returns {object} 课程信息
*/
function getLesson(lessonName, day, info) {
let lesson = { sections: getSections(info), weeks: getWeeks(info) };
lesson.name = lessonName;
lesson.day = day;
lesson.teacher = info.split("[")[0].replace(/\s+/g, ""); //去掉空格,多教师只提取第一个

//提取教室
tmp = info.split("周");
lesson.position = tmp[tmp.length - 1].split("\n")[0];

return lesson;
}
/**
* 获取多节课程信息(同一课程被拆分为多节课)
* @param {string} lessonName 课程名
* @param {number} day 星期
* @param {string} info1 课程信息(不含教室,第一行)
* @param {string} info2 教室(第二行)
* @returns
*/
function getLessons1(lessonName, day, info1, info2) {
let lessons = [];
let position = info2.split("\n")[0];
let sections = getSections(info2);
let allWeeks = info1.split("周,");
for (i in allWeeks) {
let lesson = { sections: sections, weeks: getWeeks(allWeeks[i]) };
lesson.name = lessonName;
lesson.day = day;
lesson.teacher = allWeeks[i].split("[")[0].replace(/\s+/g, ""); //去掉空格
lesson.position = position;
lessons.push(lesson);
}
return lessons;
}
/**
* 获取多节课程信息(同一老师不同教室, 如`张老师[10-13]周机房 第1,2节,[2-9]周(五)111 第1,2节`)
* @param {string} lessonName 课程名
* @param {number} day 星期
* @param {string} info 课程信息字符串
* @returns {Array<object>} 课程信息
*/
function getLessons2(lessonName, day, info) {
let lessons = [];
let teacher = info.split("[")[0].replace(/\s+/g, ""); //去掉空格
let weekArray = info.split("\n");
let n = weekArray.length;
for (let i = 0; i < n - 1; i++) {
let lesson = {
sections: getSections(weekArray[i + 1]),
weeks: getWeeks(weekArray[i]),
};
lesson.name = lessonName;
lesson.day = day;
lesson.teacher = teacher;
lesson.position = weekArray[i].split("周")[1];
lessons.push(lesson);
}
return lessons;
}

/** 课程信息 */
let courseInfos = [];
const lines = $(".addlist_01").find("tr"); // 表的所有行
for (let l = 1; l <= 6; l++) {
// 1-6行为课程
const linelessons = $(lines[l]).find("td");
for (let day = 1; day <= 7; day++) {
// 1-7列为每一天
const index = day + 1; // 列标为天数+1
let child = linelessons[index].children[0];
if (child.data !== "&nbsp;") {
//时间段不为空
while (child) {
//一个时间段可能有多个课程
lessonName = child.data.slice(0, 49); //课程名长度不能超过50字符
// lessonName = child.data;
child = child.next.children[0];
if (child.data.slice(-1) === "节") {
if (child.data.indexOf(",[") === -1) {
//同一教室不同老师
courseInfos.push(getLesson(lessonName, day, child.data));
} else {
//同一老师不同教室
lessons = getLessons2(lessonName, day, child.data);
for (let i in lessons) {
courseInfos.push(lessons[i]);
}
}
//一个时间段可能有多个课程
child = child.next;
} else {
//同一课程被拆分为多节课(两行)
lessons = getLessons1(
lessonName,
day,
child.data,
child.next.children[0].data
);
for (let i in lessons) {
courseInfos.push(lessons[i]);
}

//一个时间段可能有多个课程
child = child.next.children[0].next;
}
if (child) {
child = child.children[0];
}
}
}
}
}

return courseInfos;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/**
* 时间配置函数,此为入口函数,不要改动函数名
*/
async function scheduleTimer({ providerRes, parserRes } = {}) {
//周数,包括两周考试周,春18秋19夏9
let totalWeek = 19;
if (
providerRes.indexOf("春季学期") >= 0 &&
providerRes.indexOf("春季学期") < 150
) {
totalWeek = 18;
} else if (
providerRes.indexOf("秋季学期") >= 0 &&
providerRes.indexOf("秋季学期") < 150
) {
totalWeek = 19;
} else {
totalWeek = 9;
}
//课程时间
const sections = [
{
section: 1,
startTime: "08:00",
endTime: "08:45",
},
{
section: 2,
startTime: "08:50",
endTime: "09:35",
},
{
section: 3,
startTime: "09:50",
endTime: "10:35",
},
{
section: 4,
startTime: "10:40",
endTime: "11:25",
},
{
section: 5,
startTime: "11:30",
endTime: "12:15",
},
{
section: 6,
startTime: "14:00",
endTime: "14:45",
},
{
section: 7,
startTime: "14:50",
endTime: "15:35",
},
{
section: 8,
startTime: "15:50",
endTime: "16:35",
},
{
section: 9,
startTime: "16:40",
endTime: "17:25",
},
{
section: 10,
startTime: "17:30",
endTime: "18:15",
},
{
section: 11,
startTime: "19:00",
endTime: "19:45",
},
{
section: 12,
startTime: "19:50",
endTime: "20:35",
},
{
section: 13,
startTime: "20:40",
endTime: "21:25",
},
{
section: 14,
startTime: "21:30",
endTime: "22:15",
},
];
return {
totalWeek: totalWeek, // 总周数:[1, 30]之间的整数
startSemester: "", // 开学时间:时间戳,13位长度字符串,推荐用代码生成
startWithSunday: false, // 是否是周日为起始日,该选项为true时,会开启显示周末选项
showWeekend: true, // 是否显示周末
forenoon: 5, // 上午课程节数:[1, 10]之间的整数
afternoon: 5, // 下午课程节数:[0, 10]之间的整数
night: 4, // 晚间课程节数:[0, 10]之间的整数
sections: sections, // 课程时间表,注意:总长度要和上边配置的节数加和对齐
};
}