※この記事はグロービス Advent Calendar 2017 17日目の記事です。
はじめに
愛校精神が強すぎて会社のアドベントカレンダーに学校のこと書いてる
— mktakuya (@mktakuya) 2017年12月17日
自分は北海道の苫小牧高専(以下、本校とする)の学生をしながら、東京都 麹町にある株式会社グロービスのエンジニアチームにてリモートインターンとしてコードを書いたり、SlackのPostに意味のないEmoji Reactionをつけてまわるなどの業務に従事している。インターンとしての業務は、ある程度時間を決めて学校の研究室や近所のカフェ、自宅などから行っているが、たまに授業変更が入ってきて業務時間を変更しなければならないことがある。
本校の授業変更情報はWebサイト上から確認することが出来るが、正直こんなものいちいち見に行くのはめんどくさい。結果、当日まで授業変更に気づかず、ギリギリになって業務時間変更の連絡をすることになり、上司やチーム全体に迷惑がかかってしまう。
重要な連絡事項をギリギリにしたり、アドベントカレンダーの担当日に記事の投稿を遅刻してしまうようでは、19年4月から社会人として働くという自覚が足りないと言われても仕方がないであろう。ではどうすれば良いのか。わざわざ授業変更情報をWebサイトを見に行くのがめんどくさいのなら、普段から見ているものに授業変更情報を取り込んでやれば良いのだ。
そこで今回開発したのが、「苫小牧高専授業変更情報 iCalendarフィード」(以下、本システムとする)である。
苫小牧高専授業変更情報 iCalendarフィード
苫小牧高専授業変更情報 iCalendarフィード (Beta版)
本システムを利用すれば、このようにかんたんに普段使いのカレンダー(GoogleカレンダーやApple iCalなど)に授業変更情報を取り込んで閲覧することが出来る。
本システムの解説
本システムは、授業変更情報の取得部分と配信部分の2つのプログラムからなる。
授業変更情報の取得
授業変更情報の取得部分の全ソースコードは、以下のリポジトリにある。
このプログラムは、以下のようなGemを利用している。
- HTMLのパースにNokogiri
- 授業変更情報のDBへの保存にSequel, pg
本校の授業変更情報ページは、以下のようなURLでアクセスすると対応したクラスの授業変更情報を吐いてくれるので、それをNokogiri
で適当にパースしてデータベースに突っ込むだけである。
http://jyugyou.tomakomai-ct.ac.jp/jyugyou.php?class=${クラス名}&all=true
このプログラムの核となる授業変更情報の取得部は以下のようなコードで実現している。
connect_opt = {"options"=>{"host"=>ENV['JYUGYOU_DB_HOST'], "user"=>ENV['JYUGYOU_DB_USER'], "password"=>ENV['JYUGYOU_DB_PASSWORD']}}
db = Sequel.postgres(ENV['JYUGYOU_DB_NAME'], connect_opt)
base_url = 'http://jyugyou.tomakomai-ct.ac.jp/jyugyou.php'
# CLASSES定数にはクラス名の対応が格納されている
# https://github.com/mktakuya/jyugyou/blob/master/classes.rb
CLASSES.each do |k, v|
url = base_url + "?class=#{k.to_s.upcase}&all=true"
html = Nokogiri::HTML(open(url))
table = html.search('table')[-1]
table.search('tr')[1..-1].each do |tr|
data = {class: v}
td0 = tr.search('td')[0]
td0_text = td0.text.gsub(' ', '').split
data[:date] = Date.strptime(td0_text[0], '%Y年%m月%d日')
data[:period] = td0_text[1]
td1 = tr.search('td')[1]
data[:content] = td1.text
data[:content].slice!(0)
db.transaction do
db[:jyugyous].insert(data) unless db[:jyugyous].where(data).first
end
end
end
db.disconnect
これをcronで日に1回程度の間隔でまわしている。
iCalendarフィードの出力
iCalendarフィードを出力する部分は、Sinatra製のWebアプリとなっている。
このプログラムは、以下のようなGemを利用している。
- Webアプリケーション開発用DSLとしてSinatra
- データベース接続用にSequel, pg
- iCalendar出力用にicalendar
iCalendarフィード出力部分は、以下のようなコードで実現している。
# CLASSES定数にはクラス名の対応が格納されている
# https://github.com/mktakuya/jyugyou-feed/blob/master/classes.rb
get '/feeds' do
if CLASSES.has_key? params[:class].downcase.to_sym
conds = {
class: CLASSES[params[:class].downcase.to_sym],
date: Date.today.last_week.beginning_of_week..Date.today.next_week.end_of_week
}
calendar = Icalendar::Calendar.new
calendar.append_custom_property("X-WR-CALNAME;VALUE=TEXT", "TNCT #{conds[:class]} 授業変更情報")
calendar.timezone do |t|
t.tzid = 'Asia/Tokyo'
t.standard do |s|
s.tzoffsetfrom = '+0900'
s.tzoffsetto = '+0900'
s.tzname = 'JST'
s.dtstart = '19700101T000000'
end
end
@db[:jyugyous].where(conds).all.each do |col|
calendar.event do |e|
e.dtstart = Icalendar::Values::Date.new(col[:date])
e.summary = "#{col[:period]} #{col[:content]}"
e.description = "#{col[:class]} #{col[:date].to_s}(#{col[:period]}) #{col[:content]}"
end
end
calendar.publish
content_type 'text/calendar'
calendar.to_ical
else
status 400
'400 Bad Request'
end
end
このWebアプリケーションに、 /feeds?class=AP1
というパスでアクセスすると、AP1(自分の所属する電子・生産システム工学専攻科1年を表す)の授業変更情報がiCalendarフィードとして出力される。
正直、iCalendarフィードはリクエストの度に動的生成する必要は無いので、近々作り変えたい。そうすると、Sinatraなんて使う必要なくて、本当にただの静的サイトになってしまうのだけど……。
おわりに
一人の学生として、そして一人のエンジニアとして、自分が不便だと感じたことは積極的に改善していく。この姿勢を忘れないでいきたい。
アドベントカレンダーみんなマジメなこと書いてるしここで一発ネタを仕込みたいって思ってたら遅刻した
— mktakuya (@mktakuya) 2017年12月17日