diff --git a/app/views/api/scratch/projects/show.json b/app/views/api/scratch/projects/show.json deleted file mode 100644 index 501c70955..000000000 --- a/app/views/api/scratch/projects/show.json +++ /dev/null @@ -1,264 +0,0 @@ -{ - "targets": [ - { - "isStage": true, - "name": "Stage", - "variables": { - "`jEk@4|i[#Fk?(8x)AV.-my variable": [ - "my variable", - "1" - ] - }, - "lists": {}, - "broadcasts": {}, - "blocks": { - "Q*IGJj0mdm@]wJ,v%1M5": { - "opcode": "event_whenflagclicked", - "next": "]ML!StPuGDgh@B`^[v0d", - "parent": null, - "inputs": {}, - "fields": {}, - "shadow": false, - "topLevel": true, - "x": 188, - "y": 132 - }, - "]ML!StPuGDgh@B`^[v0d": { - "opcode": "data_setvariableto", - "next": null, - "parent": "Q*IGJj0mdm@]wJ,v%1M5", - "inputs": { - "VALUE": [ - 1, - [ - 10, - "1" - ] - ] - }, - "fields": { - "VARIABLE": [ - "my variable", - "`jEk@4|i[#Fk?(8x)AV.-my variable" - ] - }, - "shadow": false, - "topLevel": false - } - }, - "comments": {}, - "currentCostume": 0, - "costumes": [ - { - "name": "backdrop1", - "dataFormat": "svg", - "assetId": "cd21514d0531fdffb22204e0ec5ed84a", - "md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg", - "rotationCenterX": 240, - "rotationCenterY": 180 - } - ], - "sounds": [], - "volume": 100, - "layerOrder": 0, - "tempo": 60, - "videoTransparency": 50, - "videoState": "on", - "textToSpeechLanguage": null - }, - { - "isStage": false, - "name": "teapot", - "variables": {}, - "lists": {}, - "broadcasts": {}, - "blocks": { - "+7D@27U/UaREbT1:$D2p": { - "opcode": "motion_goto_menu", - "next": null, - "parent": "WQem=5FI$H@eF8Oy8=%m", - "inputs": {}, - "fields": { - "TO": [ - "_random_" - ] - }, - "shadow": true, - "topLevel": false - }, - ":Cul13Fz-$kW#uI)b3Ih": { - "opcode": "motion_turnright", - "next": "`HzTVWK}[NHS}n(wXj(~", - "parent": "o6dY$%i#z$_:%J%y.1Uv", - "inputs": { - "DEGREES": [ - 1, - [ - 4, - "15" - ] - ] - }, - "fields": {}, - "shadow": false, - "topLevel": false - }, - "El0`*m+.st%JF!*L54E,": { - "opcode": "control_repeat", - "next": null, - "parent": "JP;xPsfOSUqBfPdVA)C9", - "inputs": { - "TIMES": [ - 1, - [ - 6, - "5" - ] - ], - "SUBSTACK": [ - 2, - "WQem=5FI$H@eF8Oy8=%m" - ] - }, - "fields": {}, - "shadow": false, - "topLevel": false - }, - "JP;xPsfOSUqBfPdVA)C9": { - "opcode": "event_whenflagclicked", - "next": "El0`*m+.st%JF!*L54E,", - "parent": null, - "inputs": {}, - "fields": {}, - "shadow": false, - "topLevel": true, - "x": 5, - "y": -1196 - }, - "Lf3w#r641AVS(Z;,+4y3": { - "opcode": "event_whenthisspriteclicked", - "next": "o6dY$%i#z$_:%J%y.1Uv", - "parent": null, - "inputs": {}, - "fields": {}, - "shadow": false, - "topLevel": true, - "x": 277, - "y": -1199 - }, - "TFT|@Y(or^}uOFGDC|FB": { - "opcode": "looks_setsizeto", - "next": null, - "parent": "`HzTVWK}[NHS}n(wXj(~", - "inputs": { - "SIZE": [ - 1, - [ - 4, - "100" - ] - ] - }, - "fields": {}, - "shadow": false, - "topLevel": false - }, - "WQem=5FI$H@eF8Oy8=%m": { - "opcode": "motion_goto", - "next": "k`v/#:wkfU{|QkjM)6G|", - "parent": "El0`*m+.st%JF!*L54E,", - "inputs": { - "TO": [ - 1, - "+7D@27U/UaREbT1:$D2p" - ] - }, - "fields": {}, - "shadow": false, - "topLevel": false - }, - "`HzTVWK}[NHS}n(wXj(~": { - "opcode": "control_wait", - "next": "TFT|@Y(or^}uOFGDC|FB", - "parent": ":Cul13Fz-$kW#uI)b3Ih", - "inputs": { - "DURATION": [ - 1, - [ - 5, - "1" - ] - ] - }, - "fields": {}, - "shadow": false, - "topLevel": false - }, - "k`v/#:wkfU{|QkjM)6G|": { - "opcode": "control_wait", - "next": null, - "parent": "WQem=5FI$H@eF8Oy8=%m", - "inputs": { - "DURATION": [ - 1, - [ - 5, - "0.1" - ] - ] - }, - "fields": {}, - "shadow": false, - "topLevel": false - }, - "o6dY$%i#z$_:%J%y.1Uv": { - "opcode": "looks_setsizeto", - "next": ":Cul13Fz-$kW#uI)b3Ih", - "parent": "Lf3w#r641AVS(Z;,+4y3", - "inputs": { - "SIZE": [ - 1, - [ - 4, - "200" - ] - ] - }, - "fields": {}, - "shadow": false, - "topLevel": false - } - }, - "comments": {}, - "currentCostume": 0, - "costumes": [ - { - "name": "teapot", - "bitmapResolution": 1, - "dataFormat": "svg", - "assetId": "8a9dadf4eea61892ec6908b1c99e4961", - "md5ext": "8a9dadf4eea61892ec6908b1c99e4961.svg", - "rotationCenterX": 23.171110153198242, - "rotationCenterY": 22.78113555908203 - } - ], - "sounds": [], - "volume": 100, - "layerOrder": 1, - "visible": true, - "x": 0, - "y": 6, - "size": 100, - "direction": 105, - "draggable": false, - "rotationStyle": "all around" - } - ], - "monitors": [], - "extensions": [], - "meta": { - "semver": "3.0.0", - "vm": "12.2.2", - "agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36" - } -} \ No newline at end of file diff --git a/lib/tasks/for_education.rake b/lib/tasks/for_education.rake index fa1d1ab19..d222abe32 100644 --- a/lib/tasks/for_education.rake +++ b/lib/tasks/for_education.rake @@ -91,15 +91,13 @@ namespace :for_education do school = create_school(creator_id, TEST_SCHOOL) verify_school(school) + school.update!(scratch_enabled: true) assign_a_teacher(teacher_id, school) school_class = create_school_class(creator_id, school) assign_students(school_class, school) - lessons = create_lessons(creator_id, school, school_class) - lessons.each do |lesson| - create_project(creator_id, school, lesson) - end + create_lessons(creator_id, school, school_class) Rails.logger.info 'Done...' end end diff --git a/lib/tasks/project_preview_seeds_helper.rb b/lib/tasks/project_preview_seeds_helper.rb index e09590017..2aab9738c 100644 --- a/lib/tasks/project_preview_seeds_helper.rb +++ b/lib/tasks/project_preview_seeds_helper.rb @@ -7,7 +7,7 @@ module ProjectPreviewSeedsHelper PROJECT_PREVIEW_LOCALE_FR = 'fr-FR' PROJECT_PREVIEW_NAME = 'Experience CS Preview Starter' PROJECT_PREVIEW_NAME_FR = 'Démo Aperçu Experience CS' - PROJECT_PREVIEW_CONTENT_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter.json') + PROJECT_PREVIEW_CONTENT_PATH = Rails.root.join('lib/tasks/seed_data/scratch_starter_example.json') PROJECT_PREVIEW_INSTRUCTIONS_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter_instructions.json') PROJECT_PREVIEW_INSTRUCTIONS_FR_PATH = Rails.root.join('lib/tasks/seed_data/excs_preview_starter_instructions_fr.json') diff --git a/lib/tasks/seed_data/excs_preview_starter.json b/lib/tasks/seed_data/scratch_starter_example.json similarity index 96% rename from lib/tasks/seed_data/excs_preview_starter.json rename to lib/tasks/seed_data/scratch_starter_example.json index 298a4f11b..6a808308b 100644 --- a/lib/tasks/seed_data/excs_preview_starter.json +++ b/lib/tasks/seed_data/scratch_starter_example.json @@ -3,9 +3,7 @@ { "isStage": true, "name": "Stage", - "variables": { - "`jEk@4|i[#Fk?(8x)AV.-my variable": ["my variable", 0] - }, + "variables": { "`jEk@4|i[#Fk?(8x)AV.-my variable": ["my variable", 0] }, "lists": {}, "broadcasts": {}, "blocks": {}, diff --git a/lib/tasks/seeds_helper.rb b/lib/tasks/seeds_helper.rb index df0a5992e..c13ec74b0 100644 --- a/lib/tasks/seeds_helper.rb +++ b/lib/tasks/seeds_helper.rb @@ -2,6 +2,7 @@ require_relative 'project_preview_seeds_helper' +# rubocop:disable Metrics/ModuleLength module SeedsHelper include ProjectPreviewSeedsHelper @@ -93,21 +94,39 @@ def assign_students(school_class, school) end end + LESSON_PROJECT_TYPES = ['python', 'html/css', 'scratch'].freeze + + LESSON_SCRATCH_CONTENT_PATH = Rails.root.join('lib/tasks/seed_data/scratch_starter_example.json') + + LESSON_HTML_CONTENT = <<~HTML + +
+ + + +Paragraph
+ + + HTML + def create_lessons(user_id, school, school_class, visibility = 'students') - Array.new(2) do |i| - lesson_name = "Lesson #{i + 1}: #{Faker::ProgrammingLanguage.name}" - Lesson.find_or_create_by!(school:, school_class:, name: lesson_name, user_id:) do |lesson| + LESSON_PROJECT_TYPES.each_with_index.map do |project_type, i| + lesson_name = "Lesson #{i + 1} #{project_type}" + lesson = Lesson.find_or_create_by!(school:, school_class:, name: lesson_name, user_id:) do |l| Rails.logger.info "Seeding Lesson #{i + 1}..." - lesson.user_id = user_id - lesson.school = school - lesson.school_class = school_class - lesson.name = lesson_name - lesson.visibility = visibility + l.user_id = user_id + l.school = school + l.school_class = school_class + l.name = lesson_name + l.visibility = visibility end + create_project(user_id, school, lesson, project_type) + lesson end end - def create_project(user_id, school, lesson, code = '') + def create_project(user_id, school, lesson, project_type) Project.find_or_create_by!(user_id:, school:, lesson:) do |project| Rails.logger.info "Seeding a project for #{lesson.name}..." project.name = lesson.name @@ -115,9 +134,20 @@ def create_project(user_id, school, lesson, code = '') project.school = school project.lesson = lesson project.locale = 'en' - project.project_type = Project::Types::PYTHON - project.components << Component.new({ extension: 'py', name: 'main', - content: code }) + + case project_type + when 'scratch' + project.project_type = Project::Types::CODE_EDITOR_SCRATCH + project.build_scratch_component(content: JSON.parse(File.read(LESSON_SCRATCH_CONTENT_PATH))) + when 'html/css' + project.project_type = Project::Types::HTML + project.components << Component.new(extension: 'html', name: 'index', content: LESSON_HTML_CONTENT) + project.components << Component.new(extension: 'css', name: 'style', content: "h1 {\n color: blue;\n}") + else + project.project_type = Project::Types::PYTHON + project.components << Component.new(extension: 'py', name: 'main', content: 'print("Hello World!")') + end end end end +# rubocop:enable Metrics/ModuleLength diff --git a/lib/tasks/test_seeds.rake b/lib/tasks/test_seeds.rake index 05cadbe3b..e667caee5 100644 --- a/lib/tasks/test_seeds.rake +++ b/lib/tasks/test_seeds.rake @@ -65,6 +65,7 @@ namespace :test_seeds do school = create_school(creator_id, TEST_SCHOOL) verify_school(school) + school.update!(scratch_enabled: true) school.school_email_domains.build(domain: 'example.com').save! @@ -77,10 +78,7 @@ namespace :test_seeds do school_class = create_school_class(user_id, school, "#{teacher_name}'s Class", "A class for #{teacher_name}'s students") assign_students(school_class, school) - lessons = create_lessons(user_id, school, school_class) - lessons.each do |lesson| - create_project(user_id, school, lesson, 'print("Hello World!")') - end + create_lessons(user_id, school, school_class) end Rails.logger.info 'Done...' end diff --git a/spec/lib/tasks/for_education_spec.rb b/spec/lib/tasks/for_education_spec.rb index aab58523d..9189baab1 100644 --- a/spec/lib/tasks/for_education_spec.rb +++ b/spec/lib/tasks/for_education_spec.rb @@ -70,22 +70,26 @@ expect(SchoolClass.where(school_id: school.id)).to exist end - it 'adds two lessons to the school' do - lesson = Lesson.where(school_id: school.id) - expect(lesson.length).to eq(2) + it 'adds three lessons to the school, one per project type' do + lessons = Lesson.where(school_id: school.id) + expect(lessons.pluck(:name)).to contain_exactly( + 'Lesson 1 python', 'Lesson 2 html/css', 'Lesson 3 scratch' + ) end - it 'adds two projects' do + it 'adds three projects, matching each lesson project type' do lessons = Lesson.where(school_id: school.id) projects = Project.where(lesson_id: lessons.pluck(:id)) - if projects.length != 2 + if projects.length != 3 $stdout.puts('Debug info for intermittent test') lessons.each { |lesson| $stdout.puts(lesson.inspect) } projects.each { |project| $stdout.puts(project.inspect) } end - expect(projects.length).to eq(2) + expect(projects.length).to eq(3) + expect(projects.find_by(name: 'Lesson 2 html/css').project_type).to eq(Project::Types::HTML) + expect(projects.find_by(name: 'Lesson 3 scratch').project_type).to eq(Project::Types::CODE_EDITOR_SCRATCH) end it 'does not add more lessons or projects when run again' do diff --git a/spec/lib/tasks/seeds_helper_spec.rb b/spec/lib/tasks/seeds_helper_spec.rb new file mode 100644 index 000000000..5b174e6c2 --- /dev/null +++ b/spec/lib/tasks/seeds_helper_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'rails_helper' +require_relative '../../../lib/tasks/seeds_helper' + +RSpec.describe SeedsHelper do + subject(:seeder) { Class.new { include SeedsHelper }.new } + + describe '#create_lessons' do + let(:teacher_id) { SecureRandom.uuid } + let(:school) { create(:school) } + let(:school_class) { create(:school_class, school:, teacher_ids: [teacher_id]) } + + let(:lessons) { seeder.create_lessons(teacher_id, school, school_class) } + + before { create(:teacher_role, user_id: teacher_id, school:) } + + it 'creates a lesson with a Python project' do + lesson = lessons.find { |l| l.name == 'Lesson 1 python' } + + expect(lesson.project.project_type).to eq(Project::Types::PYTHON) + component = lesson.project.components.find_by(extension: 'py') + expect(component.name).to eq('main') + expect(component.content).to eq('print("Hello World!")') + end + + it 'creates a lesson with an HTML/CSS project' do + lesson = lessons.find { |l| l.name == 'Lesson 2 html/css' } + + expect(lesson.project.project_type).to eq(Project::Types::HTML) + html_component = lesson.project.components.find_by(extension: 'html') + css_component = lesson.project.components.find_by(extension: 'css') + expect(html_component.content).to include('