Skip to content

Commit

Permalink
Add a first version for the copy course service, and basic specs for it
Browse files Browse the repository at this point in the history
  • Loading branch information
gabina committed Nov 7, 2023
1 parent ec400de commit fa0fb2d
Show file tree
Hide file tree
Showing 2 changed files with 296 additions and 0 deletions.
100 changes: 100 additions & 0 deletions app/services/copy_course.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# frozen_string_literal: true

#= Copy course from another server dashboard
class CopyCourse
def initialize(url:)
@url = url
end

def make_copy
@course_data = retrieve_course_data
copy_main_course_data
add_tracked_wikis
@cat_data = retrieve_categories_data
copy_tracked_categories_data
@users_data = retrieve_users_data
copy_users_data
return { course: @course, error: nil }
rescue StandardError => e
return { course: nil, error: e.message }
rescue ActiveRecord::RecordInvalid => e
return { course: nil, error: "Error copying data to db: #{e.message}" }
end

private

def copy_main_course_data
# Extract the attributes we want to copy
params_to_copy = %w[school title term description start end subject slug timeline_start
timeline_end type flags]
copied_data = {}
params_to_copy.each { |p| copied_data[p] = @course_data[p] }
@home_wiki = Wiki.get_or_create(language: @course_data['home_wiki']['language'],
project: @course_data['home_wiki']['project'])
copied_data['home_wiki_id'] = @home_wiki.id
copied_data['passcode'] = 'passcode' # set an arbitrary passcode
if copied_data['flags'].key?('update_logs')
copied_data['flags']['update_logs'] =
fix_update_logs_parsing(copied_data['flags']['update_logs'])
end
# Create the course
@course = Course.create!(copied_data)
end

# When parsing update_logs from flags, keys are set as strings instead of integers
# This causes problems, so we need to force the keys to be integers.
def fix_update_logs_parsing(update_logs)
update_logs.transform_keys(&:to_i)
end

def add_tracked_wikis
@course_data['wikis'].each do |wiki_hash|
wiki = Wiki.get_or_create(language: wiki_hash['language'], project: wiki_hash['project'])
next if wiki.id == @home_wiki.id # home wiki was automatically added already
@course.wikis << wiki
end
end

def copy_tracked_categories_data
@cat_data.each do |cat_hash|
wiki = Wiki.get_or_create(language: cat_hash['wiki']['language'],
project: cat_hash['wiki']['project'])
cat = Category.find_or_create_by!(
depth: cat_hash['depth'],
source: cat_hash['source'],
name: cat_hash['name'],
wiki:
)
@course.categories << cat
end
end

def copy_users_data
@users_data.each do |user_hash|
user = User.find_or_create_by!(username: user_hash['username'])
CoursesUsers.create!(user_id: user.id, role: user_hash['role'], course_id: @course.id)
end
end

def get_request(path)
uri = URI(@url + path)
response = Net::HTTP.get_response(uri)
raise "Error getting data from #{uri}" unless response.is_a?(Net::HTTPSuccess)
response
end

def retrieve_course_data
response = get_request('/course.json')
JSON.parse(response.body)['course']
end

def retrieve_categories_data
response = get_request('/categories.json')
JSON.parse(response.body)['course']['categories']
end

def retrieve_users_data
response = get_request('/users.json')
JSON.parse(response.body)['course']['users']
end
end
196 changes: 196 additions & 0 deletions spec/services/copy_course_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
# frozen_string_literal: true
require 'rails_helper'

