最近打算写edX数据相关的文章,这一块庞大繁杂,资料分散,近来在这块花了大量时间,收集了大量资料源码以及阅读了基本所有可读的文档,决定将其整理出来,目的有二:

  • 整理一下自己的资料,不至于让数据成为坟墓,为下次深入留下线索,不至于重头开始
  • 也希望能帮助到后来的人,不会像我此前一般毫无头绪,备受煎熬

还没想好怎么组织这些文档资料和心得,先从一个具体的需求入手好了。

#任务描述 统计每个学生在每门课上的花费的学习时间

#Tracking Logs 为了完成以上任务,首先想到了Tracking Logs

这是个什么东西呢,文档中是这样介绍的

Events are emitted by the server, the browser, or the mobile device to capture information about interactions with the courseware and the Instructor Dashboard in the LMS, and are stored in JSON documents. In the data package, event data is delivered in a log file.

就是说这是个日志文件,内容为json格式,记录着用户与平台交互时发生的特定事件,事件可能由浏览器/后台系统或是手机端触发。

那么都有哪些行为会被记录呢?

这是所有的事件列表

其中比较有趣的是学生观看视频的许多行为都被记录,比如

  • edx.video.loaded
  • edx.video.paused
  • edx.video.played
  • edx.video.position.changed
  • edx.video.stopped
  • edx.video.transcript.hidden
  • edx.video.transcript.shown
  • and so on…

从这里可能挖掘出大量对改良教学本身有所助益的数据,好比学生反复回看某个视频段,那么团队可能发现这里也许讲解的不够清晰,更多有趣的行为分析,大家可以发挥想象,我觉得这也是edX最有趣的地方之一

最后给一个日志例子,我们就结束对Tracking Logs的介绍

{
    "username": "AAAAAAAAAA",
    "event_source": "server",
    "name": "edx.course.enrollment.deactivated",
    "referer": "http:\/\/localhost:8001\/container\/i4x:\/\/edX\/DemoX\/vertical\/69dedd38233a46fc89e4d7b5e8da1bf4?action=new",
    "accept_language": "en-US,en;q=0.8",
    "time": "2014-01-26T00:28:28.388782+00:00",
    "agent": "Mozilla\/5.0 (Windows NT 6.1; WOW64; Trident\/7.0; rv:11.0) like Gecko",
    "page": null
    "host": "courses.edx.org",
    "session": "a14j3ifhskngw0gfgn230g",
    "context": {
      "user_id": 9999999,
      "org_id": "edX",
      "course_id": "edX\/DemoX\/Demo_Course",
      "path": "\/change_enrollment",
    },
    "ip": "NN.NN.NNN.NNN",
    "event": {
      "course_id": "edX\/DemoX\/Demo_Course",
      "user_id": 9999999,
      "mode": "honor"
    },
    "event_type": "edx.course.enrollment.deactivated"
  }

更多的信息大家可自行去看日志文件,在/edx/var/log/tracking

对tracking log结构有兴趣的同学可以查看edx admin中的/admin/track/trackinglog/add/

#思路 既然tracking_logs跟踪记录了学生的所有行为数据,那么我们就从这里入手。看看能否从中挖掘出每个学生在每一门课所花的时间。
那么接下去的工作自然就变成对日志文本的解读,了解每个数据项的含义,这部分的文档也都在这里。此外我们知道,了解用户是否在线,sessions往往是关键。
而后设计计算用户在线时长的算法,再往后使用splunk或是hadoop来计算。
此后我去edx社区发了个求助帖,看看社区的建议,大家回复也基本是从sessions入手,@Alexander的回复让我很是开心,他说

The million dollar question

哈哈,我也觉得这是件很有价值的数据挖掘工作,此外@Kristin女神提到

and as far as I know there is no hearbeat event…

她还提到些session的细节,诸如粒度只到半小时(没有心跳检测),她似乎对这块很了解,还提出记录鼠标移动点击页面之类的事件,以此作为心跳检测,这是个很有趣的思路,这种思路也可以用来防作弊用。有兴趣的同学可以进一步挖掘,或是自定义事件来观察用户行为。

#just do it 而后我就开始阅读理解这些数据,和思考算法。
这个过程中,我也在github上搜索相关的项目,发现McGill大学也在做了这方面的工作,McGill是edX用户,这是加拿大的一所顶级名校,被誉为“加拿大的哈佛”。
随后翻了他们的源码库,发现他们在数据分析这块,可能是除了edx核心源码库之外走的最远的了,而且更棒的是,他们的分析多是以脚本形式,耦合度很低。读了几个脚本,发现和我此前的思路基本一致,很容易理解,那么我们就跟着McGill做好啦!!
所有功劳归McGill!

#步骤细节 建议数据分析在单独的机器上做,避免影响服务器性能

##导出tracking logs tracking Logs位于/edx/var/log/tracking
使用rsync同步这些文件,好处是rsync效率很高,每次只同步新增的部分。

##导入到mongo 使用以下脚本:load_tracking_logs_to_mongo 将log加载到mongo里

python load_tracking_logs_to_mongo.py db_name collection_name <path_to_directory>

当然也可以选择性地处理日志文件

python load_tracking_logs_to_mongo.py db_name collection_name <path_to_file_1> <path_to_file_2>

此外还可以选择性地加载课程相关的日志,并且指定时间段。可参考这里

以上是我从Mcgill大学fork的仓库,他们的reporting_scripts里有一个小bug,我修复了,顺便提了个pull request,好像上游仓库还没处理,所以大家可以先用我的

##查看课程 现在可以在mongo里查下都有哪些课程
db.tracking.distinct('context.course_id')
通过以上command可以得到log里的所有course_id

##归并文档 db.tracking.aggregate([{$match :{ 'context.course_id' : 'your_course_id' }}, {$out : 'your_course_id'}])
以上指令根据你指定的course_id,归并文档成为一个collection。

##统计用户在线时间 下载session_info脚本,修改里边的参数(DATABASE_NAME和collection_name)
执行python session_info.py,就可以得到所有用户这门课中每个session期间的在线时长,结果以一份csv报表(session_info.csv)呈现, 报表内容为['Username', 'Session ID', 'Number of Events', 'Start Time', 'End Time', 'Time Spent'],

如果需要统计同个用户在该课程下的花费的所有时长,只需要做个累加就行。使用pandas可能很方便~

#后记 以上只是一个可行方案,并没考虑效率,今天我翻了下我在5月4号发的帖子,@Alexander回复说

we created that script to get an initial idea but found it a very poor proxy and not helpful.

我这才得知他便是McGill大学edx_data_research项目的开发者。的确这只是个零时的不够优雅的解决方案。

如果要生产环境使用,必须考虑效率问题,可能以下几个内容会被考虑到:

  • 使用单独的计算资源,不要影响主服务器
  • 定时计划任务(cron)
  • 考虑并发
  • 使用hadoop或者spark
  • 将计算结果存回数据库
  • 新建一个页面向课程团队展示分析结果