Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions lib/tasks/project_preview_seeds_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# frozen_string_literal: true

module ProjectPreviewSeedsHelper
# Public Blocks template for Experience CS project preview (unowned, loadable without auth).
PROJECT_PREVIEW_IDENTIFIER = 'excs-preview-starter'
PROJECT_PREVIEW_LOCALE = 'en'
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_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')

def create_public_scratch_preview_project
[
{
locale: PROJECT_PREVIEW_LOCALE,
name: PROJECT_PREVIEW_NAME,
instructions: public_scratch_preview_instructions
},
{
locale: PROJECT_PREVIEW_LOCALE_FR,
name: PROJECT_PREVIEW_NAME_FR,
instructions: public_scratch_preview_instructions_fr
}
].map { |attrs| upsert_public_scratch_preview_project(**attrs) }
end

def upsert_public_scratch_preview_project(locale:, name:, instructions:)
project = Project.find_or_initialize_by(
identifier: PROJECT_PREVIEW_IDENTIFIER,
locale:
)
raise "Refusing to overwrite non-public project '#{PROJECT_PREVIEW_IDENTIFIER}' (#{locale})" if project.persisted? && (project.user_id.present? || project.school_id.present?)

Rails.logger.info "Seeding public Scratch preview project '#{PROJECT_PREVIEW_IDENTIFIER}' (#{locale})..."
project.name = name
project.user_id = nil
project.school_id = nil
project.project_type = Project::Types::CODE_EDITOR_SCRATCH
project.instructions = instructions
project.scratch_component ||= ScratchComponent.new
project.scratch_component.content = public_scratch_preview_content
project.save!
project
end

def destroy_public_scratch_preview_project
Project.where(
identifier: PROJECT_PREVIEW_IDENTIFIER,
user_id: nil,
school_id: nil,
project_type: Project::Types::CODE_EDITOR_SCRATCH
).destroy_all
end
Comment thread
DNR500 marked this conversation as resolved.

def public_scratch_preview_content
JSON.parse(File.read(PROJECT_PREVIEW_CONTENT_PATH))
end

def public_scratch_preview_instructions
JSON.parse(File.read(PROJECT_PREVIEW_INSTRUCTIONS_PATH))
end

def public_scratch_preview_instructions_fr
JSON.parse(File.read(PROJECT_PREVIEW_INSTRUCTIONS_FR_PATH))
end
end
100 changes: 100 additions & 0 deletions lib/tasks/seed_data/excs_preview_starter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"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"
}
}
11 changes: 11 additions & 0 deletions lib/tasks/seed_data/excs_preview_starter_instructions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"markdown_content": "## Welcome\n\nThis is a sample Experience CS Blocks project you can try without signing in."
},
{
"markdown_content": "## Try it out\n\n1. Click the green flag to run the project.\n2. Explore the sprites and backdrops.\n3. Change a block and run it again."
},
{
"markdown_content": "## What's next?\n\nWhen you use this project in a class, teachers can add their own steps here for learners."
}
]
11 changes: 11 additions & 0 deletions lib/tasks/seed_data/excs_preview_starter_instructions_fr.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"markdown_content": "## Bienvenue\n\nVoici un projet Blocks Experience CS d'exemple que vous pouvez essayer sans vous connecter."
},
{
"markdown_content": "## Essayez\n\n1. Cliquez sur le drapeau vert pour lancer le projet.\n2. Explorez les lutins et les arrière-plans.\n3. Modifiez un bloc et relancez."
},
{
"markdown_content": "## Et ensuite ?\n\nLorsque vous utilisez ce projet en classe, les enseignants peuvent ajouter ici leurs propres étapes pour les élèves."
}
]
4 changes: 4 additions & 0 deletions lib/tasks/seeds_helper.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# frozen_string_literal: true

require_relative 'project_preview_seeds_helper'

module SeedsHelper
include ProjectPreviewSeedsHelper

