sakai-info 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,7 @@
2
2
  # - sakai-info command line tool support
3
3
  #
4
4
  # Created 2012-02-19 daveadams@gmail.com
5
- # Last updated 2012-02-19 daveadams@gmail.com
5
+ # Last updated 2012-02-23 daveadams@gmail.com
6
6
  #
7
7
  # https://github.com/daveadams/sakai-info
8
8
  #
@@ -11,25 +11,3 @@
11
11
 
12
12
  require 'sakai-info/cli/help'
13
13
 
14
- module SakaiInfo
15
- class CLI
16
- def self.validate_config
17
- return true if Configuration.configured?
18
-
19
- begin
20
- Configuration.load_config
21
- return true
22
- rescue NoConfigFoundException
23
- STDERR.puts "ERROR: No configuration file was found at #{Configuration::DEFAULT_CONFIG_FILE}"
24
- return false
25
- rescue InvalidConfigException => e
26
- STDERR.puts "ERROR: Configuration was invalid:"
27
- e.message.each_line do |line|
28
- STDERR.puts " #{line.chomp}"
29
- end
30
- return false
31
- end
32
- end
33
- end
34
- end
35
-
@@ -16,73 +16,83 @@ module SakaiInfo
16
16
  :default => <<EOF,
17
17
  sakai-info #{VERSION}
18
18
 
19
- Object commands:
20
- user Print information about a user or users
21
- site Print information about a site or sites
19
+ Usage: sakai-info <command> [<id>] [<options>]
22
20
 
23
- Misc commands:
24
- validate Validates configuration
25
- help Prints general help
26
- version Prints version
21
+ Object commands:
22
+ user Print information about a user or users
23
+ site Print information about a site or sites
27
24
 
28
- Type 'sakai-info help <command>' for help on a specific command.
25
+ Misc commands:
26
+ test Tests configured database connections
27
+ help Prints general help
28
+ version Prints version
29
+
30
+ Options that apply to most commands:
31
+ -D <name>
32
+ --database=<name>
33
+ Connect to database instance <name> as defined in ~/.sakai-info instead
34
+ of the default (which is typically the first entry)
35
+
36
+ Type 'sakai-info help <command>' for help on a specific command.
29
37
  EOF
30
38
 
31
39
  "help" => <<EOF,
32
40
  sakai-info help
33
41
 
42
+ Usage: sakai-info help [<command>]
43
+
34
44
  Prints usage information for other sakai-info commands, or without an
35
45
  argument it prints a list of possible commands.
36
-
37
- Usage: sakai-info help [<command>]
38
46
  EOF
39
47
 
40
48
  "version" => <<EOF,
41
49
  sakai-info version
42
50
 
43
- Prints the current version of sakai-info.
44
-
45
51
  Usage: sakai-info version
46
- EOF
47
-
48
- "validate" => <<EOF,
49
- sakai-info validate
50
52
 
51
- Reads and validates the current configuration format. To test the actual
52
- database connections, use 'sakai-info test'.
53
-
54
- Usage: sakai-info validate
53
+ Prints the current version of sakai-info.
55
54
  EOF
56
55
 
57
56
  "test" => <<EOF,
58
57
  sakai-info test
59
58
 
60
- [NOT YET IMPLEMENTED]
59
+ Usage: sakai-info test [<options>]
61
60
 
62
- Reads configuration and tests connecting to each database specified, or with
63
- an argument, it will test only the named instance.
64
-
65
- Usage: sakai-info test [<instance>]
61
+ Reads configuration and tests connecting to each database specified, or if
62
+ a specific database is specified it will test only that connection.
66
63
  EOF
67
64
 
68
65
  "user" => <<EOF,
69
66
  sakai-info user
70
67
 
71
- In this release, this command prints the total number of records in the
72
- SAKAI_USER table. In future releases, subcommands will be available to
73
- perform more tasks.
68
+ Usage: sakai-info user <id> [<options>]
69
+
70
+ Prints information about the user ID or EID specified. Additional options
71
+ may be passed to include additional information:
74
72
 
75
- Usage: sakai-info user [<subcommand>]
73
+ --sites Print site membership information
74
+ --pools Print list of owned question pools
75
+ --all Print all possible details
76
76
  EOF
77
77
 
78
78
  "site" => <<EOF,
79
79
  sakai-info site
80
80
 
