小爱课程表课程导入开发实践
MIUI 的小爱同学内置小爱课程表,可以从学校的教务系统自动导入课程信息,非常实用。可惜我们学校没有适配,索性我就自己写了一下,顺便练练手。
代码已经上传到 Github 了,只有三个文件,开发者文档有详细说明,这里就贴一下代码。
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;
}
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 !== " ") {
//时间段不为空
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;
}
/**
* 时间配置函数,此为入口函数,不要改动函数名
*/
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, // 课程时间表,注意:总长度要和上边配置的节数加和对齐
};
}