TEST_USERS = {
jane_doe: '583ba872-b16e-46e1-9f7d-df89d267550d', # jane.doe@example.com
john_doe: 'bbb9b8fd-f357-4238-983d-6f87b99bdbb2', # john.doe@example.com
Expand Down
7 changes: 6 additions & 1 deletion lib/tasks/test_seeds.rake
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ namespace :test_seeds do
school_ids = [school_id, teacher_signup_school_id].compact
School.where(id: school_ids).destroy_all

destroy_public_scratch_preview_project

# Clear any feature flags that may have been set
Flipper.features.each(&:remove)

Expand All @@ -47,10 +49,13 @@ namespace :test_seeds do
desc 'Create a school with lessons and students'
task create: :environment do
if School.exists?(id: TEST_SCHOOL)
puts "Test school (#{TEST_SCHOOL}) already exists, run the destroy_seed_data task to start over)."
create_public_scratch_preview_project
puts "Test school (#{TEST_SCHOOL}) already exists, run the destroy_seed_data task to start over."
else
ActiveRecord::Base.transaction do
Rails.logger.info 'Attempting to seed data...'
create_public_scratch_preview_project

creator_id = ENV.fetch('SEEDING_CREATOR_ID', TEST_USERS[:jane_doe])
teacher_id = ENV.fetch('SEEDING_TEACHER_ID', TEST_USERS[:john_doe])

Expand Down
104 changes: 104 additions & 0 deletions spec/lib/test_seeds_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,42 @@
expect(ScratchAsset.where(project_id: scratch_project_id)).not_to exist
end

it 'destroys the public Scratch preview project' do
create(
:scratch_project,
identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER,
locale: SeedsHelper::PROJECT_PREVIEW_LOCALE,
user_id: nil,
name: SeedsHelper::PROJECT_PREVIEW_NAME
)
create(
:scratch_project,
identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER,
locale: SeedsHelper::PROJECT_PREVIEW_LOCALE_FR,
user_id: nil,
name: SeedsHelper::PROJECT_PREVIEW_NAME_FR
)

task.invoke

expect(
Project.where(identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER)
).not_to exist
end

it 'does not destroy owned projects that share the preview identifier' do
owned = create(
:scratch_project,
identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER,
locale: SeedsHelper::PROJECT_PREVIEW_LOCALE,
user_id: SecureRandom.uuid
)

task.invoke

expect(Project.find_by(id: owned.id)).to be_present
end

it 'removes all feature flags' do
Flipper.enable(:some_feature)
task.invoke
Expand All @@ -60,6 +96,74 @@
expect(School.find_by(creator_id:).verified_at).to be_truthy
end

it 'creates a public Scratch preview project' do
project = Project.find_by!(
identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER,
locale: SeedsHelper::PROJECT_PREVIEW_LOCALE
)

expect(project).to have_attributes(
name: SeedsHelper::PROJECT_PREVIEW_NAME,
user_id: nil,
school_id: nil,
project_type: Project::Types::CODE_EDITOR_SCRATCH
)
expect(project.scratch_component).to be_present
expect(project.scratch_component.content.to_h).to include('targets')
expect(project.instructions).to eq(
JSON.parse(File.read(SeedsHelper::PROJECT_PREVIEW_INSTRUCTIONS_PATH))
)
end

it 'creates a French locale public Scratch preview project' do
project = Project.find_by!(
identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER,
locale: SeedsHelper::PROJECT_PREVIEW_LOCALE_FR
)

expect(project).to have_attributes(
name: SeedsHelper::PROJECT_PREVIEW_NAME_FR,
user_id: nil,
school_id: nil,
project_type: Project::Types::CODE_EDITOR_SCRATCH
)
expect(project.scratch_component).to be_present
expect(project.instructions).to eq(
JSON.parse(File.read(SeedsHelper::PROJECT_PREVIEW_INSTRUCTIONS_FR_PATH))
)
end

it 'updates instructions on an existing public Scratch preview project' do
project = Project.find_by!(
identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER,
locale: SeedsHelper::PROJECT_PREVIEW_LOCALE
)
project.update!(instructions: [{ markdown_content: 'stale step' }])

task.reenable
task.invoke

expect(project.reload.instructions).to eq(
JSON.parse(File.read(SeedsHelper::PROJECT_PREVIEW_INSTRUCTIONS_PATH))
)
end

it 'refuses to overwrite a non-public project with the preview identifier' do
Project.where(identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER).destroy_all
create(
:scratch_project,
identifier: SeedsHelper::PROJECT_PREVIEW_IDENTIFIER,
locale: SeedsHelper::PROJECT_PREVIEW_LOCALE,
user_id: SecureRandom.uuid
)

task.reenable
expect { task.invoke }.to raise_error(
RuntimeError,
/Refusing to overwrite non-public project/
)
end

it 'creates lessons with projects' do
school = School.find_by(creator_id:)
expect(SchoolClass.where(school_id: school.id)).to exist
Expand Down
Loading