81
- In this release, this command prints the total number of records in the
82
- SAKAI_SITE table. In future releases, subcommands will be available to
83
- perform more tasks.
84
-
85
- Usage: sakai-info site [<subcommand>]
81
+ Usage: sakai-info site <id> [<options>]
82
+
83
+ Prints information about the site ID specified. Additional options may be
84
+ passed to include additional information:
85
+
86
+ --users Print membership information
87
+ --pages Print page list with tools
88
+ --groups Print group information
89
+ --quizzes Print information about quizzes
90
+ --disk Print disk usage
91
+ --assignments Print assignment info
92
+ --gradebook Print gradebook item info
93
+ --realm Print site realm details
94
+ --forums Print forum details
95
+ --all Print all possible details
86
96
  EOF
87
97
  }
88
98
 
@@ -93,7 +103,7 @@ EOF
93
103
  else
94
104
  STDERR.puts "ERROR: help topic '#{topic}' was unrecognized"
95
105
  STDERR.puts
96
- CLI.help(:default, STDERR)
106
+ CLI::Help.help(:default, STDERR)
97
107
  exit 1
98
108
  end
99
109
  end
@@ -2,7 +2,7 @@
2
2
  # SakaiInfo::Content library
3
3
  #
4
4
  # Created 2012-02-17 daveadams@gmail.com
5
- # Last updated 2012-02-18 daveadams@gmail.com
5
+ # Last updated 2012-02-24 daveadams@gmail.com
6
6
  #
7
7
  # https://github.com/daveadams/sakai-info
8
8
  #
@@ -39,10 +39,8 @@ module SakaiInfo
39
39
 
40
40
  def binary
41
41
  if @binary.nil?
42
- DB.connect.exec("select binary_entity from #{@table_name} " +
43
- "where #{@id_column} = :id", id) do |row|
44
- @binary = row[0].read
45
- end
42
+ row = DB.connect[@table_name.to_sym].filter(@id_column.to_sym => @id).first
43
+ @binary = row[:binary_entity].read
46
44
  end
47
45
  @binary
48
46
  end
@@ -104,15 +102,11 @@ module SakaiInfo
104
102
  @@cache = {}
105
103
  def self.find(id)
106
104
  if @@cache[id].nil?
107
- DB.connect.exec("select resource_id, in_collection, file_path, " +
108
- "resource_uuid, file_size, context, resource_type_id " +
109
- "from content_resource " +
110
- "where resource_id=:id", id) do |row|
111
- @@cache[id] = ContentResource.new(row[0], row[1], row[2], row[3], row[4].to_i, row[5], row[6])
112
- end
113
- if @@cache[id].nil?
105
+ row = DB.connect[:content_resource].filter(:resource_id => id).first
106
+ if row.nil?
114
107
  raise ObjectNotFoundException.new(ContentResource, id)
115
108
  end
109
+ @@cache[id] = ContentResource.new(row[:resource_id], row[:in_collection], row[:file_path], row[:resource_uuid], row[:file_size].to_i, row[:context], row[:resource_type_id])
116
110
  end
117
111
  @@cache[id]
118
112
  end
@@ -136,23 +130,18 @@ module SakaiInfo
136
130
 
137
131
  def self.find_by_parent(parent_id)
138
132
  resources = []
139
- DB.connect.exec("select resource_id, in_collection, file_path, " +
140
- "resource_uuid, file_size, context, resource_type_id " +
141
- "from content_resource " +
142
- "where in_collection=:parent_id", parent_id) do |row|
143
- @@cache[row[0]] = ContentResource.new(row[0], row[1], row[2], row[3], row[4].to_i, row[5], row[6])
133
+ DB.connect[:content_resource].filter(:in_collection => parent_id) do |row|
134
+ @@cache[row[:resource_id]] =
135
+ ContentResource.new(row[:resource_id], row[:in_collection], row[:file_path],
136
+ row[:resource_uuid], row[:file_size].to_i, row[:context],
137
+ row[:resource_type_id])
144
138
  resources << @@cache[row[0]]
145
139
  end
146
140
  resources
147
141
  end
148
142
 
149
143
  def self.count_by_parent(parent_id)
150
- count = 0
151
- DB.connect.exec("select count(*) from content_resource " +
152
- "where in_collection=:parent_id", parent_id) do |row|
153
- count = row[0].to_i
154
- end
155
- count
144
+ DB.connect[:content_resource].filter(:in_collection => parent_id).count
156
145
  end
157
146
  end
158
147
 
