sakai-info 0.1.0 → 0.2.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.
@@ -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)