sfcsfs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +52 -0
- data/Rakefile +10 -0
- data/lib/sfcsfs.rb +16 -0
- data/lib/sfcsfs/agent.rb +88 -0
- data/lib/sfcsfs/agent/navigations.rb +97 -0
- data/lib/sfcsfs/homework.rb +12 -0
- data/lib/sfcsfs/lecture.rb +125 -0
- data/lib/sfcsfs/util.rb +15 -0
- data/script/console +12 -0
- data/script/console_helper.rb +4 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/sfcsfs.gemspec +22 -0
- data/spec/sfcsfs_agent_spec.rb +84 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +8 -0
- data/tasks/add.rake +49 -0
- data/tasks/rspec.rake +20 -0
- metadata +124 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 ymrl
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# SFCSFS
|
2
|
+
|
3
|
+
SFCSFS is a SFC-SFS Scraping Library. SFC-SFS is an internal website for Keio Univ. Shounan Fujisawa Campus.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'sfcsfs'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install sfcsfs
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
config = Pit.get("sfcsfs", :require => {
|
22
|
+
:account => "your CNS account",
|
23
|
+
:password => "your CNS password"
|
24
|
+
})
|
25
|
+
|
26
|
+
# Login to SFC-SFS
|
27
|
+
agent = SFCSFS.login(config[:account],config[:password])
|
28
|
+
|
29
|
+
# Get all classes of this semester and shows
|
30
|
+
# It takes several minutes...
|
31
|
+
list = agent.all_classes_of_this_semester
|
32
|
+
|
33
|
+
# Show name of these
|
34
|
+
list.each do |e|
|
35
|
+
puts e.title
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get your current weekly schedule
|
39
|
+
list = agent.my_schedule
|
40
|
+
|
41
|
+
# Show name of these
|
42
|
+
list.each do |e|
|
43
|
+
puts e.title
|
44
|
+
end
|
45
|
+
|
46
|
+
## Contributing
|
47
|
+
|
48
|
+
1. Fork it
|
49
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
50
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
51
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
52
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/sfcsfs.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#coding:utf-8
|
2
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
3
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
4
|
+
|
5
|
+
directory = File.expand_path(File.dirname(__FILE__))
|
6
|
+
|
7
|
+
require directory+'/sfcsfs/lecture.rb'
|
8
|
+
require directory+'/sfcsfs/homework.rb'
|
9
|
+
require directory+'/sfcsfs/util.rb'
|
10
|
+
require directory+'/sfcsfs/agent.rb'
|
11
|
+
require directory+'/sfcsfs/agent/navigations.rb'
|
12
|
+
|
13
|
+
|
14
|
+
module SFCSFS
|
15
|
+
VERSION = '0.1.0'
|
16
|
+
end
|
data/lib/sfcsfs/agent.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
#coding:utf-8
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'uri'
|
4
|
+
require 'net/https'
|
5
|
+
require 'addressable/uri'
|
6
|
+
|
7
|
+
module SFCSFS
|
8
|
+
REDIRECT_LIMIT = 5
|
9
|
+
|
10
|
+
class Agent
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
"#{self.class} #{@id}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def request uri,method=:get,data={}
|
17
|
+
ret = nil
|
18
|
+
count = 0
|
19
|
+
while count < REDIRECT_LIMIT and !ret or Net::HTTPRedirection === ret
|
20
|
+
uri = (URI::Generic === uri ? uri : URI.parse(uri))
|
21
|
+
req = nil
|
22
|
+
uri += '/' if uri.path == ''
|
23
|
+
path = uri.path
|
24
|
+
path += "?#{uri.query}" if uri.query && uri.query.length > 0
|
25
|
+
if method.to_sym == :post
|
26
|
+
req = Net::HTTP::Post.new(path)
|
27
|
+
else
|
28
|
+
req = Net::HTTP::Get.new(path)
|
29
|
+
end
|
30
|
+
req.set_form_data(data) if data.length > 0
|
31
|
+
http = Net::HTTP.new(uri.host,uri.port)
|
32
|
+
http.use_ssl = true if uri.scheme == 'https'
|
33
|
+
http.start{|http| ret = http.request(req)}
|
34
|
+
@uri = uri
|
35
|
+
uri = ret['location'] if ret['location']
|
36
|
+
count += 0
|
37
|
+
end
|
38
|
+
if count >= REDIRECT_LIMIT
|
39
|
+
raise TooManyRedirectionsException
|
40
|
+
end
|
41
|
+
return ret
|
42
|
+
end
|
43
|
+
|
44
|
+
def request_parse uri,method=:get,data={}
|
45
|
+
uri = (URI::Generic === uri ? uri : URI.parse(uri))
|
46
|
+
r = request(uri,method,data)
|
47
|
+
@doc = Nokogiri.HTML(SFCSFS.convert_encoding(r.body),nil,'UTF-8')
|
48
|
+
if meta = @doc.search('meta[http-equiv=refresh]').first
|
49
|
+
match = meta.attr('content').match(/url=(.*)$/)
|
50
|
+
if match
|
51
|
+
request_parse(@uri+match[1])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
return @doc
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize
|
58
|
+
@uri = nil
|
59
|
+
@doc = nil
|
60
|
+
@id = nil
|
61
|
+
@base_uri = nil
|
62
|
+
@type = nil
|
63
|
+
return self
|
64
|
+
end
|
65
|
+
|
66
|
+
def Agent.login(account,passwd,options={})
|
67
|
+
a = Agent.new
|
68
|
+
doc = a.request_parse('https://gc.sfc.keio.ac.jp/sfc-sfs/')
|
69
|
+
action = doc.search('form').attr('action')
|
70
|
+
a.request_parse(action,:post,:u_login => account, :u_pass => passwd)
|
71
|
+
query_values = Addressable::URI.parse(a.uri).query_values
|
72
|
+
a.id = query_values['id']
|
73
|
+
a.type = query_values['type']
|
74
|
+
|
75
|
+
if options[:vu9]
|
76
|
+
a.base_uri = URI.parse('https://vu9.sfc.keio.ac.jp/')
|
77
|
+
else
|
78
|
+
a.base_uri = a.uri + '/'
|
79
|
+
end
|
80
|
+
|
81
|
+
return a
|
82
|
+
end
|
83
|
+
attr_accessor :login,:query_values,:base_uri,
|
84
|
+
:doc,:id,:uri,:type
|
85
|
+
end
|
86
|
+
class TooManyRedirectionsException < Exception
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#coding:UTF-8
|
2
|
+
class SFCSFS::Agent
|
3
|
+
def plan_of_this_semester
|
4
|
+
get_plans_page_of_this_semester
|
5
|
+
plan_list_from_plans_page
|
6
|
+
end
|
7
|
+
|
8
|
+
def plan_of_next_semester
|
9
|
+
get_plans_page_of_next_semester
|
10
|
+
plan_list_from_plans_page
|
11
|
+
end
|
12
|
+
|
13
|
+
def all_classes_of_this_semester
|
14
|
+
get_plans_page_of_this_semester
|
15
|
+
all_classes_from_plans_page
|
16
|
+
end
|
17
|
+
|
18
|
+
def all_classes_of_next_semester
|
19
|
+
get_plans_page_of_next_semester
|
20
|
+
all_classes_from_plans_page
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_plans_page_of_this_semester
|
24
|
+
outer_uri = @base_uri +
|
25
|
+
"/sfc-sfs/portal_s/s02.cgi?id=#{@id}&type=#{@type}&mode=1&lang=ja"
|
26
|
+
request_parse(outer_uri)
|
27
|
+
inner_uri = outer_uri + @doc.search('iframe').attr('src').to_s
|
28
|
+
request_parse(inner_uri)
|
29
|
+
end
|
30
|
+
|
31
|
+
def get_plans_page_of_next_semester
|
32
|
+
outer_uri = @base_uri +
|
33
|
+
"/sfc-sfs/portal_s/s02.cgi?id=#{@id}&type=#{@type}&mode=2&lang=ja"
|
34
|
+
request_parse(outer_uri)
|
35
|
+
inner_uri = outer_uri + @doc.search('iframe').attr('src').to_s
|
36
|
+
request_parse(inner_uri)
|
37
|
+
end
|
38
|
+
|
39
|
+
def all_classes_from_plans_page
|
40
|
+
list = []
|
41
|
+
uri = @uri
|
42
|
+
@doc.search('a[href]').to_a.delete_if{|e| !e.attr('href').match(/class_list\.cgi/)}.each do |e|
|
43
|
+
request_parse uri+e.attr('href')
|
44
|
+
@doc.search('a[href]').to_a.delete_if{|e| !e.attr('href').match(/plan_list\.cgi/)}.each do |e|
|
45
|
+
str = e.parent.text
|
46
|
+
title = nil
|
47
|
+
instructor = nil
|
48
|
+
#if match = str.match(/:\d+\(.+?\) …\s?(.+?)\s?\((.+?)\)…/)
|
49
|
+
if match = str.match(/:\d+\(.+?\) … (.+?)\s?\(([^\(\)]+?)\)…/u)
|
50
|
+
title = match[1]
|
51
|
+
instructor = match[2]
|
52
|
+
end
|
53
|
+
q = Addressable::URI.parse(e.attr('href')).query_values
|
54
|
+
ks = q['ks']
|
55
|
+
yc = q['yc']
|
56
|
+
reg = q['reg']
|
57
|
+
term = q['term']
|
58
|
+
list.push SFCSFS::Lecture.new(self,title,instructor,:ks=>ks,:yc=>yc,:reg=>reg,:term=>term)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
return list
|
62
|
+
end
|
63
|
+
|
64
|
+
def plan_list_from_plans_page
|
65
|
+
@doc.search('a').to_a.delete_if{|e| !e.attributes['href'].match(/syll_view.cgi/)}.map do |e|
|
66
|
+
href = e.attributes['href']
|
67
|
+
q = Addressable::URI.parse(href).query_values
|
68
|
+
ks = q['ks']
|
69
|
+
yc = q['yc']
|
70
|
+
title = e.children.first.to_s
|
71
|
+
SFCSFS::Lecture.new(self,title,nil,:ks=>ks,:yc=>yc)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def my_schedule
|
76
|
+
outer_uri = @base_uri +
|
77
|
+
"/sfc-sfs/portal_s/s01.cgi?id=#{@id}&type=#{@type}&mode=1&lang=ja"
|
78
|
+
request_parse outer_uri
|
79
|
+
inner_uri = @uri + @doc.search('iframe').attr('src').to_s
|
80
|
+
request_parse inner_uri
|
81
|
+
|
82
|
+
@doc.search('table a[href]').to_a.delete_if{|e|
|
83
|
+
!e.attr('href').match(/sfs_class/)
|
84
|
+
}.map do |e|
|
85
|
+
href = e.attr("href")
|
86
|
+
q = Addressable::URI.parse(href).query_values
|
87
|
+
option ={}
|
88
|
+
option[:ks] = q['ks']
|
89
|
+
option[:yc] = q['yc']
|
90
|
+
option[:mode] = href.match(/faculty/) ? 'faculty' : 'student'
|
91
|
+
title = e.children.first.to_s
|
92
|
+
title.gsub!(/\[([^\]]*)\]$/u){option[:place]=$1;""}
|
93
|
+
instructor = e.next.next.to_s.gsub(/[()\s ]/u,'')
|
94
|
+
SFCSFS::Lecture.new(self,title,instructor,option)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
#coding:utf-8
|
2
|
+
module SFCSFS
|
3
|
+
class Lecture
|
4
|
+
def initialize(agent,title,instructor,config={})
|
5
|
+
@agent = agent
|
6
|
+
@title = title
|
7
|
+
@instructor = instructor
|
8
|
+
@instructors = config[:instructors] || []
|
9
|
+
@mode = config[:mode]
|
10
|
+
@yc = config[:yc]
|
11
|
+
@ks = config[:ks]
|
12
|
+
@reg = config[:reg]
|
13
|
+
@term = config[:term]
|
14
|
+
@homeworks = config[:homeworks] || []
|
15
|
+
@periods = config[:periods] || []
|
16
|
+
@applicants = config[:applicants]
|
17
|
+
@limit = config[:limit]
|
18
|
+
@place = config[:place]
|
19
|
+
end
|
20
|
+
def inspect
|
21
|
+
"#{self.class} \"#{@title}\" \"#{@instructor}\" #{@yc} #{@ks}"
|
22
|
+
end
|
23
|
+
def class_top_path
|
24
|
+
unless @yc && @ks && @agent.id
|
25
|
+
raise NotEnoughParamsException
|
26
|
+
end
|
27
|
+
mode = 'student'
|
28
|
+
file = 's_class_top.cgi'
|
29
|
+
if(self.mode == 'faculty')
|
30
|
+
mode = 'faculty'
|
31
|
+
file = 'f_class_top.cgi'
|
32
|
+
end
|
33
|
+
return "/sfc-sfs/sfs_class/#{mode}/#{file}?lang=ja&ks=#{@ks}&yc=#{@yc}&id=#{@agent.id}"
|
34
|
+
end
|
35
|
+
def stay_input_path
|
36
|
+
unless @yc && @ks && @agent.id
|
37
|
+
raise NotEnoughParamsException
|
38
|
+
end
|
39
|
+
"/sfc-sfs/sfs_class/stay/stay_input.cgi?yc=#{@yc}&ks=#{@ks}&enc_id=#{@agent.id}"
|
40
|
+
end
|
41
|
+
def student_selection_path
|
42
|
+
unless @yc && @agent.id
|
43
|
+
raise NotEnoughParamsException
|
44
|
+
end
|
45
|
+
"/sfc-sfs/sfs_class/student/view_student_select.cgi?enc_id=#{@agent.id}&yc=#{@yc}&lang=ja"
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_detail
|
49
|
+
uri = @agent.base_uri + class_top_path
|
50
|
+
doc = @agent.request_parse(uri)
|
51
|
+
@title = doc.search('h3.one').first.children.first.to_s.gsub(/[\s ]/,'')
|
52
|
+
@instructors += doc.search('h3.one > a[href]').to_a.delete_if{|e| !e.attr('href').match(/profile\.cgi/)}.map{|e|e.children.first.to_s}
|
53
|
+
@instructors.uniq!
|
54
|
+
term = doc.search('h3.one .ja').text.match(/時限:(\d{4})年([春秋])学期(.*)$/)
|
55
|
+
if term
|
56
|
+
@term = "#{term[1]}#{term[2] == '春' ? 's' : 'f'}"
|
57
|
+
@periods += term[3].gsub(/\s/,'').split(/,/)
|
58
|
+
@periods.uniq!
|
59
|
+
end
|
60
|
+
page = doc.to_s
|
61
|
+
#a.doc.search('a').to_a.delete_if{|e|!e.attributes['href'].match(%r{/report/report\.cgi\?})}.each do |e|
|
62
|
+
# # TODO : Homeworks Func
|
63
|
+
#end
|
64
|
+
if m = page.match(/履修希望者数:(\d+)/)
|
65
|
+
@applicants = m[1].to_i
|
66
|
+
end
|
67
|
+
if m = page.match(/受入学生数(予定):約 (\d+) 人/)
|
68
|
+
@limit = m[1].to_i
|
69
|
+
end
|
70
|
+
return self
|
71
|
+
end
|
72
|
+
|
73
|
+
def student_selection_list
|
74
|
+
uri = @agent.base_uri + student_selection_path
|
75
|
+
doc = @agent.request_parse(uri)
|
76
|
+
return doc.search('tr[bgcolor="#efefef"] td').map{|e| e.children.first.to_s}.delete_if{|e| !e.match(/^\d{8}/)}
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_stay_input_page
|
80
|
+
uri = @agent.base_uri + stay_input_path
|
81
|
+
@agent.request_parse(uri)
|
82
|
+
end
|
83
|
+
def submit_stay_form data
|
84
|
+
param = {}
|
85
|
+
uri = @agent.base_uri + '/sfc-sfs/sfs_class/stay/stay_input.cgi'
|
86
|
+
unless @yc && @ks && @aget.id
|
87
|
+
raise NotEnoughParamsException
|
88
|
+
end
|
89
|
+
param[:stay_phone] = data[:stay_phone]
|
90
|
+
param[:stay_p_phone] = data[:stay_p_phone]
|
91
|
+
param[:stay_time] = data[:stay_time]
|
92
|
+
param[:selectRoom] = data[:selectRoom]
|
93
|
+
param[:selectFloor] = data[:selectFloor]
|
94
|
+
param[:stay_room_other] = data[:stay_room_other]
|
95
|
+
param[:stay_reason] = data[:stay_reason]
|
96
|
+
param[:mode] = 'submit'
|
97
|
+
param[:yc] = @yc
|
98
|
+
param[:ks] = @ks
|
99
|
+
param[:enc_id] = @agent.id
|
100
|
+
request uri,:post,data
|
101
|
+
end
|
102
|
+
def add_to_plan
|
103
|
+
unless @reg && @yc && @ks && @agent.id && @term
|
104
|
+
raise NotEnoughParamsException
|
105
|
+
end
|
106
|
+
uri = @agent.base_uri + "/sfc-sfs/sfs_class/student/plan_list.cgi?reg=#{@reg}&yc=#{@yc}&mode=add&ks=#{@ks}&id=#{@agent.id}&term=#{@term}&lang=ja"
|
107
|
+
@agent.request(uri)
|
108
|
+
end
|
109
|
+
|
110
|
+
def delete_from_plan
|
111
|
+
unless @yc && @ks && @agent.id && @term
|
112
|
+
raise NotEnoughParamsException
|
113
|
+
end
|
114
|
+
uri = @agent.base_uri + "/sfc-sfs/sfs_class/student/plan_list.cgi?reg=#{@reg}&yc=#{@yc}&mode=del&ks=#{@ks}&id=#{@agent.id}&term=#{@term}&lang=ja"
|
115
|
+
@agent.request(uri)
|
116
|
+
end
|
117
|
+
|
118
|
+
attr_accessor :title,:instructor,:mode,:yc,:ks,:homeworks,
|
119
|
+
:add_list_url,:periods,:applicants,:limit,
|
120
|
+
:reg,:term, :place
|
121
|
+
end
|
122
|
+
|
123
|
+
class NotEnoughParamsException < Exception
|
124
|
+
end
|
125
|
+
end
|
data/lib/sfcsfs/util.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module SFCSFS
|
2
|
+
def SFCSFS.login(account,passwd)
|
3
|
+
return Agent.login(account,passwd)
|
4
|
+
end
|
5
|
+
|
6
|
+
def SFCSFS.convert_encoding str
|
7
|
+
if RUBY_VERSION >= "1.9"
|
8
|
+
return str.force_encoding(Encoding::EUC_JP).encode(Encoding::UTF_8,:invalid=>:replace,:undef=>:replace)
|
9
|
+
else
|
10
|
+
require 'kconv'
|
11
|
+
return Kconv.kconv(str,Kconv::UTF8,Kconv::EUC)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
data/script/console
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r ./#{File.dirname(__FILE__) + '/../lib/sfcsfs'}"
|
9
|
+
libs << " -r ./#{File.dirname(__FILE__) + '/console_helper'}"
|
10
|
+
puts "Loading SFCSFS"
|
11
|
+
puts "SFCSFS::Agent.login(CONFIG[:account],CONFIG[:password])"
|
12
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/sfcsfs.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/sfcsfs', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["ymrl"]
|
6
|
+
gem.email = ["ymrl@ymrl.net"]
|
7
|
+
gem.description = %q{SFC-SFS Scraping Library}
|
8
|
+
gem.summary = %q{SFC-SFS Scraping Library}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "sfcsfs"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = SFCSFS::VERSION
|
17
|
+
gem.add_dependency('nokogiri','~> 1.5.5')
|
18
|
+
gem.add_dependency('addressable','~> 2.2.8')
|
19
|
+
gem.add_development_dependency('rspec', '~> 2.10.0')
|
20
|
+
gem.add_development_dependency('rake')
|
21
|
+
gem.add_development_dependency('pit')
|
22
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
#coding:utf-8
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
4
|
+
require File.dirname(__FILE__) + '/../lib/sfcsfs.rb'
|
5
|
+
|
6
|
+
describe 'Agent' do
|
7
|
+
before do
|
8
|
+
config = Pit.get("sfcsfs", :require => {
|
9
|
+
:account => "your CNS account",
|
10
|
+
:password => "your CNS password"
|
11
|
+
})
|
12
|
+
@agent = SFCSFS.login(config[:account],config[:password])
|
13
|
+
end
|
14
|
+
it 'URLがs01.cgiにいる' do
|
15
|
+
@agent.uri.to_s.match(%r!^https://vu.\.sfc\.keio\.ac\.jp/sfc-sfs/portal_s/s01\.cgi.*!).should be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'idがセットされている' do
|
19
|
+
@agent.id.should be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
#it '次学期プランページへのアクセス' do
|
23
|
+
# @agent.get_plans_page_of_next_semester
|
24
|
+
#end
|
25
|
+
|
26
|
+
#context '次学期プランの履修' do
|
27
|
+
# it '科目一覧の取得' do
|
28
|
+
# list = @agent.get_class_list_of_next_semester
|
29
|
+
# list.each do |e|
|
30
|
+
# e.should be_instance_of(SFCSFS::Lecture)
|
31
|
+
# e.add_list_url.should be_true
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#end
|
35
|
+
#it '今学期プランページへのアクセス' do
|
36
|
+
# @agent.get_plans_page_of_this_semester
|
37
|
+
#end
|
38
|
+
|
39
|
+
#context '今学期プランの履修' do
|
40
|
+
# it '科目一覧の取得' do
|
41
|
+
# list = @agent.get_class_list_of_this_semester
|
42
|
+
# list.each do |e|
|
43
|
+
# e.should be_instance_of(SFCSFS::Lecture)
|
44
|
+
# e.add_list_url.should be_true
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#end
|
48
|
+
|
49
|
+
|
50
|
+
context 'my時間割にアクセスする' do
|
51
|
+
before do
|
52
|
+
@lectures = @agent.my_schedule
|
53
|
+
end
|
54
|
+
it 'Lectureが生成されている' do
|
55
|
+
@lectures.each do |e|
|
56
|
+
e.should be_instance_of(SFCSFS::Lecture)
|
57
|
+
e.instructor.should have_at_least(1).items
|
58
|
+
e.yc.should be_true
|
59
|
+
e.ks.should be_true
|
60
|
+
e.mode.should be_true
|
61
|
+
e.homeworks.should be_instance_of(Array)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
#it 'Lectureから残留届けページヘ行ける' do
|
65
|
+
# @lectures.each {|e| e.get_stay_input_page(@agent)}
|
66
|
+
#end
|
67
|
+
it 'Lecture#instructorが括弧とか\sとか含まない' do
|
68
|
+
@lectures.each { |e| (e.instructor.match(/[()()\s]/)).should be_false }
|
69
|
+
end
|
70
|
+
#it 'Lectureそれぞれに詳細情報を取得できる' do
|
71
|
+
# @lectures.each do |e| e.get_detail()
|
72
|
+
# e.homeworks.each do |h|
|
73
|
+
# h.should be_instance_of(SFCSFS::Homework)
|
74
|
+
# h.title.should have_at_least(1).items
|
75
|
+
# h.title.match(/^「/).should be_false
|
76
|
+
# h.title.match(/」$/).should be_false
|
77
|
+
# h.url.match(%r{.*\.sfc\.keio\.ac\.jp/sfc-sfs/sfs_class/report/report.cgi\?}).should be_true
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
#end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
data/tasks/add.rake
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require './' + File.dirname(__FILE__) + '/../lib/sfcsfs.rb'
|
2
|
+
require 'pit'
|
3
|
+
desc "Add All Lectures of Next Semester"
|
4
|
+
|
5
|
+
task :addnext do
|
6
|
+
config = Pit.get("sfcsfs", :require => {
|
7
|
+
:account => "your CNS account",
|
8
|
+
:password => "your CNS password"
|
9
|
+
})
|
10
|
+
agent = SFCSFS.login(config[:account],config[:password])
|
11
|
+
list = agent.all_classes_of_next_semester
|
12
|
+
list.each do |lecture|
|
13
|
+
puts "#{lecture.title} (#{lecture.instructor})"
|
14
|
+
retry_count = 0
|
15
|
+
begin
|
16
|
+
lecture.add_to_plan
|
17
|
+
rescue
|
18
|
+
sleep 60
|
19
|
+
retry_count += 1
|
20
|
+
if retry_count < 10
|
21
|
+
retry
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "Add All Lectures of this Semester"
|
28
|
+
task :addall do
|
29
|
+
config = Pit.get("sfcsfs", :require => {
|
30
|
+
:account => "your CNS account",
|
31
|
+
:password => "your CNS password"
|
32
|
+
})
|
33
|
+
agent = SFCSFS.login(config[:account],config[:password])
|
34
|
+
list = agent.all_classes_of_this_semester
|
35
|
+
list.each do |lecture|
|
36
|
+
puts "#{lecture.title} (#{lecture.instructor})"
|
37
|
+
retry_count = 0
|
38
|
+
begin
|
39
|
+
lecture.add_to_plan
|
40
|
+
rescue
|
41
|
+
sleep 60
|
42
|
+
retry_count += 1
|
43
|
+
if retry_count < 10
|
44
|
+
retry
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
data/tasks/rspec.rake
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
begin
|
2
|
+
require 'rspec'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
5
|
+
require 'rspec'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rspec/core/rake_task'
|
9
|
+
rescue LoadError
|
10
|
+
puts <<-EOS
|
11
|
+
To use rspec for testing you must install rspec gem:
|
12
|
+
gem install rspec
|
13
|
+
EOS
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
|
17
|
+
RSpec::Core::RakeTask.new do |t|
|
18
|
+
t.rspec_opts = ['--options', "spec/spec.opts"]
|
19
|
+
t.pattern = 'spec/**/*_spec.rb'
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sfcsfs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- ymrl
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-09-20 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nokogiri
|
16
|
+
requirement: &87235160 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.5.5
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *87235160
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: addressable
|
27
|
+
requirement: &87234920 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.2.8
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *87234920
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &87234690 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.10.0
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *87234690
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: &87234500 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *87234500
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: pit
|
60
|
+
requirement: &87234270 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *87234270
|
69
|
+
description: SFC-SFS Scraping Library
|
70
|
+
email:
|
71
|
+
- ymrl@ymrl.net
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- lib/sfcsfs.rb
|
82
|
+
- lib/sfcsfs/agent.rb
|
83
|
+
- lib/sfcsfs/agent/navigations.rb
|
84
|
+
- lib/sfcsfs/homework.rb
|
85
|
+
- lib/sfcsfs/lecture.rb
|
86
|
+
- lib/sfcsfs/util.rb
|
87
|
+
- script/console
|
88
|
+
- script/console_helper.rb
|
89
|
+
- script/destroy
|
90
|
+
- script/generate
|
91
|
+
- sfcsfs.gemspec
|
92
|
+
- spec/sfcsfs_agent_spec.rb
|
93
|
+
- spec/spec.opts
|
94
|
+
- spec/spec_helper.rb
|
95
|
+
- tasks/add.rake
|
96
|
+
- tasks/rspec.rake
|
97
|
+
homepage: ''
|
98
|
+
licenses: []
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
105
|
+
requirements:
|
106
|
+
- - ! '>='
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '0'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 1.8.11
|
118
|
+
signing_key:
|
119
|
+
specification_version: 3
|
120
|
+
summary: SFC-SFS Scraping Library
|
121
|
+
test_files:
|
122
|
+
- spec/sfcsfs_agent_spec.rb
|
123
|
+
- spec/spec.opts
|
124
|
+
- spec/spec_helper.rb
|