describe CopyCourse do
let(:url_base) { 'https://dashboard.wikiedu.org/courses/' }
let(:existent_prod_course_slug) do
'University_of_South_Carolina/Invertebrate_Zoology_(Spring_2022)'
end
let(:course_url) { url_base + existent_prod_course_slug + '/course.json' }
let(:categories_url) { url_base + existent_prod_course_slug + '/categories.json' }
let(:users_url) { url_base + existent_prod_course_slug + '/users.json' }
let(:course_response_body) do
'{
"course": {
"id": 15907,
"slug": "University_of_South_Carolina/Invertebrate_Zoology_(Spring_2022)",
"school": "University of South Carolina",
"title": "Invertebrate Zoology",
"term": "Spring 2022",
"start": "2022-01-11T00:00:00.000Z",
"end": "2022-04-30T23:59:59.000Z",
"type": "ClassroomProgramCourse",
"home_wiki": {
"id": 1,
"language": "en",
"project": "wikipedia"
},
"flags": {
"update_logs": {
"28763": {
"start_time": "2022-05-26T16:13:42.177+00:00",
"end_time": "2022-05-26T16:13:46.648+00:00",
"sentry_tag_uuid": "4c0a1b87-da5a-4e82-8f40-3d560690cdb2",
"error_count": 0
}
}
},
"wikis": [
{
"language": "en",
"project": "wikipedia"
}
]
}
}'
end
let(:categories_response_body) do
'{
"course": {
"categories": [
{
"name": "Category 0",
"depth": 0,
"source": "Source 0",
"wiki": {
"id": 1,
"language": "en",
"project": "wikipedia"
}
}
]
}
}'
end
let(:users_response_body) do
'{
"course": {
"users": [
{
"role": 1,
"id": 28451264,
"username": "Joshua Stone"
},
{
"role": 4,
"id": 22694295,
"username": "Helaine (Wiki Ed)"
},
{
"role": 0,
"id": 28515697,
"username": "CharlieJ385"
},
{
"role": 0,
"id": 28515751,
"username": "Diqi Yan"
}
]
}
}'
end
let(:subject) do
service = described_class.new(url: url_base + existent_prod_course_slug)
service.make_copy
end

describe '#make_copy' do
it 'returns an error if /course.json request fails' do
stub_request(:get, course_url)
.to_return(status: 404, body: '', headers: {})

result = subject
expect(result[:error]).to eq("Error getting data from #{course_url}")
expect(result[:course]).to be_nil
end

it 'returns an error if /categories.json request fails' do
# Stub the response to the course request
stub_request(:get, course_url)
.to_return(status: 200, body: course_response_body, headers: {})
# Stub the response to the categories request
stub_request(:get, categories_url)
.to_return(status: 404, body: '', headers: {})

result = subject
expect(result[:error]).to eq("Error getting data from #{categories_url}")
expect(result[:course]).to be_nil
end

it 'returns an error if /users.json request fails' do
# Stub the response to the course request
stub_request(:get, course_url)
.to_return(status: 200, body: course_response_body, headers: {})

# Stub the response to the categories request
stub_request(:get, categories_url)
.to_return(status: 200, body: categories_response_body, headers: {})

# Stub the response to the users request
stub_request(:get, users_url)
.to_return(status: 404, body: '', headers: {})

result = subject
expect(result[:error]).to eq("Error getting data from #{users_url}")
expect(result[:course]).to be_nil
end

it 'course, categories, and users are created if no error' do
# Stub the response to the course request
stub_request(:get, course_url)
.to_return(status: 200, body: course_response_body, headers: {})

# Stub the response to the categories request
stub_request(:get, categories_url)
.to_return(status: 200, body: categories_response_body, headers: {})

# Stub the response to the users request
stub_request(:get, users_url)
.to_return(status: 200, body: users_response_body, headers: {})
result = subject

# No error was returned
expect(result[:error]).to be_nil
# Course returned is not nil
expect(result[:course]).not_to be_nil

# The course was created
expect(Course.exists?(slug: existent_prod_course_slug)).to eq(true)

# Course users were created
course = Course.find_by(slug: existent_prod_course_slug)

expect(course.instructors.length).to eq(1)
expect(course.instructors.first.username).to eq('Joshua Stone')

expect(course.staff.length).to eq(1)
expect(course.staff.first.username).to eq('Helaine (Wiki Ed)')

expect(course.students.length).to eq(2)
expect(course.students.first.username).to eq('CharlieJ385')
expect(course.students.second.username).to eq('Diqi Yan')

# Wiki exists
expect(Wiki.exists?(language: 'en', project: 'wikipedia')).to eq(true)

# Category was created
expect(Category.exists?(name: 'Category 0', depth: 0, source: 'Source 0',
wiki: 1)).to eq(true)

# Category course was created
expect(course.categories.length).to eq(1)

# Users were created
expect(User.exists?(username: 'Joshua Stone')).to eq(true)
expect(User.exists?(username: 'Helaine (Wiki Ed)')).to eq(true)
expect(User.exists?(username: 'CharlieJ385')).to eq(true)
expect(User.exists?(username: 'Diqi Yan')).to eq(true)

# Update logs were correctly created
course.flags['update_logs'].each do |key, _value|
expect(key).to be_a(Integer)
end
end
end
end

0 comments on commit fa0fb2d

Please sign in to comment.