From edca20ae8fd8a6aeedc7d68635ec4fb730174728 Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Wed, 26 Aug 2026 16:56:58 +0100 Subject: [PATCH 1/3] Seed a Python, HTML/CSS and Scratch lesson for each class Previously the education seed tasks only ever created Python lessons/projects, with lesson names drawn from a random Faker programming language that had no relation to the actual project content. This made it harder to manually test school class flows, or run E2E tests, against Scratch or HTML/CSS lessons using the seed data. This change makes create_lessons build one lesson per entry in LESSON_PROJECT_TYPES (python, html/css, scratch) for a school class, naming each lesson after its project type (e.g. "Lesson 3 scratch") and generating a project with matching starter content via create_project: a .py component for Python, an .html/.css pair for HTML/CSS, and a Scratch component (seeded from lib/tasks/seed_data/lesson_scratch_starter.json) for Scratch. "Project type" is used instead of "language" throughout, since Scratch and HTML/CSS aren't languages. Both for_education.rake and test_seeds.rake share this behaviour, and the seeded school gets scratch_enabled set in both, matching the real constraint enforced when a teacher creates a Scratch lesson via LessonCreation#verify_lesson_scratch!. Existing specs for for_education and test_seeds are updated for the new lesson counts and assert on the specific lesson names/project types for the Scratch and HTML/CSS lessons. --- lib/tasks/for_education.rake | 6 +- .../seed_data/lesson_scratch_starter.json | 98 +++++++++++++++++++ lib/tasks/seeds_helper.rb | 54 +++++++--- lib/tasks/test_seeds.rake | 6 +- spec/lib/tasks/for_education_spec.rb | 16 +-- spec/lib/tasks/seeds_helper_spec.rb | 45 +++++++++ spec/lib/test_seeds_spec.rb | 29 ++++-- 7 files changed, 220 insertions(+), 34 deletions(-) create mode 100644 lib/tasks/seed_data/lesson_scratch_starter.json create mode 100644 spec/lib/tasks/seeds_helper_spec.rb 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/seed_data/lesson_scratch_starter.json b/lib/tasks/seed_data/lesson_scratch_starter.json new file mode 100644 index 000000000..6a808308b --- /dev/null +++ b/lib/tasks/seed_data/lesson_scratch_starter.json @@ -0,0 +1,98 @@ +{ + "targets": [ + { + "isStage": true, + "name": "Stage", + "variables": { "`jEk@4|i[#Fk?(8x)AV.-my variable": ["my variable", 0] }, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "backdrop1", + "dataFormat": "svg", + "assetId": "cd21514d0531fdffb22204e0ec5ed84a", + "md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg", + "rotationCenterX": 240, + "rotationCenterY": 180 + } + ], + "sounds": [ + { + "name": "pop", + "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", + "dataFormat": "wav", + "format": "", + "rate": 48000, + "sampleCount": 1123, + "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" + } + ], + "volume": 100, + "layerOrder": 0, + "tempo": 60, + "videoTransparency": 50, + "videoState": "on", + "textToSpeechLanguage": null + }, + { + "isStage": false, + "name": "Sprite1", + "variables": {}, + "lists": {}, + "broadcasts": {}, + "blocks": {}, + "comments": {}, + "currentCostume": 0, + "costumes": [ + { + "name": "costume1", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "bcf454acf82e4504149f7ffe07081dbc", + "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", + "rotationCenterX": 48, + "rotationCenterY": 50 + }, + { + "name": "costume2", + "bitmapResolution": 1, + "dataFormat": "svg", + "assetId": "0fb9be3e8397c983338cb71dc84d0b25", + "md5ext": "0fb9be3e8397c983338cb71dc84d0b25.svg", + "rotationCenterX": 46, + "rotationCenterY": 53 + } + ], + "sounds": [ + { + "name": "Meow", + "assetId": "83c36d806dc92327b9e7049a565c6bff", + "dataFormat": "wav", + "format": "", + "rate": 48000, + "sampleCount": 40681, + "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" + } + ], + "volume": 100, + "layerOrder": 1, + "visible": true, + "x": 0, + "y": 0, + "size": 100, + "direction": 90, + "draggable": false, + "rotationStyle": "all around" + } + ], + "monitors": [], + "extensions": [], + "meta": { + "semver": "3.0.0", + "vm": "12.7.0", + "agent": "default" + } +} diff --git a/lib/tasks/seeds_helper.rb b/lib/tasks/seeds_helper.rb index df0a5992e..0efec2feb 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/lesson_scratch_starter.json') + + LESSON_HTML_CONTENT = <<~HTML + + + + + +

