Skip to content

Commit

Permalink
Merge branch 'staging-next' into throttle-profanity
Browse files Browse the repository at this point in the history
  • Loading branch information
Madelyn Kasula committed Dec 11, 2020
2 parents 816d729 + 917eb32 commit 21bb2de
Show file tree
Hide file tree
Showing 124 changed files with 1,681 additions and 1,418 deletions.
3 changes: 3 additions & 0 deletions SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ You can do Code.org development using OSX, Ubuntu, or Windows (running Ubuntu in
* `ALTER DATABASE dashboard_development CHARACTER SET utf8 COLLATE utf8_unicode_ci;`
* `ALTER DATABASE dashboard_test CHARACTER SET utf8 COLLATE utf8_unicode_ci;`

1. increase `max_allowed_packet` to enable loading fixtures
* add `max_allowed_packet = 268435456` to `/usr/local/etc/my.cnf` (Mac) or `/etc/mysql/mysql.conf.d/mysqld.cnf` (Linux)

1. `bundle exec rake build`
* This may fail if your are on a Mac and your OSX XCode Command Line Tools were not installed properly. See Bundle Install Tips for more information.
1. (Optional, Code.org engineers only) Setup AWS - Ask a Code.org engineer how to complete this step
Expand Down
12 changes: 11 additions & 1 deletion apps/i18n/common/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@
"autoGenerated": "Auto-generated",
"autolock": "Note: Stage auto-locks after 24 hours.",
"availableLessons": " Available in {numLessons, plural, one {1 Lesson} other {# Lessons}}:",
"azureTtsTooManyRequests": "The app has made too many requests to convert text to speech. Please wait before trying again.",
"azureTtsDefaultError": "There's been an error converting text to speech. If this issue persists, please refresh the page and try again.",
"back": "Back",
"backToActivity": "Back to activity",
"backToPreviousLevel": "Back to previous level",
Expand Down Expand Up @@ -626,6 +628,14 @@
"dropletBlock_mathAbs_param0": "x",
"dropletBlock_mathAbs_param0_description": "An arbitrary number.",
"dropletBlock_mathAbs_signatureOverride": "Math.abs(x)",
"dropletBlock_mathIncrement_description": "Adds one to x",
"dropletBlock_mathIncrement_param0": "x",
"dropletBlock_mathIncrement_param0_description": "An arbitrary number.",
"dropletBlock_mathIncrement_signatureOverride": "x++",
"dropletBlock_mathDecrement_description": "Subtracts one from x",
"dropletBlock_mathDecrement_param0": "x",
"dropletBlock_mathDecrement_param0_description": "An arbitrary number.",
"dropletBlock_mathDecrement_signatureOverride": "x--",
"dropletBlock_mathMax_description": "Takes the maximum value among one or more values n1, n2, ..., nX",
"dropletBlock_mathMax_param0": "n1, n2,..., nX",
"dropletBlock_mathMax_param0_description": "One or more numbers to compare.",
Expand Down Expand Up @@ -659,7 +669,7 @@
"dropletBlock_playSound_description": "Plays the MP3 sound file from the specified URL.",
"dropletBlock_playSound_param0_description": "The URL to a sound file. Can be a project asset name or external URL.",
"dropletBlock_playSound_param1_description": "If true, loop the sound until asked to stop.",
"dropletBlock_playSpeech_description": "Plays the specified text as speech with a voice in the specified gender. Powered by Microsoft Azure.",
"dropletBlock_playSpeech_description": "Plays the specified text as speech with a voice in the specified gender. Powered by Microsoft Azure AI.",
"dropletBlock_randomNumber_description": "Returns a random number in the closed range from min to max.",
"dropletBlock_randomNumber_param0": "min",
"dropletBlock_randomNumber_param0_description": "The minimum number returned",
Expand Down
11 changes: 7 additions & 4 deletions apps/lib/droplet/droplet-full.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
/* Droplet.
* Copyright (c) 2019 Anthony Bau.
* Copyright (c) 2020 Anthony Bau.
* MIT License.
*
* Date: 2019-07-24
* Date: 2020-11-25
*/
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.droplet = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
Expand Down Expand Up @@ -139,7 +139,7 @@ arguments[4][2][0].apply(exports,arguments)
/*!
* The buffer module from node.js, for the browser.
*
* @author Feross Aboukhadijeh <feross@feross.org> <http://feross.org>
* @author Feross Aboukhadijeh <http://feross.org>
* @license MIT
*/
/* eslint-disable no-proto */
Expand Down Expand Up @@ -2108,6 +2108,7 @@ function isUndefined(arg) {
}

},{}],8:[function(require,module,exports){
/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
exports.read = function (buffer, offset, isLE, mLen, nBytes) {
var e, m
var eLen = (nBytes * 8) - mLen - 1
Expand Down Expand Up @@ -12272,7 +12273,7 @@ NODE_CATEGORIES = {
'FunctionExpression': 'functions',
'FunctionDeclaration': 'functions',
'AssignmentExpression': 'assignments',
'UpdateExpression': 'assignments',
'UpdateExpression': 'arithmetic',
'VariableDeclaration': 'assignments',
'ReturnStatement': 'returns',
'IfStatement': 'conditionals',
Expand Down Expand Up @@ -12438,6 +12439,8 @@ exports.JavaScriptParser = JavaScriptParser = (function(superClass) {
}
} else if (node.type === 'FunctionExpression') {
return [node.type, 'mostly-value', 'function-value'];
} else if (node.type === 'UpdateExpression') {
return [node.type, 'any-drop'];
} else if (node.type.match(/Expression$/) != null) {
return [node.type, 'mostly-value'];
} else if (node.type.match(/Declaration$/) != null) {
Expand Down
8 changes: 4 additions & 4 deletions apps/lib/droplet/droplet-full.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/lib/droplet/droplet-full.min.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"@babel/preset-react": "^7.0.0",
"@cdo/interpreted": "link:../dashboard/config/libraries",
"@code-dot-org/artist": "0.2.1",
"@code-dot-org/blockly": "3.5.31",
"@code-dot-org/blockly": "3.5.32",
"@code-dot-org/bramble": "0.1.26",
"@code-dot-org/craft": "0.2.2",
"@code-dot-org/dance-party": "1.0.1",
Expand Down
63 changes: 46 additions & 17 deletions apps/src/AzureTextToSpeech.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import $ from 'jquery';
import i18n from '@cdo/locale';
import {hashString, findProfanity} from '@cdo/apps/utils';
import Sounds from '@cdo/apps/Sounds';

Expand All @@ -7,7 +8,7 @@ import Sounds from '@cdo/apps/Sounds';
* @param {ArrayBuffer} bytes Sound bytes from Azure Speech Service. For clarity, this should be null if the response contains profaneWords.
* @param {Object} playbackOptions Configuration options for a playing sound.
* @param {Array<string>} profaneWords Any profanity in the response. Used to determine whether the response should be cached and played.
* @param {string} error Any error that occurs while requesting the sound or checking for profanity.
* @param {Error} error Any error that occurs while requesting the sound or checking for profanity.
*/
export class SoundResponse {
constructor(bytes, playbackOptions, profaneWords = [], error = null) {
Expand All @@ -20,6 +21,30 @@ export class SoundResponse {
success = () => {
return this.bytes && this.profaneWords.length === 0 && !this.error;
};

profanityMessage = () => {
if (!this.profaneWords || this.profaneWords.length === 0) {
return null;
}

return i18n.textToSpeechProfanity({
profanityCount: this.profaneWords.length,
profaneWords: this.profaneWords.join(', ')
});
};

errorMessage = () => {
if (!this.error) {
return null;
}

switch (this.error.status) {
case 429:
return i18n.azureTtsTooManyRequests();
default:
return i18n.azureTtsDefaultError();
}
};
}

let singleton;
Expand Down Expand Up @@ -55,16 +80,15 @@ export default class AzureTextToSpeech {

/**
*
* @param {Promise<SoundResponse>} soundPromise A promise that returns a SoundResponse when resolved.
* playback configuration options.
* @param {function} soundPromise A thunk that returns a promise, which resolves to a SoundResponse.
*/
enqueueAndPlay = soundPromise => {
this.enqueue_(soundPromise);
this.asyncPlayFromQueue_(this.playBytes_);
};

/**
* Returns a promise representing a TTS sound that can be enqueued and played. Utilizes a sound cache --
* A thunk that returns a promise representing a TTS sound that can be enqueued and played. Utilizes a sound cache --
* will check for a cache hit to avoid duplicate network requests, and caches network responses for re-use.
* @param {Object} opts
* @param {string} opts.text
Expand All @@ -73,11 +97,13 @@ export default class AzureTextToSpeech {
* @param {string} opts.url URL to request sound from.
* @param {string} opts.ssml SSML in request body.
* @param {string} opts.token Authentication token from Azure.
* @param {function(Array<string>)} opts.onProfanityFound Called if the given text contains profanity.
* @returns {Promise<SoundResponse>} A promise that returns a SoundResponse when resolved.
* @param {function(string)} opts.onFailure Called with an error message if the sound will not be played.
* @returns {function} A thunk that returns a promise, which resolves to a SoundResponse. Example usage:
* const soundPromise = createSoundPromise(options);
* const soundResponse = await soundPromise();
*/
createSoundPromise = opts => {
const {text, gender, languageCode, onProfanityFound} = opts;
createSoundPromise = opts => () => {
const {text, gender, languageCode, onFailure} = opts;
const cachedSound = this.getCachedSound_(languageCode, gender, text);
const wrappedSetCachedSound = soundResponse => {
this.setCachedSound_(languageCode, gender, text, soundResponse);
Expand All @@ -90,8 +116,9 @@ export default class AzureTextToSpeech {

return new Promise(resolve => {
if (profaneWords && profaneWords.length > 0) {
onProfanityFound(profaneWords);
resolve(wrappedCreateSoundResponse({profaneWords}));
const soundResponse = wrappedCreateSoundResponse({profaneWords});
onFailure(soundResponse.profanityMessage());
resolve(soundResponse);
} else {
resolve(wrappedCreateSoundResponse({bytes}));
}
Expand All @@ -103,8 +130,8 @@ export default class AzureTextToSpeech {
try {
const profaneWords = await findProfanity(text, languageCode);
if (profaneWords && profaneWords.length > 0) {
onProfanityFound(profaneWords);
const soundResponse = wrappedCreateSoundResponse({profaneWords});
onFailure(soundResponse.profanityMessage());
wrappedSetCachedSound(soundResponse);
resolve(soundResponse);
return;
Expand All @@ -119,7 +146,9 @@ export default class AzureTextToSpeech {
wrappedSetCachedSound(soundResponse);
resolve(soundResponse);
} catch (error) {
resolve(wrappedCreateSoundResponse({error: error.statusText}));
const soundResponse = wrappedCreateSoundResponse({error});
onFailure(soundResponse.errorMessage());
resolve(soundResponse);
}
});
};
Expand Down Expand Up @@ -157,7 +186,7 @@ export default class AzureTextToSpeech {
}

this.playing = true;
let response = await nextSoundPromise;
let response = await nextSoundPromise();
if (response.success()) {
play(response.bytes.slice(0), response.playbackOptions);
} else {
Expand Down Expand Up @@ -224,17 +253,17 @@ export default class AzureTextToSpeech {
};

/**
* Add a promise to the end of the queue.
* @param {Promise<SoundResponse>} promise A promise that returns a SoundResponse when resolved.
* Add to the end of the queue.
* @param {function} promise A thunk that returns a promise, which resolves to a SoundResponse.
* @private
*/
enqueue_ = promise => {
this.queue_.push(promise);
};

/**
* Get the next promise in the queue.
* @returns {Promise<SoundResponse>} A promise that returns a SoundResponse when resolved.
* Get the next item in the queue.
* @returns {function} A thunk that returns a promise, which resolves to a SoundResponse.
* @private
*/
dequeue_ = () => {
Expand Down
5 changes: 4 additions & 1 deletion apps/src/block_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -1199,7 +1199,10 @@ exports.createJsWrapperBlockCreator = function(
}
}

if (this.type === 'gamelab_setPrompt') {
if (
this.type === 'gamelab_setPrompt' ||
this.type === 'gamelab_setPromptWithChoices'
) {
const input = this.getInput('VAR');
if (input) {
const targetBlock = input.connection.targetBlock();
Expand Down
4 changes: 2 additions & 2 deletions apps/src/code-studio/components/progress/MiniView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class MiniView extends React.Component {
hasGroups: PropTypes.bool.isRequired,
scriptName: PropTypes.string.isRequired,
hasFullProgress: PropTypes.bool.isRequired,
selectedSectionId: PropTypes.string
selectedSectionId: PropTypes.number
};

render() {
Expand Down Expand Up @@ -89,5 +89,5 @@ export default connect(state => ({
scriptName: state.progress.scriptName,
hasFullProgress: state.progress.hasFullProgress,
hasGroups: hasGroups(state.progress),
selectedSectionId: state.teacherSections.selectedSectionId.toString()
selectedSectionId: state.teacherSections.selectedSectionId
}))(MiniView);
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default class MiniViewTopRow extends React.Component {
scriptName: PropTypes.string.isRequired,
// May not have this (i.e if not logged in)
linesOfCodeText: PropTypes.string,
selectedSectionId: PropTypes.string
selectedSectionId: PropTypes.number
};

render() {
Expand Down
4 changes: 2 additions & 2 deletions apps/src/code-studio/components/progress/SectionSelector.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class SectionSelector extends React.Component {
id: PropTypes.number.isRequired
})
).isRequired,
selectedSectionId: PropTypes.string,
selectedSectionId: PropTypes.number,
selectSection: PropTypes.func.isRequired
};

Expand Down Expand Up @@ -96,7 +96,7 @@ export const UnconnectedSectionSelector = SectionSelector;

export default connect(
state => ({
selectedSectionId: state.teacherSections.selectedSectionId.toString(),
selectedSectionId: state.teacherSections.selectedSectionId,
sections: sectionsNameAndId(state.teacherSections)
}),
dispatch => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import React from 'react';
import i18n from '@cdo/locale';
import {TeacherPanelProgressBubble} from '@cdo/apps/code-studio/components/progress/TeacherPanelProgressBubble';
import Button from '@cdo/apps/templates/Button';
import {studentType} from '@cdo/apps/templates/progress/progressTypes';
import {LevelStatus} from '@cdo/apps/util/sharedConstants';
import Radium from 'radium';
import FontAwesome from '@cdo/apps/templates/FontAwesome';
import {studentShape} from './StudentTable';

const RadiumFontAwesome = Radium(FontAwesome);

Expand Down Expand Up @@ -45,7 +45,7 @@ const styles = {

export class SelectedStudentInfo extends React.Component {
static propTypes = {
students: PropTypes.arrayOf(studentShape).isRequired,
students: PropTypes.arrayOf(studentType).isRequired,
selectedStudent: PropTypes.object,
level: PropTypes.object,
onSelectUser: PropTypes.func.isRequired,
Expand Down
6 changes: 3 additions & 3 deletions apps/src/code-studio/components/progress/StageLockDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class StageLockDialog extends React.Component {
lockStatus: PropTypes.oneOf(Object.values(LockStatus)).isRequired
})
),
selectedSectionId: PropTypes.string.isRequired,
selectedSectionId: PropTypes.number,
saving: PropTypes.bool.isRequired,
saveDialog: PropTypes.func.isRequired
};
Expand Down Expand Up @@ -151,7 +151,7 @@ class StageLockDialog extends React.Component {
const responsiveHeight = {
maxHeight: window.innerHeight * 0.8 - 100
};
const hasSelectedSection = this.props.selectedSectionId !== '';
const hasSelectedSection = !!this.props.selectedSectionId;
const hiddenUnlessSelectedSection = hasSelectedSection ? {} : styles.hidden;
return (
<BaseDialog
Expand Down Expand Up @@ -331,7 +331,7 @@ export default connect(
initialLockStatus: state.stageLock.lockStatus,
isOpen: !!state.stageLock.lockDialogStageId,
saving: state.stageLock.saving,
selectedSectionId: state.teacherSections.selectedSectionId.toString()
selectedSectionId: state.teacherSections.selectedSectionId
}),
dispatch => ({
saveDialog(sectionId, lockStatus) {
Expand Down
8 changes: 2 additions & 6 deletions apps/src/code-studio/components/progress/StudentTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import Radium from 'radium';
import color from '@cdo/apps/util/color';
import i18n from '@cdo/locale';
import {studentType} from '@cdo/apps/templates/progress/progressTypes';
import {TeacherPanelProgressBubble} from '@cdo/apps/code-studio/components/progress/TeacherPanelProgressBubble';
import FontAwesome from '@cdo/apps/templates/FontAwesome';

Expand Down Expand Up @@ -52,14 +53,9 @@ const styles = {
}
};

export const studentShape = PropTypes.shape({
id: PropTypes.number.isRequired,
name: PropTypes.string.isRequired
});

class StudentTable extends React.Component {
static propTypes = {
students: PropTypes.arrayOf(studentShape).isRequired,
students: PropTypes.arrayOf(studentType).isRequired,
onSelectUser: PropTypes.func.isRequired,
getSelectedUserId: PropTypes.func.isRequired,
levels: PropTypes.array,
Expand Down
5 changes: 3 additions & 2 deletions apps/src/code-studio/components/progress/TeacherPanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {fullyLockedStageMapping} from '../../stageLockRedux';
import {ViewType} from '../../viewAsRedux';
import {hasLockableStages} from '../../progressRedux';
import {pageTypes} from '@cdo/apps/templates/teacherDashboard/teacherSectionsRedux';
import StudentTable, {studentShape} from './StudentTable';
import {studentType} from '@cdo/apps/templates/progress/progressTypes';
import StudentTable from './StudentTable';
import {teacherDashboardUrl} from '@cdo/apps/templates/teacherDashboard/urlHelpers';
import {SelectedStudentInfo} from './SelectedStudentInfo';
import Button from '@cdo/apps/templates/Button';
Expand Down Expand Up @@ -76,7 +77,7 @@ class TeacherPanel extends React.Component {
}),
scriptHasLockableStages: PropTypes.bool.isRequired,
unlockedStageNames: PropTypes.arrayOf(PropTypes.string).isRequired,
students: PropTypes.arrayOf(studentShape)
students: PropTypes.arrayOf(studentType)
};

logToFirehose = (eventName, overrideData = {}) => {
Expand Down

0 comments on commit 21bb2de

Please sign in to comment.