@@ -172,13 +161,11 @@ module SakaiInfo
172
161
  end
173
162
 
174
163
  if @@cache[id].nil?
175
- DB.connect.exec("select collection_id, in_collection from content_collection " +
176
- "where collection_id=:id", id) do |row|
177
- @@cache[id] = ContentCollection.new(row[0], row[1])
178
- end
179
- if @@cache[id].nil?
164
+ row = DB.connect[:content_collection].filter(:collection_id => id).first
165
+ if row.nil?
180
166
  raise ObjectNotFoundException.new(ContentCollection, id)
181
167
  end
168
+ @@cache[id] = ContentCollection.new(row[:collection_id], row[:in_collection])
182
169
  end
183
170
  @@cache[id]
184
171
  end
@@ -194,31 +181,23 @@ module SakaiInfo
194
181
 
195
182
  def self.find_portfolio_interaction_collections
196
183
  collections = []
197
- DB.connect.exec("select collection_id, in_collection from content_collection " +
198
- "where collection_id like '%/portfolio-interaction/'") do |row|
199
- @@cache[row[0]] = ContentCollection.new(row[0], row[1])
200
- collections << @@cache[row[0]]
184
+ DB.connect[:content_collection].
185
+ where(:collection_id.like('%/portfolio-interaction/')) do |row|
186
+ @@cache[row[:collection_id]] =
187
+ ContentCollection.new(row[:collection_id], row[:in_collection])
188
+ collections << @@cache[row[:collection_id]]
201
189
  end
202
190
  collections
203
191
  end
204
192
 
205
193
  def self.count_by_parent(parent_id)
206
- count = 0
207
- DB.connect.exec("select count(*) from content_collection " +
208
- "where in_collection=:parent_id", parent_id) do |row|
209
- count = row[0].to_i
210
- end
211
- count
194
+ DB.connect[:content_collection].filter(:in_collection => parent_id).count
212
195
  end
213
196
 
214
197
  def size_on_disk
215
- if @size_on_disk.nil?
216
- DB.connect.exec("select sum(file_size) from content_resource " +
217
- "where resource_id like :collmatch", (@id + "%")) do |row|
218
- @size_on_disk = row[0].to_i
219
- end
220
- end
221
- @size_on_disk
198
+ @size_on_disk ||=
199
+ DB.connect[:content_resource].select(:sum.sql_function(:file_size).as(:total_size)).
200
+ where(:resource_id.like("#{@id}%")).first[:total_size].to_i
222
201
  end
223
202
 
224
203
  def children
@@ -278,10 +257,10 @@ module SakaiInfo
278
257
 
279
258
  def self.find_by_parent(parent_id)
280
259
  collections = []
281
- DB.connect.exec("select collection_id, in_collection from content_collection " +
282
- "where in_collection = :parent_id", parent_id) do |row|
283
- @@cache[row[0]] = ContentCollection.new(row[0], row[1])
284
- collections << @@cache[row[0]]
260
+ DB.connect[:content_collection].filter(:in_collection => parent_id) do |row|
261
+ @@cache[row[:collection_id]] =
262
+ ContentCollection.new(row[:collection_id], row[:in_collection])
263
+ collections << @@cache[row[:collection_id]]
285
264
  end
286
265
  collections
287
266
  end