Heading

+

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('

Heading

') + expect(css_component.content).to include('color: blue') + end + + it 'creates a lesson with a Scratch project' do + lesson = lessons.find { |l| l.name == 'Lesson 3 scratch' } + + expect(lesson.project.project_type).to eq(Project::Types::CODE_EDITOR_SCRATCH) + content = lesson.project.scratch_component.content.to_h + expect(content).to include('targets', 'monitors', 'extensions') + end + end +end diff --git a/spec/lib/test_seeds_spec.rb b/spec/lib/test_seeds_spec.rb index 1dd11cb5e..7f9f84594 100644 --- a/spec/lib/test_seeds_spec.rb +++ b/spec/lib/test_seeds_spec.rb @@ -164,12 +164,20 @@ ) end - it 'creates lessons with projects' do + it 'creates lessons with projects, one per language, for each class' do school = School.find_by(creator_id:) expect(SchoolClass.where(school_id: school.id)).to exist lesson = Lesson.where(school_id: school.id) - expect(lesson.length).to eq(4) - expect(Project.where(lesson_id: lesson.pluck(:id)).length).to eq(4) + expect(lesson.length).to eq(6) + expect(lesson.pluck(:name)).to contain_exactly( + 'Lesson 1 python', 'Lesson 2 html/css', 'Lesson 3 scratch', + 'Lesson 1 python', 'Lesson 2 html/css', 'Lesson 3 scratch' + ) + + projects = Project.where(lesson_id: lesson.pluck(:id)) + expect(projects.length).to eq(6) + 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 'assigns a teacher' do @@ -182,7 +190,7 @@ school_class = SchoolClass.joins(:teachers).find_by(school_id:, teachers: { teacher_id: creator_id }) expect(school_class).not_to be_nil - expect(Lesson.where(school_id:, school_class_id: school_class.id).length).to eq(2) + expect(Lesson.where(school_id:, school_class_id: school_class.id).length).to eq(3) end it 'creates a class teacher association for the owner' do @@ -193,7 +201,7 @@ school_id = School.find_by(creator_id:).id school_class = SchoolClass.joins(:teachers).find_by(school_id:, teachers: { teacher_id: }) expect(school_class).not_to be_nil - expect(Lesson.where(school_id:, school_class_id: school_class.id).length).to eq(2) + expect(Lesson.where(school_id:, school_class_id: school_class.id).length).to eq(3) end it 'creates a class teacher association for the teacher' do @@ -210,8 +218,8 @@ task.invoke end.not_to change { [SchoolClass.where(school_id: school.id).count, Lesson.where(school_id: school.id).count, Project.where(school_id: school.id).count] } - expect(owner_class.reload.lessons.count).to eq(2) - expect(teacher_class.reload.lessons.count).to eq(2) + expect(owner_class.reload.lessons.count).to eq(3) + expect(teacher_class.reload.lessons.count).to eq(3) end it 'assigns students' do @@ -223,6 +231,11 @@ expect(ClassStudent.where(student_id: student_2, school_class_id:)).to exist end + it 'enables scratch for the school' do + school = School.find_by(creator_id:) + expect(school.scratch_enabled?).to be true + end + context 'when the seeded school is in the US' do let(:seed_country_code) { 'US' } @@ -234,7 +247,7 @@ expect(school.district_nces_id).to match(/\A\d{7}\z/) expect(school.district_name).to be_present expect(school_class).not_to be_nil - expect(Lesson.where(school_id: school.id, school_class_id: school_class.id).length).to eq(2) + expect(Lesson.where(school_id: school.id, school_class_id: school_class.id).length).to eq(3) end end end From 8e53c74b35e36dff7c2e1cb136a632518073d4d2 Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:06:33 +0100 Subject: [PATCH 2/3] Combine two sample scratch files These are both the same and are just used to represent the Scratch Starter project --- lib/tasks/project_preview_seeds_helper.rb | 2 +- lib/tasks/seed_data/excs_preview_starter.json | 100 ------------------ ...rter.json => scratch_starter_example.json} | 0 lib/tasks/seeds_helper.rb | 2 +- 4 files changed, 2 insertions(+), 102 deletions(-) delete mode 100644 lib/tasks/seed_data/excs_preview_starter.json rename lib/tasks/seed_data/{lesson_scratch_starter.json => scratch_starter_example.json} (100%) 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/excs_preview_starter.json deleted file mode 100644 index 298a4f11b..000000000 --- a/lib/tasks/seed_data/excs_preview_starter.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "targets": [ - { - "isStage": true, - "name": "Stage", - "variables": { - "`jEk@4|i[#Fk?(8x)AV.-my variable": ["my variable", 0] - }, - "lists": {}, - "broadcasts": {}, - "blocks": {}, - "comments": {}, - "currentCostume": 0, - "costumes": [ - { - "name": "backdrop1", - "dataFormat": "svg", - "assetId": "cd21514d0531fdffb22204e0ec5ed84a", - "md5ext": "cd21514d0531fdffb22204e0ec5ed84a.svg", - "rotationCenterX": 240, - "rotationCenterY": 180 - } - ], - "sounds": [ - { - "name": "pop", - "assetId": "83a9787d4cb6f3b7632b4ddfebf74367", - "dataFormat": "wav", - "format": "", - "rate": 48000, - "sampleCount": 1123, - "md5ext": "83a9787d4cb6f3b7632b4ddfebf74367.wav" - } - ], - "volume": 100, - "layerOrder": 0, - "tempo": 60, - "videoTransparency": 50, - "videoState": "on", - "textToSpeechLanguage": null - }, - { - "isStage": false, - "name": "Sprite1", - "variables": {}, - "lists": {}, - "broadcasts": {}, - "blocks": {}, - "comments": {}, - "currentCostume": 0, - "costumes": [ - { - "name": "costume1", - "bitmapResolution": 1, - "dataFormat": "svg", - "assetId": "bcf454acf82e4504149f7ffe07081dbc", - "md5ext": "bcf454acf82e4504149f7ffe07081dbc.svg", - "rotationCenterX": 48, - "rotationCenterY": 50 - }, - { - "name": "costume2", - "bitmapResolution": 1, - "dataFormat": "svg", - "assetId": "0fb9be3e8397c983338cb71dc84d0b25", - "md5ext": "0fb9be3e8397c983338cb71dc84d0b25.svg", - "rotationCenterX": 46, - "rotationCenterY": 53 - } - ], - "sounds": [ - { - "name": "Meow", - "assetId": "83c36d806dc92327b9e7049a565c6bff", - "dataFormat": "wav", - "format": "", - "rate": 48000, - "sampleCount": 40681, - "md5ext": "83c36d806dc92327b9e7049a565c6bff.wav" - } - ], - "volume": 100, - "layerOrder": 1, - "visible": true, - "x": 0, - "y": 0, - "size": 100, - "direction": 90, - "draggable": false, - "rotationStyle": "all around" - } - ], - "monitors": [], - "extensions": [], - "meta": { - "semver": "3.0.0", - "vm": "12.7.0", - "agent": "default" - } -} diff --git a/lib/tasks/seed_data/lesson_scratch_starter.json b/lib/tasks/seed_data/scratch_starter_example.json similarity index 100% rename from lib/tasks/seed_data/lesson_scratch_starter.json rename to lib/tasks/seed_data/scratch_starter_example.json diff --git a/lib/tasks/seeds_helper.rb b/lib/tasks/seeds_helper.rb index 0efec2feb..c13ec74b0 100644 --- a/lib/tasks/seeds_helper.rb +++ b/lib/tasks/seeds_helper.rb @@ -96,7 +96,7 @@ def assign_students(school_class, school) LESSON_PROJECT_TYPES = ['python', 'html/css', 'scratch'].freeze - LESSON_SCRATCH_CONTENT_PATH = Rails.root.join('lib/tasks/seed_data/lesson_scratch_starter.json') + LESSON_SCRATCH_CONTENT_PATH = Rails.root.join('lib/tasks/seed_data/scratch_starter_example.json') LESSON_HTML_CONTENT = <<~HTML From d474ec11ec7cd1972b2da4ed375f625d8704565b Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Thu, 27 Aug 2026 14:07:23 +0100 Subject: [PATCH 3/3] Remove unused scratch project json This was added in as a stub before we saved projects to the db --- app/views/api/scratch/projects/show.json | 264 ----------------------- 1 file changed, 264 deletions(-) delete mode 100644 app/views/api/scratch/projects/show.json 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