时光

时光的时光轴

寄以时间,予以文字
telegram
github
微信公众号

让课程表通过日历更新

81696c97-741c-466d-8810-9276bdaa993b.JPG

你手中的课程表是不是也是这样?所有课程叠在一起,每次上课前要算周数、找教师、算时间。也许过一段时间你会记下来这一切,但很快一个学期过去了,你又要重新开始这一切。

我在入学之前也了解过这个问题,大抵搜了几款网上推荐的,应用商店排名高的 “课程表” App,说实话,它们更像是大学生交友平台

有一个小爱课程表倒是挺符合我的感受的,而且接受个人开发者做适配,不过我尝试申请资格申请了两回也没消息,交流群里面也是三天两头的报 BUG,而且我在简单的体验过程中也发现了不少问题...


不过此时我将目光投向了自带的日历App,想起许久之前 Apple 自带日历还不支持中国节假日的时候(烦恼),曾在网络上有许多通过订阅日历的方法来解决这个问题。这种方法不仅能够通过服务端分发日程,并且还能够定期更新,最重要是的创建出来的日程完全跟普通创建的日程提醒功能一样,这让我对怎么实现的非常好奇。

协议

首先我需要搞清楚别人是怎么实现的。

在访问订阅链接时,会下载下来一个.ics文件,咕噜咕噜一番,了解到这个属于iCalendar协议,这个i开头的单词让我一下想到了苹果,仔细一看果然有不少关系。然后分发协议是一个名为WebCal的标准,订阅日程便是基于以上两项技术实现的。

你可以在iCalendar找到格式的相关信息。

iCalendar 格式

我根据自身水平,最大努力将格式的关键部分表述出来,但仍然可能存在错误,烦请指正。

我使用 Apple 日历导出来一份.ics文件,以便我理解它的格式:

BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:飞行计划
X-APPLE-CALENDAR-COLOR:#63DA38
BEGIN:VEVENT
DESCRIPTION:起飞降落时间为航班计划时间,仅供参考。
DTEND;TZID=Asia/Shanghai:20200901T170000
DTSTART;TZID=Asia/Shanghai:20200901T150500
SUMMARY:乘坐HO1072 长沙黄花-上海浦东 当地时间15:05-17:00[航旅纵横]
URL;VALUE=URI:umetrip://OPENAPP
END:VEVENT
END:VCALENDAR

其中有部分节点,比如动态时区我去掉了,经过测试这并不影响实际出来的日程,并且作为面向东八区的教程,让我看着很头大。

该文件由 “BEGIN” 和 “END” 两个头包括在一起,详细的文件格式你可以去其官网上找到更多资料。

  • X-WR-CALNAME 定义该日历的名称
  • X-APPLE-CALENDAR-COLOR 这个是 Apple 日历定义图标颜色的

然后是 “BEGIN” 和 “END” 段,这将定义一次事件

  • CREATED 创建日期
  • DESCRIPTION 备注 / 描述
  • SUMMARY 标题
  • DTEND;TZID 结束日期,其指定了时区
  • DTSTART;TZID 开始日期,其指定了时区
  • URL;VALUE 事件的 url 地址,可以点开

你可以使用 Outlook 网页版快捷测试自己的日历。
所以我照葫芦画瓢,写了一个:

BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:课程表
X-APPLE-CALENDAR-COLOR:#63DA38
BEGIN:VEVENT
DESCRIPTION:教师:曹华山\n学分:5.0
DTSTART;TZID=Asia/Shanghai:20201003T160000
DTEND;TZID=Asia/Shanghai:20201003T193000
LOCATION:教学楼C404
SUMMARY:网页设计综合实训
URL;VALUE=URI:hello
END:VEVENT
END:VCALENDAR

将其保存到文本文件,并使用 ics 后缀名,即可尝试导入到 Outlook,你便可以看到添加后的效果。

a355fdfd-e4f8-49fa-a8d9-a544cb91c5c7.png

在调试过程中发现,各家的日历都有一些专有参数,比如 Apple 日历:X-APPLE-CALENDAR-COLOR便可以定义日程的显示颜色。

