From 0ff6b3e9ecd08d9623033fb2024eb94c820c0a7f Mon Sep 17 00:00:00 2001 From: bencodeorg Date: Fri, 16 Feb 2018 09:45:01 -0800 Subject: [PATCH 01/45] Add census submissions to contact rollups --- lib/cdo/contact_rollups.rb | 21 +++++++++++++++++++++ lib/cdo/contact_rollups_validation.rb | 7 +++++++ 2 files changed, 28 insertions(+) diff --git a/lib/cdo/contact_rollups.rb b/lib/cdo/contact_rollups.rb index 9cf0728ae8f2e..05057e56b8900 100644 --- a/lib/cdo/contact_rollups.rb +++ b/lib/cdo/contact_rollups.rb @@ -107,6 +107,7 @@ class ContactRollups ROLE_TEACHER = "Teacher".freeze ROLE_FORM_SUBMITTER = "Form Submitter".freeze + ROLE_CENSUS_SUBMITTER = "Census Submitter".freeze def self.build_contact_rollups start = Time.now @@ -120,6 +121,7 @@ def self.build_contact_rollups insert_from_pegasus_forms insert_from_dashboard_contacts insert_from_dashboard_pd_enrollments + insert_from_dashboard_census_submissions update_unsubscribe_info update_roles update_grades_taught @@ -295,6 +297,25 @@ def self.insert_from_dashboard_pd_enrollments log_completion(start) end + def self.insert_from_dashboard_census_submissions + start = Time.now + log "Inserting contacts from dashboard.census_submissions" + PEGASUS_REPORTING_DB_WRITER.run " + INSERT INTO #{PEGASUS_DB_NAME}.#{DEST_TABLE_NAME} (email, name, roles) + SELECT submitter_email_address, submitter_name, '#{ROLE_CENSUS_SUBMITTER}' + FROM #{DASHBOARD_DB_NAME}.census_submissions AS census_submissions + WHERE LENGTH(census_submissions.submitter_email_address) > 0 + ON DUPLICATE KEY UPDATE name = #{DEST_TABLE_NAME}.name, + -- Use LOCATE to determine if this role is already present and CONCAT+COALESCE to add it if it is not. + roles = + CASE LOCATE(values(roles), COALESCE(#{DEST_TABLE_NAME}.roles,'')) + WHEN 0 THEN LEFT(CONCAT(COALESCE(CONCAT(#{DEST_TABLE_NAME}.roles, ','), ''),values(roles)),255) + ELSE #{DEST_TABLE_NAME}.roles + END" + + log_completion(start) + end + def self.update_teachers_from_forms start = Time.now log "Updating teacher roles based on submitted forms" diff --git a/lib/cdo/contact_rollups_validation.rb b/lib/cdo/contact_rollups_validation.rb index c754ec0179448..01568b8dd39f7 100644 --- a/lib/cdo/contact_rollups_validation.rb +++ b/lib/cdo/contact_rollups_validation.rb @@ -88,6 +88,13 @@ class ContactRollupsValidation min: 2_500_000, max: 10_000_000 }, + { + name: "Census Submitter count", + query: "SELECT COUNT(*) from contact_rollups_daily WHERE roles + LIKE '%Census Submitter%'", + min: 40_000, + max: 400_000 + }, { # Check that rollup's 'opt_out' data matches pegasus.contact's # 'unsubscribed_at' data. In general the From c13654e638e77fe2940e30d17aff8766d01f8735 Mon Sep 17 00:00:00 2001 From: Suresh Chanmugam Date: Fri, 16 Feb 2018 11:22:27 -0800 Subject: [PATCH 02/45] Conditionally add trace option to dashboard and pegasus seed when invoked via rake ci --> rake build --- lib/rake/build.rake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rake/build.rake b/lib/rake/build.rake index b1980fb7422ca..ffc0bdc256152 100644 --- a/lib/rake/build.rake +++ b/lib/rake/build.rake @@ -81,7 +81,7 @@ namespace :build do else ChatClient.log 'Seeding dashboard...' ChatClient.log 'consider setting "skip_seed_all" in locals.yml if this is taking too long' if rack_env?(:development) - RakeUtils.rake 'seed:all' + RakeUtils.rake 'seed:all', (rack_env?(:test) ? '--trace' : nil) end # Commit dsls.en.yml changes on staging @@ -125,7 +125,7 @@ namespace :build do if CDO.daemon ChatClient.log 'Updating pegasus database...' begin - RakeUtils.rake 'pegasus:setup_db' + RakeUtils.rake 'pegasus:setup_db', (rack_env?(:test) ? '--trace' : nil) rescue => e ChatClient.log "/quote #{e.message}\n#{CDO.backtrace e}", message_format: 'text' raise e From 520836ad6372297a398feb78340e6a0b1ad862cb Mon Sep 17 00:00:00 2001 From: Josh Lory Date: Fri, 16 Feb 2018 11:35:09 -0800 Subject: [PATCH 03/45] Add an integration test for "jump forward" in Artist --- .../integration/levelSolutions/turtle/1_1.js | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/apps/test/integration/levelSolutions/turtle/1_1.js b/apps/test/integration/levelSolutions/turtle/1_1.js index 581cd6c0dcdbf..a6e8c1027a115 100644 --- a/apps/test/integration/levelSolutions/turtle/1_1.js +++ b/apps/test/integration/levelSolutions/turtle/1_1.js @@ -30,6 +30,49 @@ module.exports = { solution + '' }, + { + description: "Solution using more than the ideal number of blocks", + expected: { + result: true, + testResult: TestResults.TOO_MANY_BLOCKS_FAIL + }, + missingBlocks: [], + xml: + ` + + + + jumpBackward + 50 + + + jumpForward + 50 + + + moveForward + 100 + + + turnRight + 90 + + + moveForward + 100 + + + + + + + + + + + + ` + }, { description: "User doesnt add any blocks. Should fail.", expected: { From 1df647dde0c3597a5ad706ef83e671ee08c827a6 Mon Sep 17 00:00:00 2001 From: bencodeorg Date: Tue, 20 Feb 2018 10:42:07 -0800 Subject: [PATCH 04/45] Get teacher role from census form --- lib/cdo/contact_rollups.rb | 21 +++++++++++++++++++-- lib/cdo/contact_rollups_validation.rb | 7 ------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/cdo/contact_rollups.rb b/lib/cdo/contact_rollups.rb index 05057e56b8900..0e7f16fb05613 100644 --- a/lib/cdo/contact_rollups.rb +++ b/lib/cdo/contact_rollups.rb @@ -107,7 +107,6 @@ class ContactRollups ROLE_TEACHER = "Teacher".freeze ROLE_FORM_SUBMITTER = "Form Submitter".freeze - ROLE_CENSUS_SUBMITTER = "Census Submitter".freeze def self.build_contact_rollups start = Time.now @@ -144,6 +143,7 @@ def self.build_contact_rollups # Add contacts to the Teacher role based on form responses update_teachers_from_forms + update_teachers_from_census_submissions count = PEGASUS_REPORTING_DB_READER["select count(*) as cnt from #{PEGASUS_DB_NAME}.#{DEST_TABLE_NAME}"].first[:cnt] log "Done. Total overall time: #{Time.now - start} seconds. #{count} records created in contact_rollups_daily table." @@ -302,7 +302,7 @@ def self.insert_from_dashboard_census_submissions log "Inserting contacts from dashboard.census_submissions" PEGASUS_REPORTING_DB_WRITER.run " INSERT INTO #{PEGASUS_DB_NAME}.#{DEST_TABLE_NAME} (email, name, roles) - SELECT submitter_email_address, submitter_name, '#{ROLE_CENSUS_SUBMITTER}' + SELECT submitter_email_address, submitter_name, '#{ROLE_FORM_SUBMITTER}' FROM #{DASHBOARD_DB_NAME}.census_submissions AS census_submissions WHERE LENGTH(census_submissions.submitter_email_address) > 0 ON DUPLICATE KEY UPDATE name = #{DEST_TABLE_NAME}.name, @@ -334,6 +334,23 @@ def self.update_teachers_from_forms log_completion(start) end + def self.update_teachers_from_census_submissions + start = Time.now + log "Updating teacher roles based on census submissions" + PEGASUS_REPORTING_DB_WRITER.run " + UPDATE #{PEGASUS_DB_NAME}.#{DEST_TABLE_NAME} + INNER JOIN #{DASHBOARD_DB_NAME}.census_submissions on census_submissions.submitter_email_address = #{DEST_TABLE_NAME}.email + SET roles = + -- Use LOCATE to determine if this role is already present and CONCAT+COALESCE to add it if it is not. + CASE LOCATE('#{ROLE_TEACHER}', COALESCE(#{DEST_TABLE_NAME}.roles,'')) + WHEN 0 THEN LEFT(CONCAT(COALESCE(CONCAT(#{DEST_TABLE_NAME}.roles, ','), ''),'#{ROLE_TEACHER}'),255) + ELSE #{DEST_TABLE_NAME}.roles + END + WHERE census_submissions.submitter_role = 'TEACHER'" + + log_completion(start) + end + def self.update_unsubscribe_info start = Time.now log "Inserting contacts from pegasus.contacts" diff --git a/lib/cdo/contact_rollups_validation.rb b/lib/cdo/contact_rollups_validation.rb index 01568b8dd39f7..c754ec0179448 100644 --- a/lib/cdo/contact_rollups_validation.rb +++ b/lib/cdo/contact_rollups_validation.rb @@ -88,13 +88,6 @@ class ContactRollupsValidation min: 2_500_000, max: 10_000_000 }, - { - name: "Census Submitter count", - query: "SELECT COUNT(*) from contact_rollups_daily WHERE roles - LIKE '%Census Submitter%'", - min: 40_000, - max: 400_000 - }, { # Check that rollup's 'opt_out' data matches pegasus.contact's # 'unsubscribed_at' data. In general the From 73e96e6a4ca1b4368a14294dc3902551c860b884 Mon Sep 17 00:00:00 2001 From: Erin Date: Tue, 20 Feb 2018 10:43:13 -0800 Subject: [PATCH 05/45] Undo version routing change that caused errors --- apps/src/gamelab/animationListModule.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/apps/src/gamelab/animationListModule.js b/apps/src/gamelab/animationListModule.js index 31dd5fb205ee1..53d7368d2cbca 100644 --- a/apps/src/gamelab/animationListModule.js +++ b/apps/src/gamelab/animationListModule.js @@ -709,18 +709,16 @@ export function animationSourceUrl(key, props, withVersion = false) { // 1. If the animation has a sourceUrl it's external (from the library // or some other outside source, not the animation API) - and we may need - // to run it through the media proxy. (Note - Before 02/2018 - - // uploaded images may/may not have non-null sourceUrls. After 02/2018 - - // uploaded images will have null sourceUrls) + // to run it through the media proxy. + if (props.sourceUrl) { + return assetPrefix.fixPath(props.sourceUrl); + } + // 2. Otherwise it's local to this project, and we should use the animation // key to look it up in the animations API. - let url = (props.sourceUrl) ? - assetPrefix.fixPath(props.sourceUrl) : (animationsApi.basePath(key) + '.png'); - - // Appending version here to support projects with uploaded images - // with sourceUrls. - return url + ((props.version) ? '?version=' + props.version : ''); -} + return animationsApi.basePath(key) + '.png' + + ((withVersion && props.version) ? '?version=' + props.version : ''); + } /** * Static helper for converting a serialized animation list to an exportable one From d1aceb2d89818b590156fd473573da956b8cbb83 Mon Sep 17 00:00:00 2001 From: Erin Date: Wed, 21 Feb 2018 11:15:44 -0800 Subject: [PATCH 06/45] Test that sourceUrl is null in uploaded images --- .../AnimationPicker/animationPickerModule.js | 34 +++++++++++-------- .../animationPickerModuleTest.js | 23 +++++++++++++ 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/apps/src/gamelab/AnimationPicker/animationPickerModule.js b/apps/src/gamelab/AnimationPicker/animationPickerModule.js index b1874cf1d09d9..5629b3a5d091d 100644 --- a/apps/src/gamelab/AnimationPicker/animationPickerModule.js +++ b/apps/src/gamelab/AnimationPicker/animationPickerModule.js @@ -111,30 +111,34 @@ export function handleUploadComplete(result) { const key = result.filename.replace(/\.png$/i, ''); const sourceUrl = animationsApi.basePath(key + '.png'); + const onImageMetadataLoaded = buildOnImageMetadataLoaded(uploadFilename, goal, key, result, dispatch); // TODO (bbuchanan): This sequencing feels backwards. Eventually, we // ought to preview and get dimensions from the local filesystem, async // with the upload itself, but that will mean refactoring away from the // jQuery uploader. - loadImageMetadata(sourceUrl, metadata => { - const animation = _.assign({}, metadata, { - name: uploadFilename, - sourceUrl: null, - size: result.size, - version: result.versionId - }); - - if (goal === Goal.NEW_ANIMATION) { - dispatch(addAnimation(key, animation)); - } else if (goal === Goal.NEW_FRAME) { - dispatch(appendCustomFrames(animation)); - } - dispatch(hide()); - }, () => { + loadImageMetadata(sourceUrl, onImageMetadataLoaded, () => { dispatch(handleUploadError(gamelabMsg.animationPicker_failedToParseImage())); }); }; } +export function buildOnImageMetadataLoaded(uploadFilename, goal, key, result, dispatch) { + return (metadata) => { + const animation = _.assign({}, metadata, { + name: uploadFilename, + sourceUrl: null, + size: result.size, + version: result.versionId + }); + if (goal === Goal.NEW_ANIMATION) { + dispatch(addAnimation(key, animation)); + } else if (goal === Goal.NEW_FRAME) { + dispatch(appendCustomFrames(animation)); + } + dispatch(hide()); + }; +} + /** * Asynchronously loads an image file as an Image, then derives appropriate * animation metadata from that Image and returns the metadata to a callback. diff --git a/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js b/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js index 8a91ac29ce98b..98b1a17512605 100644 --- a/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js +++ b/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js @@ -1,4 +1,7 @@ import reducer, * as animationPickerModule from '@cdo/apps/gamelab/AnimationPicker/animationPickerModule'; +import listReducer from '@cdo/apps/gamelab/animationListModule'; +import {combineReducers} from 'redux'; +import {createStore} from '../../../util/redux'; import {expect} from '../../../util/configuredChai'; var Goal = animationPickerModule.Goal; @@ -102,5 +105,25 @@ describe('animationPickerModule', function () { expect(newState.uploadError).to.equal(status); }); }); + + describe('action: handleUploadComplete', function () { + + it('sets sourceUrl to null', function () { + const newState = {animationPicker: { uploadFilename: "filename.jpg", goal: Goal.NEW_ANIMATION }}; + var store = createStore(combineReducers({animationList: listReducer, animationPicker: reducer}), newState); + + var onMetadataLoaded = animationPickerModule.buildOnImageMetadataLoaded( + "filename.jpg", Goal.NEW_ANIMATION, "filename.jpg", + {filename: "filename.jpg", result: 0, versionId: "string"}, store.dispatch); + onMetadataLoaded({}); + + const newListState = store.getState().animationList; + const animationKey = newListState.orderedKeys[0]; + + expect(newListState.propsByKey[animationKey]).to.be.not.null; + expect(newListState.propsByKey[animationKey].name).to.equal("filename.jpg"); + expect(newListState.propsByKey[animationKey].sourceUrl).to.be.null; + }); + }); }); }); From 4ccf8fa5160279efe49e513072026eef36edddd8 Mon Sep 17 00:00:00 2001 From: Erin Date: Wed, 21 Feb 2018 14:11:36 -0800 Subject: [PATCH 07/45] Styling changes and removed redundent parameters --- .../gamelab/AnimationPicker/animationPickerModule.js | 10 +++------- .../AnimationPicker/animationPickerModuleTest.js | 11 +++++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/src/gamelab/AnimationPicker/animationPickerModule.js b/apps/src/gamelab/AnimationPicker/animationPickerModule.js index 1870a28d8b51f..ca46f38e53a41 100644 --- a/apps/src/gamelab/AnimationPicker/animationPickerModule.js +++ b/apps/src/gamelab/AnimationPicker/animationPickerModule.js @@ -107,22 +107,18 @@ export function beginUpload(filename) { */ export function handleUploadComplete(result) { return function (dispatch, getState) { - const { goal, uploadFilename } = getState().animationPicker; const key = result.filename.replace(/\.png$/i, ''); const sourceUrl = animationsApi.basePath(key + '.png'); - const onImageMetadataLoaded = buildOnImageMetadataLoaded(uploadFilename, goal, key, result, dispatch); - // TODO (bbuchanan): This sequencing feels backwards. Eventually, we - // ought to preview and get dimensions from the local filesystem, async - // with the upload itself, but that will mean refactoring away from the - // jQuery uploader. + const onImageMetadataLoaded = buildOnImageMetadataLoaded(key, result, dispatch, getState); loadImageMetadata(sourceUrl, onImageMetadataLoaded, () => { dispatch(handleUploadError(gamelabMsg.animationPicker_failedToParseImage())); }); }; } -export function buildOnImageMetadataLoaded(uploadFilename, goal, key, result, dispatch) { +export function buildOnImageMetadataLoaded(key, result, dispatch, getState) { return (metadata) => { + const { goal, uploadFilename } = getState().animationPicker; const animation = _.assign({}, metadata, { name: uploadFilename, sourceUrl: null, diff --git a/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js b/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js index ff08f80db4f0f..1693e1086833e 100644 --- a/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js +++ b/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js @@ -113,8 +113,15 @@ describe('animationPickerModule', function () { var store = createStore(combineReducers({animationList: listReducer, animationPicker: reducer}), newState); var onMetadataLoaded = animationPickerModule.buildOnImageMetadataLoaded( - "filename.jpg", Goal.NEW_ANIMATION, "filename.jpg", - {filename: "filename.jpg", result: 0, versionId: "string"}, store.dispatch); + "filename.jpg", + { + filename: "filename.jpg", + result: 0, + versionId: "string" + }, + store.dispatch, + store.getState + ); onMetadataLoaded({}); const newListState = store.getState().animationList; From 4bf083c47435f93683969eac5266b7328013cdea Mon Sep 17 00:00:00 2001 From: Erin Date: Fri, 23 Feb 2018 00:11:43 -0800 Subject: [PATCH 08/45] Add check for valid db connection --- lib/cdo/db.rb | 10 +++++++++- lib/cdo/metrics_helper.rb | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/cdo/db.rb b/lib/cdo/db.rb index 9476399b99b78..c69c269601abb 100644 --- a/lib/cdo/db.rb +++ b/lib/cdo/db.rb @@ -1,6 +1,7 @@ require 'sequel' +require 'sequel/connection_pool/threaded' -def sequel_connect(writer, reader) +def sequel_connect(writer, reader, validation_frequency = nil) reader = reader.gsub 'mysql:', 'mysql2:' writer = writer.gsub 'mysql:', 'mysql2:' @@ -14,6 +15,13 @@ def sequel_connect(writer, reader) db.extension :server_block + # Check if validation frequency is valid and then use it to specify how often to + # verify the db connection + if !validation_frequency.nil? && (validation_frequency == -1 || validation_frequency > 0) + db.extension(:connection_validator) + db.connection_validation_timeout = validation_frequency + end + # Uncomment this for Pegasus logging. Only appears to work when started # using bin/pegasus-server. #db.loggers << $log if rack_env?(:development) && $log diff --git a/lib/cdo/metrics_helper.rb b/lib/cdo/metrics_helper.rb index a8611f7a27bff..6ede75087a48a 100644 --- a/lib/cdo/metrics_helper.rb +++ b/lib/cdo/metrics_helper.rb @@ -2,7 +2,7 @@ module Metrics DEVINTERNAL_DB = CDO.devinternal_db_writer ? - sequel_connect(CDO.devinternal_db_writer, CDO.devinternal_db_writer) : nil + sequel_connect(CDO.devinternal_db_writer, CDO.devinternal_db_writer, -1) : nil # Values for DTT metrics. AUTOMATIC = 0 From db0087197e6ddd2fd93050f6f4e3edf488f76d2b Mon Sep 17 00:00:00 2001 From: Suresh Chanmugam Date: Fri, 23 Feb 2018 07:37:42 -0600 Subject: [PATCH 09/45] Import HocBeyondTutorials Google Sheet in v3 format where column name suffixes indicate type, using new option to strip column data type suffixes --- pegasus/data/beyond_tutorials.gsheet | 2 ++ pegasus/data/cdo-glossary-csf.csv | 3 ++- pegasus/data/cdo-team.csv | 2 +- pegasus/rake/seed.rake | 40 +++++++++++++++------------- 4 files changed, 27 insertions(+), 20 deletions(-) create mode 100644 pegasus/data/beyond_tutorials.gsheet diff --git a/pegasus/data/beyond_tutorials.gsheet b/pegasus/data/beyond_tutorials.gsheet new file mode 100644 index 0000000000000..9f22e9ec99085 --- /dev/null +++ b/pegasus/data/beyond_tutorials.gsheet @@ -0,0 +1,2 @@ +TestSuresh/beyond_tutorials +exclude_columns: ['notes_t'] diff --git a/pegasus/data/cdo-glossary-csf.csv b/pegasus/data/cdo-glossary-csf.csv index e768c54c8d8e9..32a21c3d66f08 100644 --- a/pegasus/data/cdo-glossary-csf.csv +++ b/pegasus/data/cdo-glossary-csf.csv @@ -28,7 +28,8 @@ call,call (a function),This is the piece of code that you add to a program to in click,click,Press the mouse button.,,,,,,,,,,,, code,code,The language that programmers create and use to tell a computer what to do.,Course 1: Stage 2,Course 1: Stage 2,,,,,,,,,, command,command,An instruction for the computer. Many commands put together make up algorithms and computer programs.,Course 1: Stage 2,Course 1: Stage 2,,,,,,,,,, -computational thinking,computational thinking,"Mental processes and strategies that include: decomposition, pattern matching, abstraction, algorithms (decomposing problems into smaller, more manageable problems, finding repeating patterns, abstracting specific differences to make one solution work for multiple problems, and creating step-by-step algorithms).",Course 3: Stage 1,Course 3: Stage 1,,,,,,,,,, +computational thinking,computational thinking,"Modifying a problem in such a way that it can be modeled or solved using a computer or machine. +Strategies include: decomposition, pattern matching, abstraction, algorithms.",Course 3: Stage 1,Course 3: Stage 1,,,,,,,,,, computer science,computer science,Using the power of computers to solve problems.,Course 1: Stage 2,Course 1: Stage 2,,,,,,,,,, conditionals,conditionals,Statements that only run under certain conditions.,"Course 2: Stages 12, 13
Course 3: Stages 7, 8","Course 2: Stages 12, 13 | diff --git a/pegasus/data/cdo-team.csv b/pegasus/data/cdo-team.csv index 75692c3df4519..a71ea25cb66d1 100644 --- a/pegasus/data/cdo-team.csv +++ b/pegasus/data/cdo-team.csv @@ -136,7 +136,7 @@ Kate Brunette,,https://www.linkedin.com/in/brunettekate,Partnerships and Campaig Katie Apone,,https://www.linkedin.com/in/katieapone/,Program Manager ,extended_inactive Katie Kurtz,,http://www.linkedin.com/in/misskatiek,Grant Writer,extended_inactive Kevin Scannell,,https://www.linkedin.com/in/kscanne,Irish Translator,translator -Koichi Ito,,https://j-code.org ,Japanese Translator,translator +Koichi Ito,,https://j-code.org,Japanese Translator,translator Laura Kline,,http://www.linkedin.com/in/lkline/,Marketing & Product Manager,extended_inactive Lauren Selig,,http://www.imdb.com/name/nm0783147/,Producer,extended_inactive Lesley Chilcott,,http://www.imdb.com/name/nm2026261/,Filmmaker,extended_inactive diff --git a/pegasus/rake/seed.rake b/pegasus/rake/seed.rake index 62c2f49e76edc..d29e3436be1d8 100644 --- a/pegasus/rake/seed.rake +++ b/pegasus/rake/seed.rake @@ -7,6 +7,7 @@ class CsvToSqlTable @db = params[:db] || DB @path = path @table = File.basename(@path, File.extname(@path)).tr('-', '_').to_sym + @remove_type_suffix = (@table == :beyond_tutorials) ? true : false end def up_to_date? @@ -29,9 +30,10 @@ class CsvToSqlTable at = 1 CSV.open(@path, 'rb') do |csv| - table, columns = create_table(csv.shift) + csv_column_names = csv.shift + table, db_column_names = create_table(csv_column_names) while values = csv.shift - table.insert(hash_from_keys_and_values(columns, values).merge({id: at += 1})) + table.insert(hash_from_column_names_and_values(db_column_names, csv_column_names, values).merge({id: at += 1})) end end @@ -44,12 +46,13 @@ class CsvToSqlTable private - def hash_from_keys_and_values(keys, values) + def hash_from_column_names_and_values(db_column_names, csv_column_names, values) h = {} - (0..keys.count - 1).each do |i| - key_name = keys[i].to_s + (0..db_column_names.count - 1).each do |i| + csv_column_name = csv_column_names[i].to_s + type_suffix = csv_column_name[csv_column_name.rindex('_')..-1] value = - case key_name[key_name.rindex('_')..-1] + case type_suffix when '_b' values[i].to_bool when '_f' @@ -60,13 +63,13 @@ class CsvToSqlTable values[i] end - h[keys[i]] = value + h[db_column_names[i]] = value end h end - def create_table(columns) - schema = columns.map {|column| column_name_to_schema(column)} + def create_table(csv_column_names) + schema = csv_column_names.map {|csv_column_name| column_name_to_schema(csv_column_name)} DB.create_table!(@table, charset: 'utf8') do primary_key :id @@ -81,20 +84,20 @@ class CsvToSqlTable [DB[@table], schema.map {|i| i[:name]}] end - def column_name_to_schema(name) - i = name.rindex('_') + def column_name_to_schema(csv_column_name) + i = csv_column_name.rindex('_') if i.nil? - ChatClient.log "Bad column name (#{name}) for table (#{@table}), see this " \ + ChatClient.log "Bad column name (#{csv_column_name}) for table (#{@table}), see this " \ "Google Drive folder." end - if name.ends_with?('!') || name.ends_with?('*') - type_flag = name[-1..-1] - name = name[0..-2] + if csv_column_name.ends_with?('!') || csv_column_name.ends_with?('*') + type_flag = csv_column_name[-1..-1] + csv_column_name = csv_column_name[0..-2] end - type_info = name[i..-1] + type_info = csv_column_name[i..-1] type = { '_b' => {type: 'boolean'}, @@ -110,7 +113,9 @@ class CsvToSqlTable type = type.merge(unique: true) if type_flag == '!' type = type.merge(index: true) if type_flag == '*' - type.merge({name: name.to_sym}) + db_column_name = @remove_type_suffix ? csv_column_name.rpartition('_')[0] : csv_column_name + + type.merge({name: db_column_name.to_sym}) end def set_table_mtime(mtime) @@ -238,7 +243,6 @@ namespace :seed do sync_tasks = [] imports = { - beyond_tutorials: 'Data/HocBeyondTutorials.gsheet', tutorials: 'Data/HocTutorials.gsheet' } From 4241c04683258d01f0808e7cba1fd42d8bc04fee Mon Sep 17 00:00:00 2001 From: Suresh Chanmugam Date: Fri, 23 Feb 2018 11:57:41 -0600 Subject: [PATCH 10/45] revert accidental content change in previous commit --- pegasus/data/cdo-glossary-csf.csv | 3 +-- pegasus/data/cdo-team.csv | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pegasus/data/cdo-glossary-csf.csv b/pegasus/data/cdo-glossary-csf.csv index 32a21c3d66f08..e768c54c8d8e9 100644 --- a/pegasus/data/cdo-glossary-csf.csv +++ b/pegasus/data/cdo-glossary-csf.csv @@ -28,8 +28,7 @@ call,call (a function),This is the piece of code that you add to a program to in click,click,Press the mouse button.,,,,,,,,,,,, code,code,The language that programmers create and use to tell a computer what to do.,Course 1: Stage 2,Course 1: Stage 2,,,,,,,,,, command,command,An instruction for the computer. Many commands put together make up algorithms and computer programs.,Course 1: Stage 2,Course 1: Stage 2,,,,,,,,,, -computational thinking,computational thinking,"Modifying a problem in such a way that it can be modeled or solved using a computer or machine. -Strategies include: decomposition, pattern matching, abstraction, algorithms.",Course 3: Stage 1,Course 3: Stage 1,,,,,,,,,, +computational thinking,computational thinking,"Mental processes and strategies that include: decomposition, pattern matching, abstraction, algorithms (decomposing problems into smaller, more manageable problems, finding repeating patterns, abstracting specific differences to make one solution work for multiple problems, and creating step-by-step algorithms).",Course 3: Stage 1,Course 3: Stage 1,,,,,,,,,, computer science,computer science,Using the power of computers to solve problems.,Course 1: Stage 2,Course 1: Stage 2,,,,,,,,,, conditionals,conditionals,Statements that only run under certain conditions.,"Course 2: Stages 12, 13
Course 3: Stages 7, 8","Course 2: Stages 12, 13 | diff --git a/pegasus/data/cdo-team.csv b/pegasus/data/cdo-team.csv index a71ea25cb66d1..75692c3df4519 100644 --- a/pegasus/data/cdo-team.csv +++ b/pegasus/data/cdo-team.csv @@ -136,7 +136,7 @@ Kate Brunette,,https://www.linkedin.com/in/brunettekate,Partnerships and Campaig Katie Apone,,https://www.linkedin.com/in/katieapone/,Program Manager ,extended_inactive Katie Kurtz,,http://www.linkedin.com/in/misskatiek,Grant Writer,extended_inactive Kevin Scannell,,https://www.linkedin.com/in/kscanne,Irish Translator,translator -Koichi Ito,,https://j-code.org,Japanese Translator,translator +Koichi Ito,,https://j-code.org ,Japanese Translator,translator Laura Kline,,http://www.linkedin.com/in/lkline/,Marketing & Product Manager,extended_inactive Lauren Selig,,http://www.imdb.com/name/nm0783147/,Producer,extended_inactive Lesley Chilcott,,http://www.imdb.com/name/nm2026261/,Filmmaker,extended_inactive From 11b76a682634137bf114b0e602e7951fcb520049 Mon Sep 17 00:00:00 2001 From: Suresh Chanmugam Date: Fri, 23 Feb 2018 15:03:32 -0600 Subject: [PATCH 11/45] add initial content commit of the new Google Sheet converted to CSV --- pegasus/data/beyond_tutorials.csv | 59 +++++++++++++++++++++++++++++++ pegasus/rake/seed.rake | 1 + 2 files changed, 60 insertions(+) create mode 100644 pegasus/data/beyond_tutorials.csv diff --git a/pegasus/data/beyond_tutorials.csv b/pegasus/data/beyond_tutorials.csv new file mode 100644 index 0000000000000..5e91fefe7fefe --- /dev/null +++ b/pegasus/data/beyond_tutorials.csv @@ -0,0 +1,59 @@ +displayweight_i,code_s,name_s,orgname_s,url_s,shortdescription_s,longdescription_t,platformtext_s,gradelevel_s,image_s,tags_s,teachers_notes_s,language_s,languages_supported_s,short_code_s +10000,codeorg_beyond,Intro courses for all ages,Code.org,http://studio.code.org/,Loved the Hour of Code? Try our suite of 20 hour courses.,"Loved the Hour of Code but want more? Learn basic computer science with our suite of classroom-ready courses for different ages (even kindergarten). Lessons blend game-like tutorials with unplugged classroom activities, and short video lectures with Bill Gates, Mark Zuckerberg, Angry Birds and more. Learn repeat-loops, conditionals, algorithms, functions, and variables.","Modern web browsers, tablets",All ages,/images/logo_carousel.jpg,Beginner,http://code.org/educate/k5,"Spanish, Portuguese, Italian, Swedish, German, French, Dutch, Greek","ar,ca,cs,da,de,el,en,es,fa,fi,fr,he,hu,id,is,it,ja,ko,nl,no,pl,pt,ro,ru,sr,sv,tr,uk,zh-tw,zh-cn", +9700,codecademyUniv_beyond,Codecademy,Codecademy,http://www.codecademy.com/,"Learn JavaScript programming, in a web-browser","Codecademy is an interactive, student-guided introduction to the basics of CS through JavaScript that's used by tens of millions of students around the world. We've prepared a no-hassle Hour of Code experience with accompanying quizzes, slides, and a completed project for students at the end.","Modern web browsers. iOS, Android apps",High school,/images/codecademy.png,JavaScriptUniv,,Spanish,es, +9600,codeschoolUniv_beyond,JavaScript Roadtrip,Code School,https://www.codeschool.com/courses/javascript-road-trip-part-1,An introduction to the very basics of the JavaScript language.,"An introduction to the very basics of the JavaScript language. Build a foundation of JavaScript syntax and learn how to use values, variables, and files.","Modern web browsers ($$ required, Part 1 free)",Ages 14+,/images/codeschool.jpg,JavaScriptUniv,,,, +9500,khan_beyond,Learn computer programming,Khan Academy,https://www.khanacademy.org/cs,Learn to draw in JavaScript,Learn the basics of JavaScript programming while creating fun drawings with your code. Do it on your own or with your class!,Modern web browsers,Middle school +,/images/khanacademy.png,"JavaScript,JavaScriptUniv,International",http://khanacademy.org/r/hour-of-code-for-teachers,Spanish,es, +9400,tynkerMid_beyond,Learn programming at home,Tynker,http://www.tynker.com/,A fun and engaging course for children in grades 4-9,"Tynker makes it fun & easy to learn programming. It makes it visual. Kids build games and mobile apps by arranging blocks of code. It removes the need to know programming syntax. Kids transform ideas into animated stories and math art right away. It promotes progressive learning. As kids learn fundamentals, Tynker introduces more advanced concepts including syntax driven programming.",Modern web browsers,Ages 5-14,/images/tynker.png,BeginnerMid,,,, +9300,scratchMid_beyond,Get creative with coding,Scratch,https://scratch.mit.edu/hoc,"Create interactive games, stories, and animations.","With Scratch, you can create your own interactive games, stories, animations — and share them with your friends. Get started by animating your name, creating a holiday card, or making a pong game.",Any browser,Ages 8+,/images/scratch.png,BeginnerMid,http://scratched.gse.harvard.edu/hoc/,,, +9200,hopscotch_beyond,Hopscotch: Programming On Your iPad,Hopscotch,http://gethopscotch.com/,Learn visual programming on an iPad,Student-guided tutorial on the iPad using the Hopscotch programming language. Students will build games and apps for their iPad on their iPad. Students can work individually or with friends (up to 3 per iPad).,iPad,Middle school +,/images/hopscotch.png,"Beginner,BeginnerMid,MakeApps,Mobile,MobTabElem,MobTabMid",,,, +9150,scratchjr_beyond,Playful programming for young kids,ScratchJr,http://www.scratchjr.org/teach.html,Create interactive stories and games using ScratchJr.,"ScratchJr is an introductory programming language that enables young children to create their own interactive stories and games. Children snap together graphical programming blocks to make characters move, jump, dance, and sing",iPad,Elementary,/images/scratchjr.png,"Apps,Mobile,Prereader",,,, +9100,codespark_beyond,Full Curriculum for codeSpark Academy with The Foos,codeSpark,http://thefoos.com/coding-resources-for-you/,A 10 lesson curriculum is available for FREE at thefoos.com. Each lesson includes a printable and unplugged activity. Learn the ABCs of computer science by helping the Foos solve puzzles. Then use your coding skills to create and share video games.,A 10 lesson curriculum is available for FREE at thefoos.com. Each lesson includes a printable and unplugged activity. Learn the ABCs of computer science by helping the Foos solve puzzles. Then use your coding skills to create and share video games.,"iPhone, iPad, Android phone and tablet, Browser (first 3 lessons)",Grades K-5,/images/tutorials/new/codesparkbeyond.jpg,"Apps,Mobile,MobTabElem,Prereader",,,, +9050,kodable_beyond,Kodable,Kodable,https://itunes.apple.com/us/app/kodable/id577673067?mt=8,A fun iPad game to teach computer programming concepts,"Kodable is a self-guided iPad game that introduces kids 5+ to programming basics. Having a teacher or parent nearby is optimal, but not necessary.",iPad,Elementary,/images/kodable.png,"Apps,Mobile,International,Prereader",,,, +9025,vidcode_beyond,Code your Pixels,Vidcode,https://www.vidcode.com/beyond-hoc,"Learn JavaScript by bringing your photos and videos to life. Create filters, graphics, emojis, and special effects with step-by-step project tutorials. Learn code through something teens & tweens love.","Learn JavaScript by bringing your photos and videos to life. Create filters, graphics, emojis, and special effects with step-by-step project tutorials. Learn code through something teens & tweens love.","Modern web browsers, Tablet, Chromebook",Grades 5-12,/images/tutorials/beyond/vidcode.png,BeyondBlocks,https://static1.squarespace.com/static/53baf4ebe4b06cb7b6c4ea8a/t/598cc863197aea1ac7d91714/1502398564467/Cross-curricular+Coding+Guide.pdf,,, +9000,tynker_beyond,Learn programming at home,Tynker,http://www.tynker.com/,A fun and engaging course for children in grades 4-8,"Tynker makes it fun & easy to learn programming. It makes it visual. Kids build games and mobile apps by arranging blocks of code. It removes the need to know programming syntax. Kids transform ideas into animated stories and math art right away. It promotes progressive learning. As kids learn fundamentals, Tynker introduces more advanced concepts including syntax driven programming.",Modern web browsers,Ages 5-13,/images/tynker.png,"Beginner,BeyondBlocks,Desktop,MobTabElem",,,, +8500,scratch_beyond,Get creative with coding,Scratch,https://scratch.mit.edu/hoc,"Create interactive games, stories, and animations.","With Scratch, you can create your own interactive games, stories, animations — and share them with your friends. Get started by animating your name, creating a holiday card, or making a pong game.",Any browser,Ages 8+,/images/scratch.png,"Beginner,IE8,International,IDE,Desktop,NoInternet",http://scratched.gse.harvard.edu/hoc/,,, +8400,kodu_beyond,Kodu,Microsoft,http://www.kodugamelab.com/hour-of-code/,Design a 3D game world,"Kodu lets kids create games on the PC and Xbox via a simple visual programming language. Kodu can be used to teach creativity, problem solving, storytelling, as well as programming. Anyone can use Kodu to make a game, young children as well as adults with no design or programming skills. Kodu for the PC is available to download for free. Kodu for the Xbox is also available in the USA on the Xbox Marketplace, in the Indie Games channel for about $5.","Windows, xBox",Ages 8+,/images/kodu.png,"MakeApps,NoInternet",,,, +8000,lightbot_beyond,LightBot,LightBot,http://lightbot.com/,A game to teach coding concepts,"Learn core programming logic, starting from super-basic programming, for ages 4+, on iOS or Android (or Web browser) . Learn how to sequence commands, identify patterns, use procedures, and utilize loops!","iOS, Android (or web browser)",All ages,/images/lightbot.png,"IE8,International,Apps,Mobile,MobTabElem",,"French, Russian, Brazilian Portuguese","fr,ru,pt", +7950,pocketcode_beyond,Pocket Code,Catrobat,https://pocketcode.org/hourOfCode,Create a game on your smartphone and share with friends to try!,Create your own game on your smart phone with Pocket Code! Help skydiver Steve to deliver his parcels. You can share it with friends and other users to try!,Mobile App,Middle school +,/images/pocketcode.jpg,"Apps,Mobile,MobTabMid",,,"de,de-at,fr,fr-ca,es,es-us,hu,it,kr,nl,pl,pt,ro,ru,tr", +7900,cargobot_beyond,Cargobot,Two Lives Left,http://twolivesleft.com/CargoBot/,Program a robot arm on your iPad,"Cargo-Bot is a puzzle game where you teach a robot how to move crates. Sounds simple, right? It features 36 fiendishly clever puzzles, haunting music and stunning retina graphics. You can even record your solutions and share them on YouTube to show your friends.",iPad,Ages 8+,/images/cargobot.png,"Apps,MobTabElem,MobTabMid",,,, +7600,codehs_beyond,Learn to Code With Karel the Dog,CodeHS,http://codehs.com/,Learn JavaScript programming with a fun visual environment,"Learn the basics of programming with Karel the Dog, a fun, accessible and visual introduction to coding, where giving commands to a computer is just like giving commands to a dog. This tutorial is great if led by a teacher, but can also be done independently.",Modern web browsers,High school,/images/codehs.png,"JavaScript,JavaScriptUniv",https://docs.google.com/a/codehs.com/document/d/1f4towRwppfVsbsmM7HjmOlXiQFV-zjTe4oXjKwu-YYM/edit,,, +7500,codecademy_beyond,Codecademy,Codecademy,http://www.codecademy.com/,"Learn JavaScript programming, in a web-browser","Codecademy is an interactive, student-guided introduction to the basics of CS through JavaScript that's used by tens of millions of students around the world. We've prepared a no-hassle Hour of Code experience with accompanying quizzes, slides, and a completed project for students at the end.","Modern web browsers. iOS, Android apps",High school,/images/codecademy.png,"JavaScript,Mobile,International",,Spanish,es, +7490,w3schoolsUniv_beyond,Learn HTML,w3schools.com,http://www.w3schools.com/,Learn to make web sites,Learn to create websites on your own computer. Learn the server basics in less than a day. Learn to add databases to your website,Modern web browsers,Ages 12+,/images/w3schools.png,WebUniv,,,, +7450,mozilla_beyond,Thimble,Mozilla,https://thimble.mozilla.org/,Create and share web pages,"Thimble is an educational code editor for teaching the learning the Web. With Thimble, you can write and edit HTML, CSS, and JavaScript on the left side of your screen, and watch your code come to life on the right. Start with simple remixes, changing words and images on a page to familiarize yourself with code and build confidence. Or work your way to creating entire web pages from scratch. Perfect for beginners and experts alike.",Modern web browsers,Ages 12+,/images/thimble.png,"Web,WebUniv",,,, +7425,w3schools_beyond,Learn HTML,w3schools.com,http://www.w3schools.com/,Learn to make web sites,Learn to create websites on your own computer. Learn the server basics in less than a day. Learn to add databases to your website,Modern web browsers,Ages 12+,/images/w3schools.png,Web,,,, +7400,codeavengers_beyond,Learn to Code Websites,Code Avengers,http://codeavengers.com/,Learn to code games and websites,"Learn how to program games, apps and websites. Designed by experts with perfect level of difficulty for beginners, easy to understand instructions and great help when you need it. Our HTML, CSS and JavaScript courses include code challenges and revision games that make learning fun and effective for all ages.",Modern web browsers,Middle school +,/images/codeavengers.png,"JavaScript,Web,WebUniv",,,, +7300,codecombat_beyond,CodeCombat,CodeCombat,http://codecombat.com/?hour_of_code=true,Play a game and learn JavaScript to win,Defeat ogres to learn Python or JavaScript in this epic programming game!,Modern web browsers,Middle school +,/images/codecombat.png,JavaScript,http://codecombat.com/teachers,,, +6500,codesters_beyond,Intro to Codesters,Codesters,http://www.codesters.com/curriculum/intro-to-codesters/Building+your+First+Program/1/,"Codesters is built for teaching coding in schools. Students learn core coding skills with project-based lessons and our unique text-based coding environment. We provide built-in course management, lesson plans, and more for teachers.","Codesters is built for teaching coding in schools. Our project-based lessons ensure that students learn core coding skills while they create engaging, interactive programs. Our unique coding environment makes text-based coding easy and accessible for students in middle grades – even those with no block-coding experience. And Codesters eases the burden on teachers by providing built-in course management, automatic feedback, lesson plans, and more.",Modern web browsers,Grades 4-10,/images/tutorials/beyond/codesters.png,OtherLang,http://bit.ly/CodestersLessonPlan1,,, +6000,groklearning_beyond,Learn Python programming,Grok Learning,https://groklearning.com/,Basic intro to python programming,An introductory course using the programming language Python for people with no programming experience. Our unique mix of introductory content and challenges will bring you to a thorough understanding of Python and programming itself. We've taught this content to students of varying ages from diverse backgrounds and we're sure it'll suit you too.,Modern web browsers,Middle school +,/images/groklearning.png,OtherLang,,,, +5000,robomind_beyond,Program a virtual robot,RoboMind Academy,http://www.robomindacademy.com/,Write code for a virtual robot,"Students learn the basics of programming by controling their own virtual robot. The online course is fully self-contained with short presentations, movies, quizzes and automatic guidance/hints to help with the programming exercises. ","Modern web browsers, Mobile web",Ages 8-13,/images/robomind.jpg,OtherLang,http://www.robomindacademy.com/go/navigator/coursedetails?course=HourOfCode,Dutch,, +4500,makeschool_beyond,Build an iPhone game in your browser!,MakeSchool,http://hoc.makeschool.com/build-an-iphone-game-in-your-browser,Make an iPhone game! Learn by writing code to teach your monster new moves!,"Learn to code by making an iPhone game using a brand new and beginner-friendly programming language called Swift! Create a Pokémon-inspired action game and write code to teach your monster new moves. You will learn how to use variables, methods, and objects to help your monster win!",Modern web browsers,High school,/images/makegameswithus.png,"OtherLang,OtherLangUniv,MakeApps",,,, +4400,codeschool_beyond,JavaScript Roadtrip,Code School,https://www.codeschool.com/courses/javascript-road-trip-part-1,An introduction to the very basics of the JavaScript language.,"An introduction to the very basics of the JavaScript language. Build a foundation of JavaScript syntax and learn how to use values, variables, and files.","Modern web browsers ($$ required, Part 1 free)",Ages 14+,/images/codeschool.jpg,"OtherLang,JavaScript",,,, +4000,appinventor_beyond,AppInventor Hour of Code,MIT Center for Mobile Learning @ The Media Lab,http://appinventor.mit.edu/,Make your own app! (Android-only),"Entertaining, quick video tutorials walk you through building three simple apps for your Android phone or tablet. Designed for novices and experts alike, this hour of code will get you ready to start building your own apps before you know it. Imagine sharing your own app creations with your friends! These activities are suitable for individuals and for teachers leading classes.",Modern web browser + Android,Middle school +,/images/appinventor.jpg,"Beginner,BeginnerMid,MakeApps,IDE",http://appinventor.mit.edu/hour-of-code/teach,,, +3500,touchdevelop_beyond,TouchDevelop,Microsoft Research,https://www.touchdevelop.com/,"Code mobile apps directly on your phone, tablet or laptop","TouchDevelop lets you create apps on iPad, iPhone, Android, PC, Mac, Windows Phone. Our touch-friendly editor makes coding fun, even on your phone or tablet!","Modern web browsers, smartphones, all devices",High school,/images/touchdevelop.png,"MakeApps,Mobile",https://www.touchdevelop.com/docs/hourofcodeeducatornotes,,, +2000,udemy_beyond,Online Programming Courses,Udemy,https://www.udemy.com/courses/Technology,Dozens of online programming courses,"Whether you've never seen a line of code or you code for a living, Udemy has a course for you, taught by professional instructors. [Note: payment is required]",Web based,High school +,/images/udemy.png,"OtherLang,OtherLangUniv,MakeApps",,,, +1500,lynda_beyond,Online Programming Courses,Lynda.com,http://www.lynda.com/Developer-training-tutorials/50-0.html?utm_medium=ldc-partner&utm_source=SSPRC&utm_content=524&utm_campaign=CD14814&bid=524&aid=CD14814&opt=,Dozens of online programming courses,"Learn how to code, create, and build web applications, from the foundations of object-oriented programming in C and C++, to how to write Java. Our developer tutorials can help you learn to develop and create mobile apps, work with PHP and MySQL databases, get started with the statistical processing language R, and much more. [Note: payment is required]",Web based ($$ required),High school +,/images/lynda.png,"OtherLang,OtherLangUniv",,,, +1400,codeschoolUniv2_beyond,JavaScript Roadtrip,Code School,https://www.codeschool.com/courses/javascript-road-trip-part-1,An introduction to the very basics of the JavaScript language.,"An introduction to the very basics of the JavaScript language. Build a foundation of JavaScript syntax and learn how to use values, variables, and files.","Modern web browsers ($$ required, Part 1 free)",Ages 14+,/images/codeschool.jpg,OtherLangUniv,,,, +1300,groklearningUniv_beyond,Learn Python programming,Grok Learning,https://groklearning.com/,Basic intro to python programming,An introductory course using the programming language Python for people with no programming experience. Our unique mix of introductory content and challenges will bring you to a thorough understanding of Python and programming itself. We've taught this content to students of varying ages from diverse backgrounds and we're sure it'll suit you too.,Modern web browsers,Middle school +,/images/groklearning.png,OtherLangUniv,,,, +1000,edx_beyond,Harvard CS50 class,EdX,https://www.edx.org/course-list/allschools/computer-science/allcourses,The most popular class at Harvard,"CS50 is Harvard University's introduction to the intellectual enterprises of computer science and the art of programming. Topics include abstraction, algorithms, data structures, encapsulation, resource management, security, software engineering, and web development. Languages include C, Python, SQL, and JavaScript plus CSS and HTML. Problem sets inspired by real-world domains of biology, cryptography, finance, forensics, and gaming. CS50 is Harvard's largest course and freely available.",Modern web browsers,University,/images/edx.png,"Univ,International",,,, +900,coursera_beyond,Stanford CS 101 class,Coursera,https://www.coursera.org/course/cs101,Start with CS101,"Stanford's CS 101 class taught by Nick Parlante (FREE!). CS101 teaches the essential ideas of Computer Science for a zero-prior-experience audience. Play and experiment with short bits of code to bring to life to the power and limitations of computers. CS101 also provides a general background on computers today: what is a computer, what is hardware, what is software, what is the internet. No previous experience is required other than the ability to use a web browser.",Modern web browsers,University,/images/coursera.png,Univ,,,, +800,udacity_beyond,CS 101,Udacity,https://www.udacity.com/course/cs101,Start with CS101,"In this course you will learn key concepts in computer science and learn how to write your own computer programs in the context of building a web crawler. There is no prior programming knowledge needed for this course. Beginners are welcome! At the end of this course, you will have learned key concepts in computer science and enough programming to be able to write Python programs to solve problems on your own. This course will prepare you to move on to intermediate-level computing courses.",Modern web browsers,University,/images/udacity.png,Univ,,,, +700,teachingtree_beyond,University courses online,Teaching Tree,http://www.teachingtree.co/,Online Knowledge: Rapid and Unconstrained,TeachingTree is an open platform that lets anybody organize educational content. Our goal is for students to quickly access the exact clips they need in order to learn individual concepts. Everyone is encouraged to help by adding videos or tagging concepts.,Web based,University,/images/teachingtree.png,Univ,,,, +400,kidsruby_beyond,KidsRuby,KidsRuby,http://www.kidsruby.com/,Learn to program using Ruby,"Have fun and make games, or hack your homework using Ruby! Just tell your parents or teachers you're learning Ruby programming... ;). Free and works on any computer. [Note: Desktop install required]",Desktop install,Ages 12+,/images/kidsruby.png,OtherLang,,,, +300,codea_beyond,Codea,Two Lives Left,http://twolivesleft.com/Codea/,Make apps on your iPad,"Codea for iPad lets you create games and simulations — or just about any visual idea you have. Turn your thoughts into interactive creations that make use of iPad features like Multi-Touch and the accelerometer. We think Codea is the most beautiful code editor you'll use, and it's easy. Codea is designed to let you touch your code. Want to change a number? Just tap and drag it. How about a color, or an image? Tapping will bring up visual editors that let you choose exactly what you want.",iPad,High school +,/images/codea.png,MakeApps,,,, +200,legoMid_beyond,LEGO® MINDSTORMS® Education EV3,LEGO Education,https://education.lego.com,Build and program a robot,Build and code with the ultimate playful learning experience. The endless flexibility of LEGO bricks and the open-ended nature of our curriculum and coding software allows students to explore and develop their ideas as far as their curiosity will take them. We empower teachers with the tools for creating and delivering the most engaging lessons in STEM for every student.,Robot purchase,Middle school +,/images/lego.jpeg,RobotsMid,,,, +150,microbitMid_beyond,Introduction to Physical Computing,micro:bit,https://microbit.org/code/,"The micro:bit is a fun, handheld, easily programmable computer that uses a web browser to program in blocks, JavaScript or Python.","The micro:bit is a fun, handheld, easily programmable computer that uses a web browser to program in blocks, JavaScript or Python. The free, built-in web simulator allows students to run their program with or without connecting to a micro:bit. Simplicity and ease-of-use make micro:bit perfect for beginners while also having advanced features for experienced programmers and makers.",Modern web browsers,Elementary +,/images/microbit.png,RobotsMid,,,, +100,wonderworkshop_beyond,Wonder Workshop,Coding with Dash & Dot,http://makewonder.com/hourofcode,Dash & Dot are robots that help kids learn the fundamentals of coding.,"Dash & Dot are robots that help kids learn the fundamentals of problem solving and coding while having fun. Download our four free iPad apps to control the robots. Use Path to learn basic sequencing and Blockly to start creating programs for Dash & Dot. Create programs for the robots to deliver a message for you, transform into a creature, and follow you around!",iPad,Elementary +,/images/wonderworkshop.jpg,"Robot,RobotsElem,RobotsMid",,,, +80,lego_beyond,LEGO® MINDSTORMS® Education EV3,LEGO Education,https://education.lego.com,Build and program a robot,Build and code with the ultimate playful learning experience. The endless flexibility of LEGO bricks and the open-ended nature of our curriculum and coding software allows students to explore and develop their ideas as far as their curiosity will take them. We empower teachers with the tools for creating and delivering the most engaging lessons in STEM for every student.,Robot purchase,Middle school +,/images/lego.jpeg,"Robot,RobotsElem",,,, +70,kinderlab_beyond,KIBO,KinderLab Robotics,http://kinderlabrobotics.com/,"Kids build their own robot, program it with wooden blocks, and decorate it with arts and crafts materials","Kids build their own robot with KIBO, program it using wooden blocks, and decorate it with arts and crafts materials - all without a PC, tablet, or smartphone. KIBO invites playful learning and children's creativity and imagination. Curriculum for schools, activities for the home, design journals and workbooks are also available. ",Robot Purchase,Ages 4 to 7,/images/kinderlab.png,"Robot,Prereader,RobotsElem",,,, +60,tickle_beyond,Tickle,Tickle Labs,http://tickleapp.com/,"Program drones, robots, and Arduino, wirelessly","Experience the magic of programming drones, robots, smart homes, and Arduino, all wirelessly! Tickle is easy to learn, fun to use, yet powerful enough for university courses and research projects. You can program a variety of drones and robots to fly, take photos, and navigate through mazes. You can even create custom robots by adding Arduino-based sensors and motors.",iPad,Ages 6+,/images/tickle.png,"Robot,RobotsElem,RobotsMid",,,, +55,kanoelem_beyond,Creative Coding with Kano,Kano Computing ,https://world.kano.me/projects,"Curious minds of all abilities can create art, games, music and more! Follow guided challenges, remix what others have made, and make original creations from scratch. Hours of challenges available with blocks or simple syntax-based coding languages.","Curious minds of all abilities can create art, games, music and more! Follow guided challenges, remix what others have made, and make original creations from scratch. Hours of challenges available with blocks or simple syntax-based coding languages.",Modern web browsers,Elementary +,/images/tutorials/beyond/kanoelem.png,RobotsElem,,,, +52,kanomid_beyond,"Build Computers, Create with Code",Kano,http://kano.me/educators,"Kano's hardware kits, learn-to-code platform, and full curriculum support bring coding to life in the physical world. Build computers, visualize music with exciting pixel light displays, and make games that respond to motion.","Kano is the best solution for demystifying technology whether at home or at school - with exciting hardware kits, an engaging and intuitive learn-to-code platform, and full curriculum support. Kano kits bring coding to life in the physical world - build computers, visualize music with exciting pixel light displays, and make games that respond to motion.",Modern web browsers; Robot purchase,Elementary +,/images/tutorials/beyond/kanomid.png,RobotsMid,,,, +50,sphero_beyond,Sphero SPRK+,Sphero Edu,http://www.sphero.com/education,SPRK lessons give kids a fun crash course in programming robots while sharpening skills in math and science.,"Designed to inspire curiosity, creativity, and invention through connected play and coding, SPRK+ is far more than just a robot. Powered by the Sphero Edu app, you can easily learn programming, complete hands-on activities, and share your creations with the community. Learning is evolving. Get on the ball.",Robot purchase,Ages 8+,/images/sphero.png,"Robot,RobotsElem,RobotsMid",,,, +40,ozobot_beyond,Ozobot,Ozobot,http://ozobot.com/stem-education/stem-lessons,Ozobot is the tiny robot that makes coding fun and easy with extensive resources for grades K-12.,"Ozobot is the tiny robot that makes coding and computer science fun and easy to learn. Choose from a wide variety of STEM lessons and activities ranging from kindergarten to high school grade levels. Whether your students are novices or experts, Ozobot will keep them engaged for hours of learning and fun. Not an experienced programmer yourself? Not a problem! Simple instruction guides, how-to videos and introductory lessons will make you an Ozobot expert in no time!",Modern web browsers,All ages,/images/ozobot.jpg,"Robot,RobotsElem,RobotsMid",,,, +30,finch_beyond,Finch,BirdBrain Technologies,http://www.finchrobot.com/,A robot for computer science education,"The Finch is a new robot for computer science education. Its design is the result of a four year study at Carnegie Mellon's CREATE lab. The Finch is designed to support an engaging introduction to the art of programming. It has support for over a dozen programming languages and environments, including several environments appropriate for students as young as eight years old. The Finch was designed to allow students to write richly interactive programs.",Robot purchase,Ages 8+,/images/finch.png,"Robot,RobotsElem,RobotsMid",,,, +20,microbit_beyond,Introduction to Physical Computing,micro:bit,https://microbit.org/code/,"The micro:bit is a fun, handheld, easily programmable computer that uses a web browser to program in blocks, JavaScript or Python.","The micro:bit is a fun, handheld, easily programmable computer that uses a web browser to program in blocks, JavaScript or Python. The free, built-in web simulator allows students to run their program with or without connecting to a micro:bit. Simplicity and ease-of-use make micro:bit perfect for beginners while also having advanced features for experienced programmers and makers.",Modern web browsers,Elementary +,/images/microbit.png,"Robot,RobotsElem",,,, +10,arduino_beyond,Arduino with Sparkfun,Sparkfun Electronics,https://www.sparkfun.com/hourofcode,Learn Arduino with Sparkfun's Digital Sandbox.,"Arduino is a popular platform designed to allow artists and designers to work with real sensors, LEDs, buzzers, and more. The Sparkfun Virtual Sandbox will teach you real Arduino code right in your browser.","Web browser, then Kit purchase",High school +,/images/arduino.png,"Robot,RobotsMid,RobotsUniv",,,, +9,microbitUniv_beyond,Introduction to Physical Computing,micro:bit,https://microbit.org/code/,"The micro:bit is a fun, handheld, easily programmable computer that uses a web browser to program in blocks, JavaScript or Python.","The micro:bit is a fun, handheld, easily programmable computer that uses a web browser to program in blocks, JavaScript or Python. The free, built-in web simulator allows students to run their program with or without connecting to a micro:bit. Simplicity and ease-of-use make micro:bit perfect for beginners while also having advanced features for experienced programmers and makers.",Modern web browsers,Elementary +,/images/microbit.png,RobotsUniv,,,, +8,legoUniv_beyond,LEGO® MINDSTORMS® Education EV3,LEGO Education,https://education.lego.com,Build and program a robot,Build and code with the ultimate playful learning experience. The endless flexibility of LEGO bricks and the open-ended nature of our curriculum and coding software allows students to explore and develop their ideas as far as their curiosity will take them. We empower teachers with the tools for creating and delivering the most engaging lessons in STEM for every student.,Robot purchase,Middle school +,/images/lego.jpeg,RobotsUniv,,,, +3,processing_beyond,Program with Processing,Processing,http://processing.org/,Learn the Processing languages,"Processing is a programming language, development environment, and online community. Since 2001, Processing has promoted software literacy within the visual arts and visual literacy within technology. Initially created to serve as a software sketchbook and to teach computer programming fundamentals within a visual context, Processing evolved into a development tool for professionals. Today, there are tens of thousands of students, artists, designers, researchers, and hobbyists who use Processing for learning, prototyping, and production.",Modern web browsers,High school,/images/processing.png,IDE,,,, +2,alice_beyond,Alice,The Alice Project,http://www.alice.org/HourOfCode/introduction.html,Teach programming in a 3D environment,"Using an innovative programming environment to support the creation of 3D animations, the Alice Project provides tools and materials for teaching and learning computational thinking, problem solving, and computer programming across a spectrum of ages and grade levels.",Windows or Mac (install required),Middle school +,/images/alice.png,IDE,,,, diff --git a/pegasus/rake/seed.rake b/pegasus/rake/seed.rake index d29e3436be1d8..5e4f93a8ae870 100644 --- a/pegasus/rake/seed.rake +++ b/pegasus/rake/seed.rake @@ -7,6 +7,7 @@ class CsvToSqlTable @db = params[:db] || DB @path = path @table = File.basename(@path, File.extname(@path)).tr('-', '_').to_sym + # TODO: suresh - find a better way to specify which tables should be imported without column name datatype suffixes @remove_type_suffix = (@table == :beyond_tutorials) ? true : false end From da127b81a623e82a596a5556af422344b0f1912c Mon Sep 17 00:00:00 2001 From: bencodeorg Date: Fri, 23 Feb 2018 15:34:26 -0800 Subject: [PATCH 12/45] Include census in forms submitted --- lib/cdo/contact_rollups.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/cdo/contact_rollups.rb b/lib/cdo/contact_rollups.rb index 0e7f16fb05613..251187bef79ee 100644 --- a/lib/cdo/contact_rollups.rb +++ b/lib/cdo/contact_rollups.rb @@ -107,6 +107,7 @@ class ContactRollups ROLE_TEACHER = "Teacher".freeze ROLE_FORM_SUBMITTER = "Form Submitter".freeze + CENSUS_FORM_NAME = "Census".freeze def self.build_contact_rollups start = Time.now @@ -301,16 +302,15 @@ def self.insert_from_dashboard_census_submissions start = Time.now log "Inserting contacts from dashboard.census_submissions" PEGASUS_REPORTING_DB_WRITER.run " - INSERT INTO #{PEGASUS_DB_NAME}.#{DEST_TABLE_NAME} (email, name, roles) - SELECT submitter_email_address, submitter_name, '#{ROLE_FORM_SUBMITTER}' + INSERT INTO #{PEGASUS_DB_NAME}.#{DEST_TABLE_NAME} (email, name, roles, forms_submitted) + SELECT submitter_email_address, submitter_name, '#{ROLE_FORM_SUBMITTER}', '#{CENSUS_FORM_NAME}' FROM #{DASHBOARD_DB_NAME}.census_submissions AS census_submissions WHERE LENGTH(census_submissions.submitter_email_address) > 0 - ON DUPLICATE KEY UPDATE name = #{DEST_TABLE_NAME}.name, - -- Use LOCATE to determine if this role is already present and CONCAT+COALESCE to add it if it is not. - roles = - CASE LOCATE(values(roles), COALESCE(#{DEST_TABLE_NAME}.roles,'')) - WHEN 0 THEN LEFT(CONCAT(COALESCE(CONCAT(#{DEST_TABLE_NAME}.roles, ','), ''),values(roles)),255) - ELSE #{DEST_TABLE_NAME}.roles + ON DUPLICATE KEY + UPDATE #{DEST_TABLE_NAME}.forms_submitted = + CASE LOCATE(values(forms_submitted), COALESCE(#{DEST_TABLE_NAME}.forms_submitted,'')) + WHEN 0 THEN LEFT(CONCAT(COALESCE(CONCAT(#{DEST_TABLE_NAME}.forms_submitted, ','), ''),values(forms_submitted)),255) + ELSE #{DEST_TABLE_NAME}.forms_submitted END" log_completion(start) From 670de7cd68e4bf126f99ff1919c9ce67be74dc82 Mon Sep 17 00:00:00 2001 From: Erin Date: Sun, 25 Feb 2018 15:09:20 -0800 Subject: [PATCH 13/45] Fix typo --- lib/cdo/db.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cdo/db.rb b/lib/cdo/db.rb index c69c269601abb..e64ea0b28d684 100644 --- a/lib/cdo/db.rb +++ b/lib/cdo/db.rb @@ -19,7 +19,7 @@ def sequel_connect(writer, reader, validation_frequency = nil) # verify the db connection if !validation_frequency.nil? && (validation_frequency == -1 || validation_frequency > 0) db.extension(:connection_validator) - db.connection_validation_timeout = validation_frequency + db.pool.connection_validation_timeout = validation_frequency end # Uncomment this for Pegasus logging. Only appears to work when started From 389b3fa708584fdde24323148ef440de2769fba9 Mon Sep 17 00:00:00 2001 From: Erin Date: Mon, 26 Feb 2018 10:37:25 -0800 Subject: [PATCH 14/45] Add comments --- lib/cdo/metrics_helper.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/cdo/metrics_helper.rb b/lib/cdo/metrics_helper.rb index 6ede75087a48a..6908d250d8a68 100644 --- a/lib/cdo/metrics_helper.rb +++ b/lib/cdo/metrics_helper.rb @@ -1,6 +1,8 @@ require_relative './db' module Metrics + # Connect to db. Third param sets frequency to check connection. Currently set + # to check before each request to db. DEVINTERNAL_DB = CDO.devinternal_db_writer ? sequel_connect(CDO.devinternal_db_writer, CDO.devinternal_db_writer, -1) : nil From 3872a706ac15959de751991f9068300fd8a442a1 Mon Sep 17 00:00:00 2001 From: Suresh Chanmugam Date: Mon, 26 Feb 2018 10:57:47 -0800 Subject: [PATCH 15/45] Revert logic that optional strips data type column name suffix --- pegasus/rake/seed.rake | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/pegasus/rake/seed.rake b/pegasus/rake/seed.rake index 5e4f93a8ae870..98d918113e78c 100644 --- a/pegasus/rake/seed.rake +++ b/pegasus/rake/seed.rake @@ -7,8 +7,6 @@ class CsvToSqlTable @db = params[:db] || DB @path = path @table = File.basename(@path, File.extname(@path)).tr('-', '_').to_sym - # TODO: suresh - find a better way to specify which tables should be imported without column name datatype suffixes - @remove_type_suffix = (@table == :beyond_tutorials) ? true : false end def up_to_date? @@ -31,10 +29,9 @@ class CsvToSqlTable at = 1 CSV.open(@path, 'rb') do |csv| - csv_column_names = csv.shift - table, db_column_names = create_table(csv_column_names) + table, columns = create_table(csv.shift) while values = csv.shift - table.insert(hash_from_column_names_and_values(db_column_names, csv_column_names, values).merge({id: at += 1})) + table.insert(hash_from_keys_and_values(columns, values).merge({id: at += 1})) end end @@ -47,13 +44,12 @@ class CsvToSqlTable private - def hash_from_column_names_and_values(db_column_names, csv_column_names, values) + def hash_from_keys_and_values(keys, values) h = {} - (0..db_column_names.count - 1).each do |i| - csv_column_name = csv_column_names[i].to_s - type_suffix = csv_column_name[csv_column_name.rindex('_')..-1] + (0..keys.count - 1).each do |i| + key_name = keys[i].to_s value = - case type_suffix + case key_name[key_name.rindex('_')..-1] when '_b' values[i].to_bool when '_f' @@ -64,13 +60,13 @@ class CsvToSqlTable values[i] end - h[db_column_names[i]] = value + h[keys[i]] = value end h end - def create_table(csv_column_names) - schema = csv_column_names.map {|csv_column_name| column_name_to_schema(csv_column_name)} + def create_table(columns) + schema = columns.map {|column| column_name_to_schema(column)} DB.create_table!(@table, charset: 'utf8') do primary_key :id @@ -85,20 +81,20 @@ class CsvToSqlTable [DB[@table], schema.map {|i| i[:name]}] end - def column_name_to_schema(csv_column_name) - i = csv_column_name.rindex('_') + def column_name_to_schema(name) + i = name.rindex('_') if i.nil? - ChatClient.log "Bad column name (#{csv_column_name}) for table (#{@table}), see this " \ + ChatClient.log "Bad column name (#{name}) for table (#{@table}), see this " \ "Google Drive folder." end - if csv_column_name.ends_with?('!') || csv_column_name.ends_with?('*') - type_flag = csv_column_name[-1..-1] - csv_column_name = csv_column_name[0..-2] + if name.ends_with?('!') || name.ends_with?('*') + type_flag = name[-1..-1] + name = name[0..-2] end - type_info = csv_column_name[i..-1] + type_info = name[i..-1] type = { '_b' => {type: 'boolean'}, @@ -114,9 +110,7 @@ class CsvToSqlTable type = type.merge(unique: true) if type_flag == '!' type = type.merge(index: true) if type_flag == '*' - db_column_name = @remove_type_suffix ? csv_column_name.rpartition('_')[0] : csv_column_name - - type.merge({name: db_column_name.to_sym}) + type.merge({name: name.to_sym}) end def set_table_mtime(mtime) From d7527db6656ea151f10e8dc1fd7f7789fc7f1230 Mon Sep 17 00:00:00 2001 From: Suresh Chanmugam Date: Mon, 26 Feb 2018 11:04:55 -0800 Subject: [PATCH 16/45] rename new version of `beyond_tutorials` table to v3 naming convention --- pegasus/data/beyond_tutorials.gsheet | 2 -- pegasus/data/{beyond_tutorials.csv => cdo-beyond-tutorials.csv} | 0 pegasus/data/cdo-beyond-tutorials.gsheet | 2 ++ 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 pegasus/data/beyond_tutorials.gsheet rename pegasus/data/{beyond_tutorials.csv => cdo-beyond-tutorials.csv} (100%) create mode 100644 pegasus/data/cdo-beyond-tutorials.gsheet diff --git a/pegasus/data/beyond_tutorials.gsheet b/pegasus/data/beyond_tutorials.gsheet deleted file mode 100644 index 9f22e9ec99085..0000000000000 --- a/pegasus/data/beyond_tutorials.gsheet +++ /dev/null @@ -1,2 +0,0 @@ -TestSuresh/beyond_tutorials -exclude_columns: ['notes_t'] diff --git a/pegasus/data/beyond_tutorials.csv b/pegasus/data/cdo-beyond-tutorials.csv similarity index 100% rename from pegasus/data/beyond_tutorials.csv rename to pegasus/data/cdo-beyond-tutorials.csv diff --git a/pegasus/data/cdo-beyond-tutorials.gsheet b/pegasus/data/cdo-beyond-tutorials.gsheet new file mode 100644 index 0000000000000..f7cdafa8ec0ed --- /dev/null +++ b/pegasus/data/cdo-beyond-tutorials.gsheet @@ -0,0 +1,2 @@ +TestSuresh/cdo-beyond-tutorials +exclude_columns: ['notes_t'] From ec1b99d85b50369d45216fbe2af8ab5ec718daba Mon Sep 17 00:00:00 2001 From: Suresh Chanmugam Date: Mon, 26 Feb 2018 11:17:34 -0800 Subject: [PATCH 17/45] continue importing Beyond Tutorials sheet in old format --- pegasus/rake/seed.rake | 1 + 1 file changed, 1 insertion(+) diff --git a/pegasus/rake/seed.rake b/pegasus/rake/seed.rake index 98d918113e78c..62c2f49e76edc 100644 --- a/pegasus/rake/seed.rake +++ b/pegasus/rake/seed.rake @@ -238,6 +238,7 @@ namespace :seed do sync_tasks = [] imports = { + beyond_tutorials: 'Data/HocBeyondTutorials.gsheet', tutorials: 'Data/HocTutorials.gsheet' } From 3773d3fd52e0499e05811b0df36618c054e0f945 Mon Sep 17 00:00:00 2001 From: Erin Bond Date: Mon, 26 Feb 2018 13:12:18 -0800 Subject: [PATCH 18/45] showFeatured param for signed in and signed out public galleries --- apps/src/sites/studio/pages/projects/index.js | 5 ++++- apps/src/sites/studio/pages/projects/public.js | 6 +++++- .../api/v1/projects/public_gallery_controller.rb | 3 ++- dashboard/lib/projects_list.rb | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/src/sites/studio/pages/projects/index.js b/apps/src/sites/studio/pages/projects/index.js index f9f47f382eff8..3e026848d21d0 100644 --- a/apps/src/sites/studio/pages/projects/index.js +++ b/apps/src/sites/studio/pages/projects/index.js @@ -46,10 +46,13 @@ $(document).ready(() => { const isPublic = window.location.pathname.startsWith('/projects/public'); const initialState = isPublic ? Galleries.PUBLIC : Galleries.PRIVATE; store.dispatch(selectGallery(initialState)); + const showFeatured = window.location.href.includes("showFeatured"); + const originalUrl = `/api/v1/projects/gallery/public/all/${MAX_PROJECTS_PER_CATEGORY}`; + const url = showFeatured ? originalUrl + `?showFeatured=1` : originalUrl; $.ajax({ method: 'GET', - url: `/api/v1/projects/gallery/public/all/${MAX_PROJECTS_PER_CATEGORY}`, + url: url, dataType: 'json' }).done(projectLists => { store.dispatch(setProjectLists(projectLists)); diff --git a/apps/src/sites/studio/pages/projects/public.js b/apps/src/sites/studio/pages/projects/public.js index 21b4e2fd90cc4..accaf7e8f2e76 100644 --- a/apps/src/sites/studio/pages/projects/public.js +++ b/apps/src/sites/studio/pages/projects/public.js @@ -11,10 +11,14 @@ import { MAX_PROJECTS_PER_CATEGORY } from '@cdo/apps/templates/projects/projectC import StartNewProject from '@cdo/apps/templates/projects/StartNewProject'; $(document).ready(() => { + const showFeatured = window.location.href.includes("showFeatured"); + const originalUrl = `/api/v1/projects/gallery/public/all/${MAX_PROJECTS_PER_CATEGORY}`; + const url = showFeatured ? originalUrl + `?showFeatured=1` : originalUrl; + registerReducers({projects}); $.ajax({ method: 'GET', - url: `/api/v1/projects/gallery/public/all/${MAX_PROJECTS_PER_CATEGORY}`, + url: url, dataType: 'json' }).done(projectLists => { getStore().dispatch(setProjectLists(projectLists)); diff --git a/dashboard/app/controllers/api/v1/projects/public_gallery_controller.rb b/dashboard/app/controllers/api/v1/projects/public_gallery_controller.rb index 4cd582f4f1ed7..b02934d709cda 100644 --- a/dashboard/app/controllers/api/v1/projects/public_gallery_controller.rb +++ b/dashboard/app/controllers/api/v1/projects/public_gallery_controller.rb @@ -8,7 +8,8 @@ def index render json: ProjectsList.fetch_published_projects( params[:project_type], limit: params[:limit], - published_before: params[:published_before] + published_before: params[:published_before], + prepend_featured: !!params[:showFeatured] ) rescue ArgumentError => e render json: {error: e.message}, status: :bad_request diff --git a/dashboard/lib/projects_list.rb b/dashboard/lib/projects_list.rb index 944173a0ed34b..c2a4a8ef36b79 100644 --- a/dashboard/lib/projects_list.rb +++ b/dashboard/lib/projects_list.rb @@ -60,7 +60,7 @@ def fetch_section_projects(section) # which to search for the requested projects. Must not be specified # when requesting all project types. Optional. # @return [Hash>] A hash of lists of published projects. - def fetch_published_projects(project_group, limit:, published_before:) + def fetch_published_projects(project_group, limit:, published_before:, prepend_featured:) unless limit && limit.to_i >= 1 && limit.to_i <= MAX_LIMIT raise ArgumentError, "limit must be between 1 and #{MAX_LIMIT}" end From 18f63332ea72493773777e68c9fc537be541dd94 Mon Sep 17 00:00:00 2001 From: Will Jordan Date: Mon, 26 Feb 2018 13:13:27 -0800 Subject: [PATCH 19/45] revert CircleCI to Ruby 2.2.3 Attempt to fix regression in #20878 causing `rake assets:precompile` to timeout. --- .circleci/Dockerfile | 10 +++++----- .circleci/config.yml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.circleci/Dockerfile b/.circleci/Dockerfile index e1ad0bc52d350..737e21d0f5aa1 100644 --- a/.circleci/Dockerfile +++ b/.circleci/Dockerfile @@ -46,15 +46,15 @@ RUN apt-get update && \ python python-dev # install ruby -RUN wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.0.tar.gz && \ - tar -xzvf ruby-2.5.0.tar.gz && \ - rm ruby-2.5.0.tar.gz && \ - cd ruby-2.5.0 && \ +RUN wget https://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.3.tar.gz && \ + tar -xzvf ruby-2.2.3.tar.gz && \ + rm ruby-2.2.3.tar.gz && \ + cd ruby-2.2.3 && \ ./configure && \ make -j"$(nproc)" && \ make install && \ cd .. && \ - rm -r ruby-2.5.0 + rm -r ruby-2.2.3 # install bundler RUN gem install bundler && \ diff --git a/.circleci/config.yml b/.circleci/config.yml index 29f2f5c3bdb61..6cb6b9bbe7362 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ jobs: build: parallelism: 2 docker: - - image: wjordan/code-dot-org:ruby-2.5 + - image: wjordan/code-dot-org:trusty environment: RAILS_ENV: test RACK_ENV: test From 6503b9bccafc3b85dfee5d5a562cf5ff0842039b Mon Sep 17 00:00:00 2001 From: Suresh Chanmugam Date: Mon, 26 Feb 2018 13:22:43 -0800 Subject: [PATCH 20/45] The new sheet is in the v3 folder now and no longer in a temporary test folder. --- pegasus/data/cdo-beyond-tutorials.gsheet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pegasus/data/cdo-beyond-tutorials.gsheet b/pegasus/data/cdo-beyond-tutorials.gsheet index f7cdafa8ec0ed..9d7e1948097cd 100644 --- a/pegasus/data/cdo-beyond-tutorials.gsheet +++ b/pegasus/data/cdo-beyond-tutorials.gsheet @@ -1,2 +1,2 @@ -TestSuresh/cdo-beyond-tutorials +v3/cdo-beyond-tutorials exclude_columns: ['notes_t'] From a988d828d18ea62869decb887413f9d7764cde32 Mon Sep 17 00:00:00 2001 From: Andrew Oberhardt Date: Sun, 25 Feb 2018 22:09:05 -0800 Subject: [PATCH 21/45] Add regional partner export script [ci skip] --- bin/oneoff/export_regional_partner_data | 168 ++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100755 bin/oneoff/export_regional_partner_data diff --git a/bin/oneoff/export_regional_partner_data b/bin/oneoff/export_regional_partner_data new file mode 100755 index 0000000000000..310f2d8d0cd7b --- /dev/null +++ b/bin/oneoff/export_regional_partner_data @@ -0,0 +1,168 @@ +#!/usr/bin/env ruby + +# Exports regional partner data to json, and imports from json. +# This is to set up an adhoc server with exported data from production + +require 'active_support/core_ext/object/try' + +# Parse options before requiring the rails env for speed +@action = ARGV.shift.try(:downcase) +unless %w(import export).include? @action + puts "Usage: #{__FILE__} export|import [file]" + exit +end +@filename = ARGV.shift || 'regional_partner_data.json' + +puts "#{@action}ing #{@action == 'export' ? 'to' : 'from'} #{File.absolute_path(@filename)}:" + +puts +puts "Loading Rails environment (this may take some time)..." +require_relative '../../dashboard/config/environment' +puts " Loaded." +puts + +raise "Import not allowed on production" if Rails.env.production? && @action == 'import' + +# Explicitly reference these models so they are autoloaded and we can monkey patch below. +# Otherwise they won't yet be autoloaded, and the monkey patch would create a new class +User.name +RegionalPartner.name +Pd::RegionalPartnerMapping.name +Pd::CourseFacilitator.name + +class User + ATTRIBUTES = %w( + name + email + ) + def export + attributes.slice(*ATTRIBUTES) + end + + def self.import(hash) + User.find_by(email: hash['email']) || User.create!( + hash.slice(*ATTRIBUTES).merge( + user_type: 'teacher', + password: 'password', + age: 21 + ) + ) + end + + def self.import_program_manager(hash) + import(hash).tap do |user| + user.permission = UserPermission::WORKSHOP_ORGANIZER + end + end + + def self.import_facilitator(hash) + import(hash).tap do |user| + user.permission = UserPermission::FACILITATOR + end + end +end + +class RegionalPartner + ATTRIBUTES = %w( + name + group + contact_id + urban + attention + street + apartment_or_suite + city + state + zip_code + phone_number + notes + ) + + def export + attributes.slice(*ATTRIBUTES).merge( + program_managers: program_managers.map(&:export), + mappings: mappings.map(&:export) + ).compact.deep_stringify_keys + end + + def self.import(hash) + RegionalPartner.find_or_create_by!(hash.slice(*ATTRIBUTES)).tap do |regional_partner| + hash['program_managers'].each do |program_manager_hash| + regional_partner.program_managers.push User.import_program_manager(program_manager_hash) + end + + hash['mappings'].each do |mapping_hash| + Pd::RegionalPartnerMapping.import(mapping_hash.merge(regional_partner_id: regional_partner.id)) + end + end + end +end + +class Pd::RegionalPartnerMapping + ATTRIBUTES = %w( + state + zip_code + ) + + def export + attributes.slice(*ATTRIBUTES) + end + + def self.import(hash) + Pd::RegionalPartnerMapping.find_or_create_by!(hash) + end +end + +class Pd::CourseFacilitator + def export + { + course: course, + facilitator: facilitator.export + } + end + + def self.import(hash) + Pd::CourseFacilitator.create!( + course: hash['course'], + facilitator: User.import_facilitator(hash['facilitator']) + ) + end +end + +def export_to(filename) + json = { + regional_partners: RegionalPartner.all.map(&:export), + facilitators: Pd::CourseFacilitator.all.map(&:export) + } + + File.open(filename, 'w') do |file| + file.puts JSON.pretty_generate(json) + end + + puts "Exported #{RegionalPartner.count} regional partners "\ + "and #{Pd::CourseFacilitator.count} facilitators to #{filename}" +end + +def import_from(filename) + File.open(filename) do |file| + hash = JSON.parse file.read + hash['regional_partners'].each do |regional_partner_hash| + RegionalPartner.import regional_partner_hash + end + hash['facilitators'].each do |facilitator_hash| + Pd::CourseFacilitator.import facilitator_hash + end + + puts "Imported #{hash['regional_partners'].count} regional partners "\ + "and #{hash['facilitators'].count} facilitators from #{filename}" + end +end + +case @action + when 'export' + export_to @filename + when 'import' + import_from @filename + else + raise "Unexpected action #{@action}" +end From 68fd3e4374c618159d994a21d74a7e4425673153 Mon Sep 17 00:00:00 2001 From: Will Jordan Date: Mon, 26 Feb 2018 14:41:20 -0800 Subject: [PATCH 22/45] bundle update ffi New version fixes compile errors on OSX. --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 37f1004f40992..b601efba850be 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -326,7 +326,7 @@ GEM redis (~> 3.2) faraday (0.9.2) multipart-post (>= 1.2, < 3) - ffi (1.9.21) + ffi (1.9.23) firebase (0.2.6) httpclient json From 1f57512777f52fe38e0a3e3e0d9b0bc7e1b310a7 Mon Sep 17 00:00:00 2001 From: Brad Buchanan Date: Mon, 26 Feb 2018 14:42:49 -0800 Subject: [PATCH 23/45] Test callback in upload image sets sourceUrl null --- .../animationPickerModuleTest.js | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js b/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js index 1693e1086833e..5896c0da61dc2 100644 --- a/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js +++ b/apps/test/unit/gamelab/AnimationPicker/animationPickerModuleTest.js @@ -1,3 +1,4 @@ +import sinon from 'sinon'; import reducer, * as animationPickerModule from '@cdo/apps/gamelab/AnimationPicker/animationPickerModule'; import listReducer from '@cdo/apps/gamelab/animationListModule'; import {combineReducers} from 'redux'; @@ -109,6 +110,7 @@ describe('animationPickerModule', function () { describe('action: handleUploadComplete', function () { it('sets sourceUrl to null', function () { + const fakeDispatch = sinon.spy(); const newState = {animationPicker: { uploadFilename: "filename.jpg", goal: Goal.NEW_ANIMATION }}; var store = createStore(combineReducers({animationList: listReducer, animationPicker: reducer}), newState); @@ -119,17 +121,30 @@ describe('animationPickerModule', function () { result: 0, versionId: "string" }, - store.dispatch, + fakeDispatch, store.getState ); onMetadataLoaded({}); - - const newListState = store.getState().animationList; - const animationKey = newListState.orderedKeys[0]; - - expect(newListState.propsByKey[animationKey]).to.be.not.null; - expect(newListState.propsByKey[animationKey].name).to.equal("filename.jpg_1"); - expect(newListState.propsByKey[animationKey].sourceUrl).to.be.null; + expect(fakeDispatch).to.have.been.calledTwice; + + const addAnimation = fakeDispatch.firstCall.args[0]; + expect(addAnimation).to.be.a('function'); + expect(fakeDispatch.secondCall.args[0]).to.deep.equal(animationPickerModule.hide()); + fakeDispatch.reset(); + + addAnimation(fakeDispatch, store.getState); + expect(fakeDispatch.firstCall.args[0]).to.deep.equal({ + type: 'AnimationList/ADD_ANIMATION', + key: 'filename.jpg', + props: + { + name: 'filename.jpg', + sourceUrl: null, + size: undefined, + version: 'string', + looping: true + } + }); }); }); }); From dca099090819b3c25db1ce13a624aa9318159b2f Mon Sep 17 00:00:00 2001 From: Mehal Shah Date: Thu, 22 Feb 2018 17:07:03 -0800 Subject: [PATCH 24/45] Checkpoint --- .../pd/workshop_dashboard/components/workshop_form.jsx | 3 ++- apps/src/code-studio/pd/workshop_dashboard/workshop_index.jsx | 2 +- .../app/controllers/api/v1/regional_partners_controller.rb | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/src/code-studio/pd/workshop_dashboard/components/workshop_form.jsx b/apps/src/code-studio/pd/workshop_dashboard/components/workshop_form.jsx index 9716e01a89d94..b4c8f88bee2b6 100644 --- a/apps/src/code-studio/pd/workshop_dashboard/components/workshop_form.jsx +++ b/apps/src/code-studio/pd/workshop_dashboard/components/workshop_form.jsx @@ -121,9 +121,10 @@ export default class WorkshopForm extends React.Component { ); initialState.sessions = this.prepareSessionsForForm(props.workshop.sessions); this.loadAvailableFacilitators(props.workshop.course); - this.loadRegionalPartners(); } + this.loadRegionalPartners(); + return initialState; } diff --git a/apps/src/code-studio/pd/workshop_dashboard/workshop_index.jsx b/apps/src/code-studio/pd/workshop_dashboard/workshop_index.jsx index 987afabc9f69b..bd228703f54b4 100644 --- a/apps/src/code-studio/pd/workshop_dashboard/workshop_index.jsx +++ b/apps/src/code-studio/pd/workshop_dashboard/workshop_index.jsx @@ -70,7 +70,7 @@ export default class WorkshopIndex extends React.Component {

Your Workshops

- {(this.permission.isWorkshopAdmin || this.permission.isOrganizer) && + {(this.permission.isWorkshopAdmin || this.permission.isOrganizer || this.permission.isFacilitator) && (