Skip to content

Commit

Permalink
Merge pull request #40178 from code-dot-org/dtp_candidate_1cd6f8c0
Browse files Browse the repository at this point in the history
DTP (Test > Production: 1cd6f8c)
  • Loading branch information
jmkulwik committed Apr 20, 2021
2 parents 6858f80 + 0ccb557 commit 7382776
Show file tree
Hide file tree
Showing 263 changed files with 12,347 additions and 3,290 deletions.
4 changes: 3 additions & 1 deletion apps/src/StudioApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,9 @@ StudioApp.prototype.init = function(config) {
if (config.locale !== 'en_us' && config.skinId === 'letters') {
this.displayWorkspaceAlert(
'error',
<div>{msg.englishOnlyWarning({nextStage: config.stagePosition + 1})}</div>
<div>
{msg.englishOnlyWarning({nextStage: config.lessonPosition + 1})}
</div>
);
}

Expand Down
2 changes: 1 addition & 1 deletion apps/src/code-studio/appOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* @typedef {Object} AppOptionsConfig
* @property {boolean} embedded
* @property {string} scriptName
* @property {string} stagePosition
* @property {string} lessonPosition
* @property {string} levelPosition
* @property {AutoplayVideo} autoplayVideo
* @property {SerializedAnimationList} initialAnimationList
Expand Down
10 changes: 8 additions & 2 deletions apps/src/code-studio/components/ModelManagerDialog.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export default class ModelManagerDialog extends React.Component {
models: [],
isModelListPending: true,
isImportPending: false,
isDeletePending: false,
confirmDialogOpen: false,
deletionStatus: undefined
};
Expand Down Expand Up @@ -123,16 +124,19 @@ export default class ModelManagerDialog extends React.Component {
};

deleteModel = () => {
this.setState({isDeletePending: true});
$.ajax({
url: `/api/v1/ml_models/${this.state.selectedModel.id}`,
method: 'DELETE'
}).then(response => {
if (response.status === 'failure') {
this.setState({
deletionStatus: `Model with id ${response.id} could not be deleted.`
deletionStatus: `Model with id ${response.id} could not be deleted.`,
isDeletePending: false
});
} else {
this.setState({confirmDialogOpen: false});
this.setState({confirmDialogOpen: false, isDeletePending: false});
this.getModelList();
}
});
};
Expand Down Expand Up @@ -221,6 +225,8 @@ export default class ModelManagerDialog extends React.Component {
onClick={this.deleteModel}
icon={'trash'}
iconClassName={'fa-trash'}
pendingText={'Deleting...'}
isPending={this.state.isDeletePending}
/>
</div>
<p style={styles.message}>{this.state.deletionStatus}</p>
Expand Down
2 changes: 1 addition & 1 deletion apps/src/code-studio/initApp/loadApp.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ function loadAppAsync(appOptions) {
$.ajax(
`/api/user_progress` +
`/${appOptions.scriptName}` +
`/${appOptions.stagePosition}` +
`/${appOptions.lessonPosition}` +
`/${appOptions.levelPosition}` +
`/${appOptions.serverLevelId}`
)
Expand Down
15 changes: 15 additions & 0 deletions apps/src/code-studio/initApp/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,21 @@ var projects = (module.exports = {
}
},

/**
* Returns the project URL for the current project.
*
* This URL accesses the dashboard API for the sources S3 bucket where
* the main.json is generally stored.
*
* This function depends on the document location to determine the current
* application environment.
*
* @returns {string} Fully-qualified sources URL for the current project.
*/
getProjectSourcesUrl() {
return `${this.getLocation().origin}/v3/sources/${this.getCurrentId()}`;
},

getCurrentTimestamp() {
if (!current) {
return;
Expand Down
53 changes: 49 additions & 4 deletions apps/src/code-studio/progressRedux.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {authorizeLockable} from './stageLockRedux';

// Action types
export const INIT_PROGRESS = 'progress/INIT_PROGRESS';
const SET_SCRIPT_PROGRESS = 'progress/SET_SCRIPT_PROGRESS';
const CLEAR_RESULTS = 'progress/CLEAR_RESULTS';
const MERGE_RESULTS = 'progress/MERGE_RESULTS';
const MERGE_PEER_REVIEW_PROGRESS = 'progress/MERGE_PEER_REVIEW_PROGRESS';
Expand Down Expand Up @@ -52,7 +53,11 @@ const initialState = {
isLessonExtras: false,

// The remaining fields do change after initialization
// a mapping of level id to result
// scriptProgress is of type scriptProgressType (a map of levelId ->
// studentLevelProgressType)
scriptProgress: {},
// levelResults is a map of levelId -> TestResult
// note: eventually, we expect usage of this field to be replaced with scriptProgress
levelResults: {},
focusAreaStageIds: [],
peerReviewLessonInfo: null,
Expand Down Expand Up @@ -106,6 +111,13 @@ export default function reducer(state = initialState, action) {
};
}

if (action.type === SET_SCRIPT_PROGRESS) {
return {
...state,
scriptProgress: action.scriptProgress
};
}

if (action.type === USE_DB_PROGRESS) {
return {
...state,
Expand Down Expand Up @@ -359,6 +371,12 @@ const userProgressFromServer = (state, dispatch, userId = null) => {

// Merge progress from server
if (data.progress) {
dispatch(setScriptProgress(data.progress));

// Note that we set the full progress object above in redux but also set
// a map containing just level results. This is the legacy code path and
// the goal is to eventually update all code paths to use scriptProgress
// instead of levelResults.
const levelResults = _.mapValues(data.progress, getLevelResult);
dispatch(mergeResults(levelResults));

Expand Down Expand Up @@ -411,6 +429,17 @@ export const initProgress = ({
currentPageNumber
});

/**
* Returns action that sets (overwrites) scriptProgress in the redux store.
*
* @param {scriptProgressType} scriptProgress
* @returns action
*/
export const setScriptProgress = scriptProgress => ({
type: SET_SCRIPT_PROGRESS,
scriptProgress: scriptProgress
});

export const clearResults = () => ({
type: CLEAR_RESULTS
});
Expand Down Expand Up @@ -553,7 +582,13 @@ const isCurrentLevel = (currentLevelId, level) => {
* state.levelResults
*/
const levelWithStatus = (
{levelResults, levelPairing = {}, currentLevelId, isSublevel = false},
{
levelResults,
scriptProgress,
levelPairing = {},
currentLevelId,
isSublevel = false
},
level
) => {
if (level.kind !== LevelKind.unplugged && !isSublevel) {
Expand All @@ -563,12 +598,15 @@ const levelWithStatus = (
);
}
}

const normalizedLevel = processedLevel(level);
const levelProgress = scriptProgress[normalizedLevel.id];
return {
...normalizedLevel,
status: statusForLevel(level, levelResults),
isCurrentLevel: isCurrentLevel(currentLevelId, normalizedLevel),
paired: levelPairing[level.activeId],
locked: levelProgress?.locked,
readonlyAnswers: level.readonly_answers
};
};
Expand All @@ -579,19 +617,26 @@ const levelWithStatus = (
export const levelsByLesson = ({
stages,
levelResults,
scriptProgress,
levelPairing,
currentLevelId
}) =>
stages.map(stage =>
stage.levels.map(level => {
let statusLevel = levelWithStatus(
{levelResults, levelPairing, currentLevelId},
{levelResults, scriptProgress, levelPairing, currentLevelId},
level
);
if (statusLevel.sublevels) {
statusLevel.sublevels = level.sublevels.map(sublevel =>
levelWithStatus(
{levelResults, levelPairing, currentLevelId, isSublevel: true},
{
levelResults,
scriptProgress,
levelPairing,
currentLevelId,
isSublevel: true
},
sublevel
)
);
Expand Down
5 changes: 5 additions & 0 deletions apps/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,13 @@ export const TestResults = {
BETTER_THAN_IDEAL: 102,

SUBMITTED_RESULT: 1000,
// LOCKED_RESULT and READONLY_SUBMISSION_RESULT are never stored on the backend.
// They are used to encode locked/readonly information into a TestResult.
// Eventually, we want to remove these and look at studentlevelProgressType.locked
// and studentlevelProgressType.readonly instead. (LP-1865)
LOCKED_RESULT: 1001,
READONLY_SUBMISSION_RESULT: 1002,

REVIEW_REJECTED_RESULT: 1500,
REVIEW_ACCEPTED_RESULT: 2000
};
Expand Down
28 changes: 27 additions & 1 deletion apps/src/javalab/Javalab.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@ import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {getStore, registerReducers} from '@cdo/apps/redux';
import JavalabView from './JavalabView';
import javalab, {getSources, setAllSources} from './javalabRedux';
import javalab, {
getSources,
setAllSources,
appendOutputLog
} from './javalabRedux';
import {TestResults} from '@cdo/apps/constants';
import project from '@cdo/apps/code-studio/initApp/project';
import JavabuilderConnection from './javabuilderConnection';
import {showLevelBuilderSaveButton} from '@cdo/apps/code-studio/header';

/**
Expand All @@ -21,6 +26,7 @@ const MOBILE_PORTRAIT_WIDTH = 600;
const Javalab = function() {
this.skin = null;
this.level = null;
this.channelId = null;

/** @type {StudioApp} */
this.studioApp_ = null;
Expand All @@ -43,6 +49,7 @@ Javalab.prototype.init = function(config) {

this.skin = config.skin;
this.level = config.level;
this.channelId = config.channel;

config.makeYourOwn = false;
config.wireframeShare = true;
Expand All @@ -61,8 +68,10 @@ Javalab.prototype.init = function(config) {
config.pinWorkspaceToBottom = true;

config.getCode = this.getCode.bind(this);
const onRun = this.onRun.bind(this);
const onContinue = this.onContinue.bind(this);
const onCommitCode = this.onCommitCode.bind(this);
const onInputMessage = this.onInputMessage.bind(this);

const onMount = () => {
// NOTE: Most other apps call studioApp.init(). Like WebLab, Ailab, and Fish, we don't.
Expand Down Expand Up @@ -118,8 +127,10 @@ Javalab.prototype.init = function(config) {
<Provider store={getStore()}>
<JavalabView
onMount={onMount}
onRun={onRun}
onContinue={onContinue}
onCommitCode={onCommitCode}
onInputMessage={onInputMessage}
/>
</Provider>,
document.getElementById(config.containerId)
Expand All @@ -141,6 +152,21 @@ Javalab.prototype.beforeUnload = function(event) {
}
};

// Called by the Javalab app when it wants execute student code.
Javalab.prototype.onRun = function() {
this.javabuilderConnection = new JavabuilderConnection(
this.channelId,
this.level.javabuilderUrl,
message => getStore().dispatch(appendOutputLog(message))
);
this.javabuilderConnection.connectJavabuilder();
};

// Called by Javalab console to send a message to Javabuilder.
Javalab.prototype.onInputMessage = function(message) {
this.javabuilderConnection.sendMessage(message);
};

// Called by the Javalab app when it wants to go to the next level.
Javalab.prototype.onContinue = function() {
const onReportComplete = result => {
Expand Down
8 changes: 6 additions & 2 deletions apps/src/javalab/JavalabConsole.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ function moveCaretToEndOfDiv(element) {

class JavalabConsole extends React.Component {
static propTypes = {
onInputMessage: PropTypes.func.isRequired,
// populated by redux
consoleLogs: PropTypes.array,
appendInputLog: PropTypes.func,
Expand Down Expand Up @@ -112,12 +113,15 @@ class JavalabConsole extends React.Component {
}

onInputKeyDown = e => {
const {appendInputLog, onInputMessage} = this.props;
const input = e.target.value;
if (e.keyCode === KeyCodes.ENTER) {
e.preventDefault();
e.target.value = '';
this.state.commandHistory.push(input);
this.props.appendInputLog(input);
// Add a newline to maintain consistency with Java command line input.
this.state.commandHistory.push(input + '\n');
appendInputLog(input);
onInputMessage(input);
} else if (e.keyCode === KeyCodes.UP) {
e.target.value = this.state.commandHistory.goBack(input);
moveCaretToEndOfDiv(e.target);
Expand Down

0 comments on commit 7382776

Please sign in to comment.