在后面的研究中应该多试试几款日历 App,研究研究有哪些专有参数,让课程表更具特色一些。那么接下来就是转换课程数据了~

数据转换

首先应当让你学校教务系统提供的课程表变成结构化数据,因为不同学校不一样,这里就用我学校的教务网站做例子。

不过我们学校就很离谱,查询课程表返回的是一张图片...

cf63f138-6f9f-4d78-bf48-246853433bc1.png

这是怕有人偷走你的课程表吗...

不过好在天无绝人之路,如果按课程教室来查询课程表,返回的是可处理的文字(不知道为什么要这么设计)

那么我便从这里试着入手:

按课程查询

简单看一下查询课程的逻辑,便可以得到课程列表。不过教务系统查询的时候竟然有验证码:

fbfd0c90-ada2-4fc1-82b8-155f5a413dc6.png

但是,这个验证码实在是不复杂,便用简单的 OCR 解决掉了。经过一番折腾,整理出来了这样的数据:

	array(2) {
  [0]=>
  array(6) {
    ["name"]=>
    string(25) "130601|客户关系管理"
    ["teacher"]=>
    string(6) "韩梅"
    ["type"]=>
    string(19) "专业课/必修课"
    ["week"]=>
    string(4) "1-18"
    ["day"]=>
    string(11) "二[1-2节]"
    ["location"]=>
    string(13) "教学楼A401"
  }
  [1]=>
  array(6) {
    ["name"]=>
    string(28) "130625|商务谈判与礼仪"
    ["teacher"]=>
    string(9) "王晓霞"
    ["type"]=>
    string(19) "专业课/必修课"
    ["week"]=>
    string(4) "1-18"
    ["day"]=>
    string(11) "三[3-4节]"
    ["location"]=>
    string(13) "教学楼C502"
  }
}

然后根据教务系统返回的校历,作息时间,对整个课程数据进行处理,接下来就是生成ics文件了。

生成 ICS 文件

按照目标格式拼凑数据,差不多就是之前这段:

BEGIN:VCALENDAR
VERSION:2.0
X-WR-CALNAME:课程表
X-APPLE-CALENDAR-COLOR:#63DA38
BEGIN:VEVENT
DESCRIPTION:教师:曹华山\n学分:5.0
DTSTART;TZID=Asia/Shanghai:20201003T160000
DTEND;TZID=Asia/Shanghai:20201003T193000
LOCATION:教学楼C404
SUMMARY:网页设计综合实训
URL;VALUE=URI:hello
END:VEVENT
END:VCALENDAR

然后将文件导入到 outlook,便可以看到效果:

进行订阅

要实现WebCal协议其实很简单,本质上还是属于 http 请求,只需要在返回头加上:

Content-type: text/calendar; charset=utf-8
Content-Disposition: attachment; filename="table.ics"

便可正常在日历 App 中订阅。

但是参考 iCalendar 的规范,每个事件还得有一个 “UID” 字段,这样便于客户端识别事件以更新。这个 UID 虽然有规范,但实际上你只需要做到和其他事件的 UID 不相同即可,这里我简单的用单个课程的名字,日期和节数进行 md5 计算。

最后的效果

在手机上:
87eba7a3-1abf-4b77-a65f-87320789c9e4.png
在 outlook 上:
c4b7f146-b0b4-4f31-b325-8dec435fc9aa.png
顺便一提,Windows10 及以上的系统是能够通过 outlook 实现订阅到系统日历,也支持锁屏提醒!

变得易用

这样基本的流程便完成了,不过最终服务人群还是同学,所以要将服务打造的简单易用!

现在是将更新任务(每周从教务系统拉数据)部署在云函数上,直接生成静态文件。前端随便找个地方放了就能提供服务了。

数据上的话策略是仅从教务系统拉取前两周,本周,未来四周的数据,这样每次更新都会有更好的性能表现。

目前也已经将服务部署在了站点上:订阅课程表
目前测试过了,所有有日历 App 的 Apple 设备,Windows10 及 Windows11,以及小米、华为、Vivo 设备均可直接导入。

目前其他安卓设备可以使用ICSX5这款软件,可以覆盖绝大多数安卓设备。

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.