moodleQuizDownloader 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +42 -0
- data/Rakefile +1 -0
- data/bin/moodleQuizDownloader.rb +4 -0
- data/lib/moodleQuizDownloader.rb +16 -0
- data/lib/moodleQuizDownloader/file_name_creator.rb +12 -0
- data/lib/moodleQuizDownloader/moodle_parser.rb +61 -0
- data/lib/moodleQuizDownloader/option_handler.rb +104 -0
- data/lib/moodleQuizDownloader/quiz_downloader.rb +78 -0
- data/lib/moodleQuizDownloader/version.rb +3 -0
- data/moodleQuizDownloader.gemspec +31 -0
- data/spec/api/asciify_spec.rb +8 -0
- data/spec/api/pdfkit_spec.rb +21 -0
- data/spec/fileNameCreator_spec.rb +19 -0
- data/spec/moodleparsing/exam-overview-moodle-ss2013.html +358 -0
- data/spec/moodleparsing/exam-overview.html +233 -0
- data/spec/moodleparsing/moodleparsing_spec.rb +53 -0
- data/spec/moodleparsing/review-page-nutzerbild.html +278 -0
- data/spec/moodleparsing/review-page.html +239 -0
- data/spec/option_handler_spec.rb +127 -0
- metadata +175 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a8c17d2cdd565630203b2eaacaaf27fe47fe3110
|
4
|
+
data.tar.gz: de31f285dd09c3cfd339c01a3761067169f08bbf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5d2fa647058a7534f13518b88ab538d1a195a57090f8293b4803b4044b134e256d20a396c2ac8a85d1d130fdea9c3ca88eb64dd3ca2424966062dfd72855470f
|
7
|
+
data.tar.gz: b862718b20c6ae363765f1dae1d6ac1b97104375f87d20af8b471f46605a791f96f77fc6cae2c2f0565c5fdcfd76992820f84353fdde8cf49abe296bba71fb99
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Dr Blinken
|
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,42 @@
|
|
1
|
+
# MoodleQuizDownloader
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'moodleQuizDownloader'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install moodleQuizDownloader
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
for example, run
|
22
|
+
|
23
|
+
$ moodleQuizDownloader.rb -e 4711 -u drblinken -s http://moodle2.htw-berlin.de/ -p geheim --verbose
|
24
|
+
|
25
|
+
to see a list of all attempts in exam 4711 on the specified server,
|
26
|
+
and
|
27
|
+
|
28
|
+
$ moodleQuizDownloader.rb -e 4711 -u drblinken -s http://moodle2.htw-berlin.de/ -p geheim --verbose download
|
29
|
+
|
30
|
+
to download them.
|
31
|
+
|
32
|
+
to see all options:
|
33
|
+
|
34
|
+
$ moodleQuizDownloader.rb help
|
35
|
+
|
36
|
+
## Contributing
|
37
|
+
|
38
|
+
1. Fork it
|
39
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
40
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
41
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
42
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative "moodleQuizDownloader/version.rb"
|
2
|
+
require_relative "moodleQuizDownloader/option_handler.rb"
|
3
|
+
require_relative "moodleQuizDownloader/quiz_downloader.rb"
|
4
|
+
|
5
|
+
module MoodleQuizDownloader
|
6
|
+
def run_script(arguments)
|
7
|
+
options = OptionHandler.new(arguments).parse
|
8
|
+
if options.usage
|
9
|
+
puts options.usage
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
QuizDownloader.new.run(options)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module MoodleParser
|
2
|
+
@@user_view_regexp = Regexp.new('http://moodle2.htw-berlin.de/moodle/user/view.php')
|
3
|
+
|
4
|
+
def moodle_login_page(server)
|
5
|
+
"#{server}/moodle/login/index.php"
|
6
|
+
end
|
7
|
+
def moodle_item(server)
|
8
|
+
"#{server}/moodle/mod/quiz/view.php?id="
|
9
|
+
end
|
10
|
+
def moodle_quiz_report(server)
|
11
|
+
"#{server}/moodle/mod/quiz/report.php?mode=overview&id="
|
12
|
+
end
|
13
|
+
|
14
|
+
def login(agent,moodle_login_page,moodle_username,moodle_password)
|
15
|
+
puts "++++#{moodle_login_page}"
|
16
|
+
page = agent.get(moodle_login_page)
|
17
|
+
form = page.forms[1]
|
18
|
+
form.username = moodle_username
|
19
|
+
form.password = moodle_password
|
20
|
+
page = form.submit
|
21
|
+
end
|
22
|
+
|
23
|
+
def selectReviewLinks(page)
|
24
|
+
page.links.select do |ll|
|
25
|
+
cls = ll.attributes.attributes['class']
|
26
|
+
cls && cls.value == 'reviewlink'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def extract_attempt_list(page)
|
31
|
+
i = 1
|
32
|
+
result_list = []
|
33
|
+
while page.at("//*[@id=\"attempts\"]/tbody/tr[#{i}]/td[3]/a[1]") do
|
34
|
+
student_name = page.at("//*[@id=\"attempts\"]/tbody/tr[#{i}]/td[3]/a[1]").text
|
35
|
+
attempt_url = page.at("//*[@id=\"attempts\"]/tbody/tr[#{i}]/td[3]/a[2]").attributes["href"].value
|
36
|
+
result_list << [student_name,attempt_url]
|
37
|
+
i += 1
|
38
|
+
end
|
39
|
+
result_list
|
40
|
+
end
|
41
|
+
|
42
|
+
def extractUserName(page)
|
43
|
+
|
44
|
+
l = page.links.select {|x| @@user_view_regexp.match(x.href)}
|
45
|
+
nutzerbild_string = "Nutzerbild "
|
46
|
+
nutzerbild = l.select{ |x| /Nutzerbild/.match(x.text)}
|
47
|
+
if nutzerbild.empty?
|
48
|
+
nutzerbild = l.select{ |x| /Picture of/.match(x.text)}
|
49
|
+
nutzerbild_string = "Picture of "
|
50
|
+
end
|
51
|
+
if nutzerbild.empty?
|
52
|
+
puts "## WARNING! ##"
|
53
|
+
puts "could not extract user name"
|
54
|
+
puts "## nutzerbild #{nutzerbild.inspect}"
|
55
|
+
puts "-- nutzerbild_string #{nutzerbild_string}"
|
56
|
+
return "Unknown Student"
|
57
|
+
end
|
58
|
+
nutzerbild.first.text.gsub(nutzerbild_string,"")
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
class OptionHandler
|
5
|
+
def valid_commands
|
6
|
+
[:list, :download, :connect]
|
7
|
+
end
|
8
|
+
attr_reader :options, :optionparser
|
9
|
+
def initialize(arguments = [])
|
10
|
+
@arguments = arguments
|
11
|
+
@options, @optionparser = parse(arguments)
|
12
|
+
end
|
13
|
+
def usage
|
14
|
+
@options.usage
|
15
|
+
end
|
16
|
+
|
17
|
+
def validate(options,optionparser)
|
18
|
+
options.usage = optionparser.help unless valid?(options)
|
19
|
+
end
|
20
|
+
def valid?(options)
|
21
|
+
options.valid &&
|
22
|
+
options.moodle_username &&
|
23
|
+
options.moodle_password &&
|
24
|
+
options.moodle_server &&
|
25
|
+
options.exam_id != 0
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse(args = @arguments)
|
29
|
+
# The options specified on the command line will be collected in *options*.
|
30
|
+
# We set default values here.
|
31
|
+
options = OpenStruct.new
|
32
|
+
options.valid = true
|
33
|
+
options.verbose = false
|
34
|
+
options.moodle_username = ENV['MOODLE_USERNAME']
|
35
|
+
options.moodle_password = ENV['MOODLE_PASSWORD']
|
36
|
+
options.moodle_server = ENV['MOODLE_SERVER']
|
37
|
+
options.outputdir = "out"
|
38
|
+
options.exam_id = 0
|
39
|
+
options.command = :list
|
40
|
+
options.usage = nil
|
41
|
+
|
42
|
+
|
43
|
+
opt_parser = OptionParser.new do |opts|
|
44
|
+
opts.banner = <<DELIM
|
45
|
+
Usage: moodleQuizDownloader [options] <command>
|
46
|
+
Where <command> is one of the following: #{valid_commands.join(", ")}
|
47
|
+
DELIM
|
48
|
+
|
49
|
+
opts.separator ""
|
50
|
+
opts.separator "Specific options:"
|
51
|
+
|
52
|
+
opts.on("-e", "--exam EXAM_ID",
|
53
|
+
"The ID of the Exam to be downloaded") do | id |
|
54
|
+
options.exam_id = id
|
55
|
+
end
|
56
|
+
opts.on("-o", "--outputdir DIR",
|
57
|
+
"Output Dir (defaults to . )") do | dir |
|
58
|
+
options.outputdir = dir
|
59
|
+
end
|
60
|
+
opts.on("-u", "--user USERNAME",
|
61
|
+
"Your Moodle User Name",
|
62
|
+
"(can also be set via MOODLE_USERNAME environment variable)") do |username|
|
63
|
+
options.moodle_username = username
|
64
|
+
end
|
65
|
+
opts.on("-p", "--password PASSWORD]",
|
66
|
+
"Your Moodle Password",
|
67
|
+
"(can also be set via MOODLE_PASSWORD environment variable)") do |password|
|
68
|
+
options.moodle_password = password
|
69
|
+
end
|
70
|
+
|
71
|
+
opts.on("-s", "--server SERVER]",
|
72
|
+
"Your Moodle Server",
|
73
|
+
"(can also be set via MOODLE_SERVER environment variable)") do |server|
|
74
|
+
options.moodle_server = server
|
75
|
+
end
|
76
|
+
|
77
|
+
opts.separator ""
|
78
|
+
opts.separator "Common options:"
|
79
|
+
|
80
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
81
|
+
puts opts
|
82
|
+
exit
|
83
|
+
end
|
84
|
+
|
85
|
+
opts.on_tail("-v", "--version", "Show version") do
|
86
|
+
options.command = :version
|
87
|
+
end
|
88
|
+
opts.on("-b", "--[no-]verbose", "Run verbosely") do |v|
|
89
|
+
options.verbose = v
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
opt_parser.order(args) do | command |
|
94
|
+
command = command.to_sym
|
95
|
+
if valid_commands.include?(command)
|
96
|
+
options.command = command
|
97
|
+
else
|
98
|
+
options.valid=false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
validate(options,opt_parser)
|
102
|
+
options
|
103
|
+
end # parse()
|
104
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'mechanize'
|
2
|
+
require 'pdfkit'
|
3
|
+
require 'asciify'
|
4
|
+
require "bundler/setup"
|
5
|
+
require 'fileutils'
|
6
|
+
require_relative 'file_name_creator'
|
7
|
+
|
8
|
+
require_relative 'moodle_parser'
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
class QuizDownloader
|
13
|
+
include MoodleParser
|
14
|
+
|
15
|
+
def smoketest(server)
|
16
|
+
begin
|
17
|
+
agent = Mechanize.new
|
18
|
+
page = login(agent,moodle_login_page(server),"xxx","xxx")
|
19
|
+
if page.nil?
|
20
|
+
puts "could not connect to server #{server}"
|
21
|
+
else
|
22
|
+
puts "connection to server seems to be working"
|
23
|
+
end
|
24
|
+
rescue Exception => e
|
25
|
+
puts "could not connect to server #{server}"
|
26
|
+
#puts e.message
|
27
|
+
end
|
28
|
+
end
|
29
|
+
def attemptlist(options,agent)
|
30
|
+
puts "connecting to moodle" if options.verbose
|
31
|
+
page = login(agent,moodle_login_page(options.moodle_server),options.moodle_username,options.moodle_password)
|
32
|
+
moodle_overview_url = moodle_quiz_report(options.moodle_server)+options.exam_id.to_s
|
33
|
+
puts "==== downloading overview" if options.verbose
|
34
|
+
puts moodle_overview_url if options.verbose
|
35
|
+
page = agent.get(moodle_overview_url)
|
36
|
+
#attempts = selectReviewLinks(page)
|
37
|
+
attempt_list = extract_attempt_list(page)
|
38
|
+
end
|
39
|
+
|
40
|
+
def download(attempt_list,options,agent)
|
41
|
+
FileUtils.mkdir_p(options.outputdir)
|
42
|
+
attempt_list.each do | attempt|
|
43
|
+
student_name, attempt_url = attempt
|
44
|
+
|
45
|
+
attempt_url = attempt_url+"&showall=1"
|
46
|
+
page = agent.get(attempt_url)
|
47
|
+
puts "==== downloading attempt" if options.verbose
|
48
|
+
puts attempt_url if options.verbose
|
49
|
+
#student = extractUserName(page)
|
50
|
+
student = student_name
|
51
|
+
outputfile = FileNameCreator.fileNameFor(options.outputdir,student)
|
52
|
+
puts "Loading: #{student}"
|
53
|
+
kit = PDFKit.new(page.body)
|
54
|
+
kit.to_file(outputfile)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
def run(options)
|
60
|
+
agent = Mechanize.new
|
61
|
+
case options.command
|
62
|
+
when :connect
|
63
|
+
smoketest(options.moodle_server)
|
64
|
+
when :list
|
65
|
+
attempt_list = attemptlist(options,agent)
|
66
|
+
puts "found #{attempt_list.length} attempts:"
|
67
|
+
puts attempt_list.map{|a,b| a}
|
68
|
+
when :download
|
69
|
+
attempt_list = attemptlist(options,agent)
|
70
|
+
download(attempt_list,options,agent)
|
71
|
+
else
|
72
|
+
puts "command #{options.command} not recognized"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
end
|
78
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'moodleQuizDownloader/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "moodleQuizDownloader"
|
8
|
+
spec.version = MoodleQuizDownloader::VERSION
|
9
|
+
spec.authors = ["Dr Blinken"]
|
10
|
+
spec.email = ["drblinken@gmail.com"]
|
11
|
+
spec.description = %q{Downloads Quizzes from Moodle as PDF}
|
12
|
+
spec.summary = %q{This is a little ruby command line tool that hooks to a moodle server and downloads all attempts for an exam as pdfs.}
|
13
|
+
spec.homepage = "https://github.com/drblinken/moodleQuizDownloader"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
|
22
|
+
spec.add_dependency "asciify"
|
23
|
+
spec.add_dependency "iconv" #this is a dependency of asciify
|
24
|
+
spec.add_dependency 'mechanize'
|
25
|
+
spec.add_dependency 'pdfkit'
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
spec.add_development_dependency "rspec"
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#
|
2
|
+
# rspec to test usage of pdfkit in this script
|
3
|
+
#
|
4
|
+
# installation
|
5
|
+
# gem 'pdfkit'
|
6
|
+
# brew install wkhtmltopdf
|
7
|
+
# don't just install the gem, this didn't work as described in
|
8
|
+
# https://github.com/pdfkit/pdfkit/wiki/Installing-WKHTMLTOPDF
|
9
|
+
# https://github.com/pdfkit/pdfkit/wiki/
|
10
|
+
# http://code.google.com/p/wkhtmltopdf/
|
11
|
+
# https://github.com/pdfkit/pdfkit
|
12
|
+
require 'pdfkit'
|
13
|
+
describe 'pdfkit api' do
|
14
|
+
it "should convert a html page to pdf" do
|
15
|
+
html = '<body>Hallu</body>'
|
16
|
+
kit = PDFKit.new(html)
|
17
|
+
# >> k.to_file('simple.pdf')
|
18
|
+
pdf = kit.to_pdf
|
19
|
+
pdf.should match /^%PDF-/
|
20
|
+
end
|
21
|
+
end
|