add group_category importer
fixes CORE-654 test plan - rake doc:api - sis docs should generate - group category importer should work Change-Id: I015692ae5795f1dec5da291fe84330185bd7b3c7 Reviewed-on: https://gerrit.instructure.com/137878 Reviewed-by: Cody Cutrer <cody@instructure.com> Tested-by: Jenkins QA-Review: Tucker McKnight <tmcknight@instructure.com> Product-Review: Rob Orton <rob@instructure.com>
This commit is contained in:
parent
33c7ecc1ab
commit
c0aa33d989
|
@ -28,6 +28,7 @@
|
|||
<li><%= before_label(t(:enrollments_label, "Enrollments")) %> <%= counts[:enrollments] %></li>
|
||||
<li><%= before_label(t(:crosslists_label, "Crosslists")) %> <%= counts[:xlists] %></li>
|
||||
<li><%= before_label(t("Admins")) %> <%= counts[:admins] %></li>
|
||||
<li><%= before_label(t("Group Categories")) %> <%= counts[:group_categories] %></li>
|
||||
<li><%= before_label(t(:groups, "Groups")) %> <%= counts[:groups] %></li>
|
||||
<li><%= before_label(t(:group_enrollments, "Group Enrollments")) %> <%= counts[:group_memberships] %></li>
|
||||
<li><%= before_label(t("User Observers")) %> <%= counts[:user_observers] %></li>
|
||||
|
|
|
@ -734,6 +734,57 @@ E411208,13834,student,2A,active
|
|||
E411208,13aa3,teacher,2A,active
|
||||
</pre>
|
||||
|
||||
group_categories.csv
|
||||
------------
|
||||
|
||||
<table class="sis_csv">
|
||||
<tr>
|
||||
<th>Field Name</th>
|
||||
<th>Data Type</th>
|
||||
<th>Required</th>
|
||||
<th>Sticky</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>group_category_id</td>
|
||||
<td>text</td>
|
||||
<td>✓</td>
|
||||
<td></td>
|
||||
<td>A unique identifier used to reference a group category.
|
||||
This identifier must not change for the group category, and must be globally unique.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>account_id</td>
|
||||
<td>text</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>The account identifier from accounts.csv, if none is specified the group
|
||||
will be attached to the root account.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>category_name</td>
|
||||
<td>text</td>
|
||||
<td>✓</td>
|
||||
<td></td>
|
||||
<td>The name of the group category.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>status</td>
|
||||
<td>enum</td>
|
||||
<td>✓</td>
|
||||
<td></td>
|
||||
<td>active, deleted</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Sample:
|
||||
|
||||
<pre>group_category_id,account_id,category_name,status
|
||||
GC08,A001,First Group Category,active
|
||||
GC07,,GC7,active
|
||||
GC10,,GC10,deleted
|
||||
</pre>
|
||||
|
||||
groups.csv
|
||||
------------
|
||||
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
#
|
||||
# Copyright (C) 2018 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module SIS
|
||||
module CSV
|
||||
# note these are account-level group categories, not course groups
|
||||
class GroupCategoryImporter < CSVBaseImporter
|
||||
def self.group_category_csv?(row)
|
||||
row.include?('group_category_id') && row.include?('category_name')
|
||||
end
|
||||
|
||||
def self.identifying_fields
|
||||
%w[group_id].freeze
|
||||
end
|
||||
|
||||
# expected columns
|
||||
# group_category_id, account_id, name, status
|
||||
def process(csv)
|
||||
@sis.counts[:group_categories] += SIS::GroupCategoryImporter.new(@root_account, importer_opts).process do |importer|
|
||||
csv_rows(csv) do |row|
|
||||
update_progress
|
||||
|
||||
begin
|
||||
importer.add_group_category(row['group_category_id'], row['account_id'], row['category_name'], row['status'])
|
||||
rescue ImportError => e
|
||||
add_warning(csv, "#{e}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -35,7 +35,7 @@ module SIS
|
|||
# * Course and Section must be imported before Xlist
|
||||
# * Course, Section, and User must be imported before Enrollment
|
||||
IMPORTERS = %i{change_sis_id account term abstract_course course section
|
||||
xlist user enrollment admin group group_membership
|
||||
xlist user enrollment admin group_category group group_membership
|
||||
grade_publishing_results user_observer}.freeze
|
||||
|
||||
def initialize(root_account, opts = {})
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
#
|
||||
# Copyright (C) 2018 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module SIS
|
||||
class GroupCategoryImporter < BaseImporter
|
||||
|
||||
def process
|
||||
start = Time.now
|
||||
importer = Work.new(@batch, @root_account, @logger)
|
||||
yield importer
|
||||
@logger.debug("Group categories took #{Time.now - start} seconds")
|
||||
return importer.success_count
|
||||
end
|
||||
|
||||
private
|
||||
class Work
|
||||
attr_accessor :success_count
|
||||
|
||||
def initialize(batch, root_account, logger)
|
||||
@batch = batch
|
||||
@root_account = root_account
|
||||
@logger = logger
|
||||
@success_count = 0
|
||||
@accounts_cache = {}
|
||||
end
|
||||
|
||||
def add_group_category(sis_id, account_id, category_name, status)
|
||||
raise ImportError, "No sis_id given for a group category" if sis_id.blank?
|
||||
raise ImportError, "No name given for group category #{sis_id}" if category_name.blank?
|
||||
raise ImportError, "No status given for group category #{sis_id}" if status.blank?
|
||||
raise ImportError, "Improper status \"#{status}\" for group category #{sis_id}, skipping" unless status =~ /\A(active|deleted)/i
|
||||
|
||||
@logger.debug("Processing Group Category #{[sis_id, account_id, category_name, status].inspect}")
|
||||
|
||||
account = nil
|
||||
if account_id.present?
|
||||
account = @accounts_cache[account_id]
|
||||
account ||= @root_account.all_accounts.where(sis_source_id: account_id).take
|
||||
raise ImportError, "Account with id \"#{account_id}\" didn't exist for group category #{sis_id}" unless account
|
||||
@accounts_cache[account.sis_source_id] = account
|
||||
end
|
||||
account ||= @root_account
|
||||
|
||||
gc = @root_account.all_group_categories.where(sis_source_id: sis_id).take
|
||||
gc ||= account.group_categories.new
|
||||
gc.name = category_name
|
||||
gc.context = account
|
||||
gc.root_account_id = @root_account.id
|
||||
gc.sis_source_id = sis_id
|
||||
gc.sis_batch_id = @batch.id if @batch
|
||||
|
||||
case status
|
||||
when /active/i
|
||||
gc.deleted_at = nil
|
||||
when /deleted/i
|
||||
gc.deleted_at = Time.zone.now
|
||||
end
|
||||
|
||||
if gc.save
|
||||
@success_count += 1
|
||||
else
|
||||
msg = "A group category did not pass validation (group category: #{sis_id}, error: "
|
||||
msg += gc.errors.full_messages.join(",") + ")"
|
||||
raise ImportError, msg
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -150,6 +150,7 @@ describe SisImportsApiController, type: :request do
|
|||
"users" => 1,
|
||||
"user_observers" => 0,
|
||||
"xlists" => 0,
|
||||
"group_categories" => 0,
|
||||
"groups" => 0,
|
||||
"group_memberships" => 0,
|
||||
"terms" => 0, }},
|
||||
|
@ -664,6 +665,7 @@ describe SisImportsApiController, type: :request do
|
|||
"users" => 0,
|
||||
"user_observers" => 0,
|
||||
"xlists" => 0,
|
||||
"group_categories" => 0,
|
||||
"groups" => 0,
|
||||
"group_memberships" => 0,
|
||||
"terms" => 0, }},
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
#
|
||||
# Copyright (C) 2018 - present Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb')
|
||||
|
||||
describe SIS::CSV::GroupCategoryImporter do
|
||||
|
||||
before(:once) do
|
||||
account_model
|
||||
process_csv_data_cleanly(
|
||||
"account_id,parent_account_id,name,status",
|
||||
"A001,,TestAccount,active"
|
||||
)
|
||||
end
|
||||
|
||||
it "should skip bad content" do
|
||||
before_count = GroupCategory.count
|
||||
importer = process_csv_data(
|
||||
"group_category_id,account_id,category_name,status",
|
||||
"GC001,A001,Group Cat 1,active",
|
||||
"Gc002,A001,Group Cat 2,blerged",
|
||||
"Gc003,A001,,active",
|
||||
"Gc004,invalid,Group Cat 4,active",
|
||||
",A001,G1,active")
|
||||
expect(importer.errors).to eq []
|
||||
expect(importer.warnings.map(&:last)).to eq(
|
||||
["Improper status \"blerged\" for group category Gc002, skipping",
|
||||
"No name given for group category Gc003",
|
||||
"Account with id \"invalid\" didn't exist for group category Gc004",
|
||||
"No sis_id given for a group category"]
|
||||
)
|
||||
expect(GroupCategory.count).to eq before_count + 1
|
||||
end
|
||||
|
||||
it "should create group categories" do
|
||||
@sub = Account.where(sis_source_id: 'A001').take
|
||||
process_csv_data_cleanly(
|
||||
"group_category_id,account_id,category_name,status",
|
||||
"Gc001,,Group Cat 1,active",
|
||||
"Gc002,A001,Group Cat 2,active")
|
||||
group_category = GroupCategory.where(sis_source_id: 'Gc001').take
|
||||
expect(group_category.context_id).to eq @account.id
|
||||
expect(group_category.sis_source_id).to eq 'Gc001'
|
||||
expect(group_category.name).to eq "Group Cat 1"
|
||||
expect(group_category.deleted_at).to be_nil
|
||||
group_category2 = GroupCategory.where(sis_source_id: 'Gc002').take
|
||||
expect(group_category2.context_id).to eq @sub.id
|
||||
end
|
||||
|
||||
it "should allow moving group categories" do
|
||||
@sub = Account.where(sis_source_id: 'A001').take
|
||||
process_csv_data_cleanly(
|
||||
"group_category_id,account_id,category_name,status",
|
||||
"Gc001,,Group Cat 1,active",
|
||||
"Gc002,A001,Group Cat 2,active")
|
||||
group_category = GroupCategory.where(sis_source_id: 'Gc001').take
|
||||
expect(group_category.context_id).to eq @account.id
|
||||
group_category2 = GroupCategory.where(sis_source_id: 'Gc002').take
|
||||
expect(group_category2.context_id).to eq @sub.id
|
||||
|
||||
process_csv_data_cleanly(
|
||||
"group_category_id,account_id,category_name,status",
|
||||
"Gc001,A001,Group Cat 1,active",
|
||||
"Gc002,,Group Cat 2,active")
|
||||
expect(group_category.reload.context_id).to eq @sub.id
|
||||
expect(group_category2.reload.context_id).to eq @account.id
|
||||
end
|
||||
|
||||
it "should fail model validations" do
|
||||
@sub = Account.where(sis_source_id: 'A001').take
|
||||
importer = process_csv_data(
|
||||
"group_category_id,account_id,category_name,status",
|
||||
"Gc001,,Group Cat 1,active",
|
||||
"Gc002,,Group Cat 1,active")
|
||||
expect(importer.errors).to eq []
|
||||
expect(importer.warnings.map(&:last)).to eq(["A group category did not pass validation (group category: Gc002, error: Name Group Cat 1 is already in use.)"])
|
||||
end
|
||||
|
||||
it "should delete and restore group categories" do
|
||||
process_csv_data_cleanly(
|
||||
"group_category_id,account_id,category_name,status",
|
||||
"Gc001,,Group Cat 1,active",
|
||||
"Gc002,A001,Group Cat 2,deleted")
|
||||
group_category= GroupCategory.where(sis_source_id: 'Gc001').take
|
||||
expect(group_category.deleted_at).to be_nil
|
||||
group_category2= GroupCategory.where(sis_source_id: 'Gc002').take
|
||||
expect(group_category2.deleted_at).to_not be_nil
|
||||
|
||||
process_csv_data_cleanly(
|
||||
"group_category_id,account_id,category_name,status",
|
||||
"Gc001,,Group Cat 1,deleted",
|
||||
"Gc002,A001,Group Cat 2,active")
|
||||
expect(group_category.reload.deleted_at).to_not be_nil
|
||||
expect(group_category2.reload.deleted_at).to be_nil
|
||||
end
|
||||
end
|
|
@ -23,14 +23,16 @@ describe "accounts/_sis_batch_counts.html.erb" do
|
|||
|
||||
it "should render sis count data" do
|
||||
data = {counts: {xlists: 2, enrollments: 3, courses: 5, users: 6, terms: 6,
|
||||
group_memberships: 7, groups: 8, sections: 9, accounts: 10,
|
||||
admins: 1, user_observers: 3, change_sis_id: 0}}
|
||||
group_memberships: 7, group_categories: 2, groups: 8,
|
||||
sections: 9, accounts: 10, admins: 1, user_observers: 3,
|
||||
change_sis_id: 0}}
|
||||
report = double()
|
||||
expect(report).to receive(:data).and_return(data)
|
||||
render :partial => 'accounts/sis_batch_counts', :object => report
|
||||
|
||||
map = {xlists: "Crosslists", group_memberships: "Group Enrollments",
|
||||
user_observers: "User Observers", change_sis_id: "Change SIS IDs"}
|
||||
user_observers: "User Observers", change_sis_id: "Change SIS IDs",
|
||||
group_categories: "Group Categories",}
|
||||
|
||||
data[:counts].each_pair do |type, count|
|
||||
name = map[type] || type.to_s.capitalize
|
||||
|
|
Loading…
Reference in New Issue