@@ -0,0 +1,114 @@
1
+ # sakai-info/database.rb
2
+ # SakaiInfo::Database library
3
+ #
4
+ # Created 2012-02-19 daveadams@gmail.com
5
+ # Last updated 2012-02-24 daveadams@gmail.com
6
+ #
7
+ # https://github.com/daveadams/sakai-info
8
+ #
9
+ # This software is public domain.
10
+ #
11
+
12
+ # DATABASE CONFIG FORMAT
13
+ #
14
+ # yaml file with lines of "nickname: sequel-connection-string", eg:
15
+ #
16
+ # prod: oracle://user:pass@sid
17
+ # test: oracle://user:pass@sid/schema_name
18
+ # dev: mysql://user:pass@hostname/db_name
19
+ #
20
+ # The default connection is the first one in the file.
21
+ # For more on Sequel connection strings, see:
22
+ # http://sequel.rubyforge.org/rdoc/files/doc/opening_databases_rdoc.html
23
+ #
24
+
25
+ # using the oci8 driver will complain if NLS_LANG is not set in the environment
26
+ ENV["NLS_LANG"] ||= "AMERICAN_AMERICA.UTF8"
27
+
28
+ module SakaiInfo
29
+ class InvalidConfigException < SakaiException; end
30
+ class ConnectionFailureException < SakaiException; end
31
+
32
+ class DB
33
+ DEFAULT_CONFIG_FILE = File.expand_path("~/.sakai-info")
34
+ @@default_database_name = nil
35
+ @@config = nil
36
+
37
+ def self.configure(config)
38
+ begin
39
+ if config.is_a? Hash
40
+ @@config = config
41
+ elsif config.is_a? String and File.exist?(config)
42
+ # try to parse as a filename first
43
+ if File.exist?(config)
44
+ @@config = YAML::load_file(config)
45
+ end
46
+ else
47
+ # otherwise try to parse it generically
48
+ @@config = YAML::load(config)
49
+ end
50
+ rescue Exception => e
51
+ raise InvalidConfigException.new("Unable to parse configuration: #{e}")
52
+ end
53
+ end
54
+
55
+ def self.load_config
56
+ if File.readable? DEFAULT_CONFIG_FILE
57
+ DB.configure(DEFAULT_CONFIG_FILE)
58
+ else
59
+ raise MissingConfigException.new("No config file found at #{DEFAULT_CONFIG_FILE}")
60
+ end
61
+ end
62
+
63
+ def self.databases
64
+ @@config
65
+ end
66
+
67
+ @@databases = {}
68
+ def self.connect(database_name = :default)
69
+ if @@config.nil?
70
+ DB.load_config
71
+ end
72
+ if @@databases[database_name].nil?
73
+ @@databases[database_name] =
74
+ Database.new(if database_name == :default
75
+ if @@default_database_name.nil?
76
+ @@config[@@config.keys.first]
77
+ else
78
+ @@config[@@default_database_name]
79
+ end
80
+ else
81
+ @@config[database_name]
82
+ end)
83
+ end
84
+ @@databases[database_name].connect
85
+ end
86
+
87
+ def self.default_database=(database_name)
88
+ @@default_database_name = database_name
89
+ end
90
+ end
91
+
92
+ class Database
93
+ def initialize(connection_string)
94
+ @connection_string = connection_string.to_s
95
+ end
96
+
97
+ def connect
98
+ if @connection and self.alive?
99
+ return @connection
100
+ end
101
+
102
+ begin
103
+ @connection = Sequel.connect(@connection_string)
104
+ rescue => e
105
+ @connection = nil
106
+ raise ConnectionFailureException.new("Could not connect: #{e}")
107
+ end
108
+ end
109
+
110
+ def alive?
111
+ @connection.nil? && @connection.test_connection
112
+ end
113
+ end
114
+ end
@@ -2,7 +2,7 @@
2
2
  # SakaiInfo::Gradebook library
3
3
  #
4
4
  # Created 2012-02-17 daveadams@gmail.com
5
- # Last updated 2012-02-18 daveadams@gmail.com
5
+ # Last updated 2012-02-24 daveadams@gmail.com
6
6
  #
7
7
  # https://github.com/daveadams/sakai-info
8
8
  #
@@ -24,17 +24,16 @@ module SakaiInfo
24
24
  @@cache_by_site_id = {}
25
25
  def self.find(id)
26
26
  if @@cache[id].nil?
27
- version = site = name = nil
28
- DB.connect.exec("select version, gradebook_uid, name " +
29
- "from gb_gradebook_t " +
30
- "where id = :id", id) do |row|
31
- version = row[0].to_i
32
- site = Site.find(row[1])
33
- name = row[2]
34
- end
35
- if version.nil?
27
+ row = DB.connect.fetch("select version, gradebook_uid, name " +
28
+ "from gb_gradebook_t " +
29
+ "where id = :id", id).first
30
+ if row.nil?
36
31
  raise ObjectNotFoundException.new(Gradebook, id)
37
32
  end
33
+
34
+ version = row[:version].to_i
35
+ site = Site.find(row[:gradebook_uid])
36
+ name = row[:name]
38
37
  @@cache[id] = Gradebook.new(id, version, site, name)
39
38
  @@cache_by_site_id[site.id] = @@cache[id]
40
39
  end
@@ -43,18 +42,17 @@ module SakaiInfo
43
42
 
44
43
  def self.find_by_site_id(site_id)
45
44
  if @@cache_by_site_id[site_id].nil?
