课表助手

本文首发于我的个人博客小小柴io

简要介绍

作者是一名学生,需要在手机上记录课表,虽然现在已经有很多的课程表App了,但是有些呢不支持从教务系统导入,有些呢会有很多广告,再有些就是界面很丑啦~

因此,为了能够便捷的查看课表,免去现有的课程表软件带来的烦恼,我决定!开发一款无害的课表软件。于是,这款“课表助手”就应运而生(虽然不是一个App,后面再详细解释),它的作用不是显示课表,而是将教务系统中的课表转为iCalendar文件。iCalendar是一种日历数据交换的标准,既然是标准了,那大家肯定都支持啦,目前已经测试了Win10/Android9/iPadOS13,都可以导入至日历。导入后就可以愉快的使用系统日历查看课表了,不得不说,系统自带的日历界面清新、使用流畅、没有广告(用过的都说好🙄)。

开发过程

准备工作

为了明确我要做什么,首先打开了教务系统课程表页面,他是这个样子的:
在这里插入图片描述
看了一下,嗯!挺好的,适合序列化。然后再看一下iCalendar文件的样子:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:Curriculum-to-iCalendar
BEGIN:VEVENT
DTSTAMP:20200313T032000Z
UID:0@https://chaiqingao.github.io/
SUMMARY:summary
DTSTART:20200217T000000Z
DTEND:20200217T013500Z
RRULE:FREQ=WEEKLY;UNTIL=20200428T013500Z;INTERVAL=1
LOCATION:location
DESCRIPTION:description
END:VEVENT
BEGIN:VEVENT
......
END:VEVENT
END:VCALENDAR

不难看出,一个iCalendar文件由三部分组成:头 + [VENVENT数组] + 尾。每一个VENVENTDTSTAMP创建的时间、UID标识符、SUMMARY事件名、DTSTART事件开始时间、DTEND事件结束时间、RRULE重复方式(包括FREQ频率、UNTIL重复截至时间、INTERVAL间隔)、LOCATION地点、DESCRIPTION详细信息。掌握了这些东西,下面就开始吧!

定位至教务系统中的课程表

查看网页源码,可以发现,教务系统的课程表嵌套在两层iframe(top->page_iframe->iframe0)中,经过一番努力,定位到了!

var page_iframe = document.getElementById("page_iframe");
var iframe0 = page_iframe.contentDocument.getElementById("iframe0");
var table = iframe0.contentDocument.getElementsByTagName("table")[0];

下面就可以对表格里的内容为所欲为进行操作了。

获取表格中课程名和上课时间非空的行

第一要事!确保这个课是存在的,这个简单一点:

for (let i = 1; i < table.rows.length; i++) {
    var courseName = table.rows[i].cells[1].innerText;
    var teacherName = table.rows[i].cells[5].innerText;
    var timeAddress = table.rows[i].cells[9].innerText;
    if (courseName !== "" && timeAddress !== "") {
        ...
    }
}

构建VEVENT

courseName就作为SUMMARY吧,下面是对timeAddress的解析,从中提取出日期时间地址

var toString = function (date) {
    return date.toISOString().split(/-|:|[.]/).slice(0, 4).join("") + "00Z";
}
var events = timeAddress.split(' ').filter(n => n != "");
for (let j = 0; j < events.length; j++) {
    //周一:1-11周,每1周;1-2节,3区,附3-401 ==> [一,1-11,1,1-2,3区,附3-401]
    var informations = events[j].split(/周,每|周|节,|:|,|;/).filter(n => n != "");
    var description = "第" + events[j].split(";")[1] + " " + teacherName;
    var weekDay = weekToNum[informations[0]];
    var startWeek = parseInt(informations[1].split("-")[0]);
    var endWeek = parseInt(informations[1].split("-")[1]);
    var interval = parseInt(informations[2]);
    var startTime = class_start[parseInt(informations[3].split('-')[0])];
    var endTime = class_start[parseInt(informations[3].split('-')[1])];
    var address = [informations[4], informations[5], teacherName].join(" ");
    var startDate = new Date(),endDate=new Date(),untilDate=new Date();
    startDate.setDate(startDate.getDate() - startDate.getDay() - (currentWeek - startWeek) * 7 + weekDay);
    startDate.setHours(startTime[0], startTime[1], 0, 0);
    endDate.setDate(endDate.getDate() - endDate.getDay() - (currentWeek - startWeek) * 7 + weekDay);
    endDate.setHours(endTime[0],endTime[1]+class_time);
    untilDate.setDate(untilDate.getDate() - untilDate.getDay() + (endWeek - currentWeek) * 7 + weekDay + 1);
    untilDate.setHours(endTime[0],endTime[1]+class_time);
    calendarEvents.push([
        'BEGIN:VEVENT',
        'DTSTAMP:' + toString(new Date()),
        'UID:' + calendarEvents.length + '@' + 'https://chaiqingao.github.io/',
        'SUMMARY:' + courseName,
        'DTSTART:' + toString(startDate),
        'DTEND:' + toString(endDate),
        'RRULE:FREQ=WEEKLY;UNTIL=' + toString(untilDate) + ';INTERVAL=' + interval,
        'LOCATION:' + address,
        'DESCRIPTION:' + description,
        'END:VEVENT'
    ].join(SEPARATOR));
}

这里的calendarEvents是一个事件数组,里面存储了所有的课程,加上头尾就可以保存了😁

保存文件

这里使用了FileSaver.js用于保存文件

var fileName = year + "学年第" + term + "学期.ics";
var calendar = calendar_start + SEPARATOR + calendarEvents.join(SEPARATOR) + calendar_end;
var blob;
if (navigator.userAgent.indexOf('MSIE 10') === -1) { // chrome or firefox
    blob = new Blob([calendar]);
} else { // ie
    var bb = new BlobBuilder();
    bb.append(calendar);
    blob = bb.getBlob('text/x-vCalendar;charset=' + document.characterSet);
}

结果展示

这里将ics文件导入至登录了Outlook邮箱的Win10日历,还是很不错的嘛😜~
https://s1.ax1x.com/2020/03/13/8uT8Nn.png再打开iPadOS日历看看,已经同步了👍~(前提是登录相同的邮箱)
在这里插入图片描述
就剩Android咯,打开一看发现并没有同步😥,又去搜了一些教程,并没有什么很好的解决方法,只好把ics文件发送至手机然后手动打开了。
在这里插入图片描述

脚本下载地址

将以上内容做成了油猴助手脚本并上传至了greasyfork,关于油猴助手请点这里,下载脚本请点这里

💫脚本特点:一键保存ics文件,方便快捷

Github项目地址

千山万水总是情,给个Star行不行 Curriculum-to-iCalendar

目前支持的教务系统

  • WHU

总结

第一次写这种油猴助手的脚本,也算是练习了Javascript的应用吧,过程磕磕绊绊的,从有这个想法到实现经历了两天时间,找朋友测试了一下,发现了很多🐛bug…修改再测试,测试再修改,到现在还算是挺完善的,也欢迎大家在Github提Issue。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值