46
- id = version = site = name = nil
47
- DB.connect.exec("select id, version, name " +
48
- "from gb_gradebook_t " +
49
- "where gradebook_uid = :site_id", site_id) do |row|
50
- id = row[0].to_i
51
- version = row[1].to_i
52
- name = row[2]
53
- site = Site.find(site_id)
54
- end
55
- if version.nil?
45
+ row = DB.connect.fetch("select id, version, name " +
46
+ "from gb_gradebook_t " +
47
+ "where gradebook_uid = ?", site_id).first
48
+ if row.nil?
56
49
  raise ObjectNotFoundException.new(Gradebook, site_id)
57
50
  end
51
+
52
+ id = row[:id].to_i
53
+ version = row[:version].to_i
54
+ name = row[:name]
55
+ site = Site.find(site_id)
58
56
  @@cache[id] = Gradebook.new(id, version, site, name)
59
57
  @@cache_by_site_id[site_id] = @@cache[id]
60
58
  end
@@ -89,24 +87,24 @@ module SakaiInfo
89
87
  @@cache = {}
90
88
  def self.find(id)
91
89
  if @@cache[id].nil?
92
- gradebook = object_type = version = name = points_possible = due_date = weight = nil
93
- DB.connect.exec("select gradebook_id, object_type_id, version, " +
94
- "name, points_possible, " +
95
- "to_char(due_date, 'YYYY-MM-DD'), " +
96
- "assignment_weighting " +
97
- "from gb_gradable_object_t " +
98
- "where id = :id", id) do |row|
99
- gradebook = Gradebook.find(row[0].to_i)
100
- object_type = row[1].to_i
101
- version = row[2].to_i
102
- name = row[3]
103
- points_possible = row[4].to_f
104
- due_date = row[5]
105
- weight = row[6].to_f
106
- end
107
- if version.nil?
90
+ row = DB.connect.fetch("select gradebook_id, object_type_id, version, " +
91
+ "name, points_possible, " +
92
+ "to_char(due_date, 'YYYY-MM-DD') as due, " +
93
+ "assignment_weighting " +
94
+ "from gb_gradable_object_t " +
95
+ "where id = ?", id).first
96
+
97
+ if row.nil?
108
98
  raise ObjectNotFoundException.new(GradableObject, id)
109
99
  end
100
+
101
+ gradebook = Gradebook.find(row[:gradebook_id].to_i)
102
+ object_type = row[:object_type_id].to_i
103
+ version = row[:version].to_i
104
+ name = row[:name]
105
+ points_possible = row[:points_possible].to_f
106
+ due_date = row[:due]
107
+ weight = row[:assignment_weighting].to_f
110
108
  @@cache[id] = GradableObject.new(id, gradebook, object_type,
111
109
  version, name, points_possible,
112
110
  due_date, weight)
@@ -119,20 +117,20 @@ module SakaiInfo
119
117
  if @@cache_by_gradebook_id[gradebook_id].nil?
120
118
  objects = []
121
119
  gradebook = Gradebook.find(gradebook_id)
122
- DB.connect.exec("select id, object_type_id, version, " +
123
- "name, points_possible, " +
124
- "to_char(due_date, 'YYYY-MM-DD'), " +
125
- "assignment_weighting " +
126
- "from gb_gradable_object_t " +
127
- "where gradebook_id = :gradebook_id " +
128
- "order by due_date asc", gradebook_id) do |row|
129
- id = row[0].to_i
130
- object_type = row[1].to_i
131
- version = row[2].to_i
132
- name = row[3]
133
- points_possible = row[4].to_f
134
- due_date = row[5]
135
- weight = row[6].to_f
120
+ DB.connect.fetch("select id, object_type_id, version, " +
121
+ "name, points_possible, " +
122
+ "to_char(due_date, 'YYYY-MM-DD') as due, " +
123
+ "assignment_weighting " +
124
+ "from gb_gradable_object_t " +
125
+ "where gradebook_id = ? " +
126
+ "order by due_date asc", gradebook_id) do |row|
127
+ id = row[:id].to_i
128
+ object_type = row[:object_type_id].to_i
129
+ version = row[:version].to_i
130
+ name = row[:name]
131
+ points_possible = row[:points_possible].to_f
132
+ due_date = row[:due]
133
+ weight = row[:assignment_weighting].to_f
136
134
  objects << GradableObject.new(id, gradebook, object_type,
137
135
  version, name, points_possible,
138
136
  due_date, weight)