feat(learn): add python video challenges (#38424)

This commit is contained in:
Beau Carnes
2020-04-06 14:49:56 -04:00
committed by Mrugesh Mohapatra
parent 22afc2a0ca
commit e776529ed0
218 changed files with 3411 additions and 114 deletions

View File

@ -167,6 +167,16 @@
"description": "Camper is information security and quality assurance certified",
"default": false
},
"isQaCert": {
"type": "boolean",
"description": "Camper is quality assurance certified",
"default": false
},
"isInfosecCert": {
"type": "boolean",
"description": "Camper is information security certified",
"default": false
},
"is2018FullStackCert": {
"type": "boolean",
"description": "Camper is full stack certified (2018)",

View File

@ -19,6 +19,8 @@ import {
dataVis2018Id,
apisMicroservicesId,
infosecQaId,
infosecId,
qaId,
fullStackId,
sciCompPyId,
dataAnalysisPyId,
@ -107,6 +109,8 @@ function createCertTypeIds(app) {
Challenge
),
[certTypes.infosecQa]: getIdsForCert$(infosecQaId, Challenge),
[certTypes.qa]: getIdsForCert$(qaId, Challenge),
[certTypes.infosec]: getIdsForCert$(infosecId, Challenge),
[certTypes.fullStack]: getIdsForCert$(fullStackId, Challenge),
[certTypes.sciCompPy]: getIdsForCert$(sciCompPyId, Challenge),
[certTypes.dataAnalysisPy]: getIdsForCert$(dataAnalysisPyId, Challenge),
@ -133,6 +137,8 @@ const certIds = {
[certTypes.dataVis2018]: dataVis2018Id,
[certTypes.apisMicroservices]: apisMicroservicesId,
[certTypes.infosecQa]: infosecQaId,
[certTypes.qa]: qaId,
[certTypes.infosec]: infosecId,
[certTypes.fullStack]: fullStackId,
[certTypes.sciCompPy]: sciCompPyId,
[certTypes.dataAnalysisPy]: dataAnalysisPyId,
@ -150,6 +156,8 @@ const certText = {
[certTypes.dataVis2018]: 'Data Visualization',
[certTypes.apisMicroservices]: 'APIs and Microservices',
[certTypes.infosecQa]: 'Information Security and Quality Assurance',
[certTypes.qa]: 'Quality Assurance',
[certTypes.infosec]: 'Information Security',
[certTypes.sciCompPy]: 'Scientific Computing with Python',
[certTypes.dataAnalysisPy]: 'Data Analysis with Python',
[certTypes.machineLearningPy]: 'Machine Learning with Python'
@ -166,6 +174,8 @@ const completionHours = {
[certTypes.dataVis2018]: 300,
[certTypes.apisMicroservices]: 300,
[certTypes.infosecQa]: 300,
[certTypes.qa]: 300,
[certTypes.infosec]: 300,
[certTypes.sciCompPy]: 400,
[certTypes.dataAnalysisPy]: 400,
[certTypes.machineLearningPy]: 400
@ -193,6 +203,8 @@ function sendCertifiedEmail(
isDataVisCert,
isApisMicroservicesCert,
isInfosecQaCert,
isQaCert,
isInfosecCert,
isSciCompPyCert,
isDataAnalysisPyCert,
isMachineLearningPyCert
@ -207,6 +219,8 @@ function sendCertifiedEmail(
!isDataVisCert ||
!isApisMicroservicesCert ||
!isInfosecQaCert ||
!isQaCert ||
!isInfosecCert ||
!isSciCompPyCert ||
!isDataAnalysisPyCert ||
!isMachineLearningPyCert
@ -237,6 +251,8 @@ function getUserIsCertMap(user) {
is2018DataVisCert = false,
isApisMicroservicesCert = false,
isInfosecQaCert = false,
isQaCert = false,
isInfosecCert = false,
isFrontEndCert = false,
isBackEndCert = false,
isDataVisCert = false,
@ -253,6 +269,8 @@ function getUserIsCertMap(user) {
is2018DataVisCert,
isApisMicroservicesCert,
isInfosecQaCert,
isQaCert,
isInfosecCert,
isFrontEndCert,
isBackEndCert,
isDataVisCert,
@ -380,6 +398,8 @@ function createShowCert(app) {
is2018DataVisCert: true,
isApisMicroservicesCert: true,
isInfosecQaCert: true,
isQaCert: true,
isInfosecCert: true,
isSciCompPyCert: true,
isDataAnalysisPyCert: true,
isMachineLearningPyCert: true,

View File

@ -162,6 +162,8 @@ function postResetProgress(req, res, next) {
isJsAlgoDataStructCert: false,
isApisMicroservicesCert: false,
isInfosecQaCert: false,
isQaCert: false,
isInfosecCert: false,
is2018FullStackCert: false,
isFrontEndCert: false,
isBackEndCert: false,

View File

@ -8,6 +8,8 @@
"jsAlgoDataStruct": "isJsAlgoDataStructCert",
"apisMicroservices": "isApisMicroservicesCert",
"infosecQa": "isInfosecQaCert",
"qa": "isQaCert",
"infosec": "isInfosecCert",
"fullStack": "isFullStackCert",
"sciCompPy": "isSciCompPyCert",
"dataAnalysisPy": "isDataAnalysisPyCert",

View File

@ -8,6 +8,8 @@
"dataVisCert": "Data Visualisation Certification",
"apisMicroservicesCert": "APIs and Microservices Certification",
"infosecQaCert": "Information Security and Quality Assurance Certification",
"qaCert": "Quality Assurance Certification",
"infosecCert": "Information Security Certification",
"sciCompPyCert": "Scientific Computing with Python Certification",
"dataAnalysisPyCert": "Data Analysis with Python Certification",
"machineLearningPyCert": "Machine Learning with Python Certification"

View File

@ -18,6 +18,8 @@ export function completeCommitment$(user) {
isDataVisCert,
isApisMicroservicesCert,
isInfosecQaCert,
isQaCert,
isInfosecCert,
isSciCompPyCert,
isDataAnalysisPyCert,
isMachineLearningPyCert
@ -40,6 +42,8 @@ export function completeCommitment$(user) {
(isDataVisCert && goal === commitGoals.dataVisCert) ||
(isApisMicroservicesCert && goal === commitGoals.apisMicroservicesCert) ||
(isInfosecQaCert && goal === commitGoals.infosecQaCert) ||
(isQaCert && goal === commitGoals.QaCert) ||
(isInfosecCert && goal === commitGoals.infosecCert) ||
(isSciCompPyCert && goal === commitGoals.sciCompPyCert) ||
(isDataAnalysisPyCert && goal === commitGoals.dataAnalysisPyCert) ||
(isMachineLearningPyCert && goal === commitGoals.machineLearningPyCert)

View File

@ -11,6 +11,8 @@
"jsAlgoDataStructId": "561abd10cb81ac38a17513bc",
"apisMicroservicesId": "561add10cb82ac38a17523bc",
"infosecQaId": "561add10cb82ac38a17213bc",
"qaId": "5e611829481575a52dc59c0e",
"infosecId": "5e6021435ac9d0ecd8b94b00",
"fullStackId": "561add10cb82ac38a17213bd",
"sciCompPyId": "5e44431b903586ffb414c951",
"dataAnalysisPyId": "5e46fc95ac417301a38fb934",

View File

@ -4,6 +4,8 @@ function getCompletedCertCount(user) {
'is2018DataVisCert',
'isFrontEndLibsCert',
'isInfosecQaCert',
'isQaCert',
'isInfosecCert',
'isJsAlgoDataStructCert',
'isRespWebDesignCert',
'isSciCompPyCert',

View File

@ -23,6 +23,8 @@ export const publicUserProps = [
'isFrontEndLibsCert',
'isHonest',
'isInfosecQaCert',
'isQaCert',
'isInfosecCert',
'isJsAlgoDataStructCert',
'isRespWebDesignCert',
'isSciCompPyCert',

View File

@ -13,6 +13,8 @@ const superBlockCertTypeMap = {
'data-visualization': certTypes.dataVis2018,
'apis-and-microservices': certTypes.apisMicroservices,
'information-security-and-quality-assurance': certTypes.infosecQa,
'quality-assurance': certTypes.qa,
'information-security': certTypes.infosec,
'full-stack': certTypes.fullStack,
'scientific-computing-with-python': certTypes.sciCompPy,
'data-analysis-with-python': certTypes.dataAnalysisPy,

View File

@ -143,6 +143,8 @@ function getCompletedCertCount(user) {
'is2018DataVisCert',
'isFrontEndLibsCert',
'isInfosecQaCert',
'isQaCert',
'isInfosecCert',
'isJsAlgoDataStructCert',
'isRespWebDesignCert',
'isSciCompPyCert',

View File

@ -60,6 +60,8 @@ const propTypes = {
isFullStackCert: PropTypes.bool,
isHonest: PropTypes.bool,
isInfosecQaCert: PropTypes.bool,
isQaCert: PropTypes.bool,
isInfosecCert: PropTypes.bool,
isJsAlgoDataStructCert: PropTypes.bool,
isRespWebDesignCert: PropTypes.bool,
isSciCompPyCert: PropTypes.bool,
@ -132,6 +134,8 @@ export function ShowSettings(props) {
isDataVisCert,
isFrontEndCert,
isInfosecQaCert,
isQaCert,
isInfosecCert,
isFrontEndLibsCert,
isFullStackCert,
isRespWebDesignCert,
@ -245,9 +249,11 @@ export function ShowSettings(props) {
isFrontEndLibsCert={isFrontEndLibsCert}
isFullStackCert={isFullStackCert}
isHonest={isHonest}
isInfosecCert={isInfosecCert}
isInfosecQaCert={isInfosecQaCert}
isJsAlgoDataStructCert={isJsAlgoDataStructCert}
isMachineLearningPyCert={isMachineLearningPyCert}
isQaCert={isQaCert}
isRespWebDesignCert={isRespWebDesignCert}
isSciCompPyCert={isSciCompPyCert}
username={username}

View File

@ -40,7 +40,7 @@ const AsFeaturedSection = () => (
export const Landing = ({ edges }) => {
const superBlocks = uniq(edges.map(element => element.node.superBlock));
const interviewPrep = superBlocks.splice(9, 1);
const interviewPrep = superBlocks.splice(10, 1);
return (
<Fragment>
<Helmet>

View File

@ -50,9 +50,11 @@ const propTypes = {
isFrontEndLibsCert: PropTypes.bool,
isFullStackCert: PropTypes.bool,
isHonest: PropTypes.bool,
isInfosecCert: PropTypes.bool,
isInfosecQaCert: PropTypes.bool,
isJsAlgoDataStructCert: PropTypes.bool,
isMachineLearningPyCert: PropTypes.bool,
isQaCert: PropTypes.bool,
isRespWebDesignCert: PropTypes.bool,
isSciCompPyCert: PropTypes.bool,
updateLegacyCert: PropTypes.func.isRequired,
@ -70,6 +72,8 @@ const isCertSelector = ({
isDataVisCert,
isFrontEndCert,
isInfosecQaCert,
isQaCert,
isInfosecCert,
isFrontEndLibsCert,
isFullStackCert,
isRespWebDesignCert,
@ -84,6 +88,8 @@ const isCertSelector = ({
isDataVisCert,
isFrontEndCert,
isInfosecQaCert,
isQaCert,
isInfosecCert,
isFrontEndLibsCert,
isFullStackCert,
isRespWebDesignCert,
@ -99,6 +105,8 @@ const isCertMapSelector = createSelector(
isApisMicroservicesCert,
isJsAlgoDataStructCert,
isInfosecQaCert,
isQaCert,
isInfosecCert,
isFrontEndLibsCert,
isRespWebDesignCert,
isDataVisCert,
@ -114,6 +122,8 @@ const isCertMapSelector = createSelector(
'Data Visualization': is2018DataVisCert,
"API's and Microservices": isApisMicroservicesCert,
'Information Security And Quality Assurance': isInfosecQaCert,
'Quality Assurance': isQaCert,
'Information Security': isInfosecCert,
'Scientific Computing with Python': isSciCompPyCert,
'Data Analysis with Python': isDataAnalysisPyCert,
'Machine Learning with Python': isMachineLearningPyCert,
@ -462,7 +472,7 @@ export class CertificationSettings extends Component {
is2018DataVisCert,
isApisMicroservicesCert,
isFrontEndLibsCert,
isInfosecQaCert,
isQaCert,
isJsAlgoDataStructCert,
isRespWebDesignCert
} = this.props;
@ -471,7 +481,7 @@ export class CertificationSettings extends Component {
is2018DataVisCert &&
isApisMicroservicesCert &&
isFrontEndLibsCert &&
isInfosecQaCert &&
isQaCert &&
isJsAlgoDataStructCert &&
isRespWebDesignCert;
@ -509,7 +519,7 @@ export class CertificationSettings extends Component {
<li>Front End Libraries</li>
<li>Data Visualization</li>
<li>APIs and Microservices</li>
<li>Information Security and Quality Assurance</li>
<li>Quality Assurance</li>
</ul>
</div>

View File

@ -189,6 +189,8 @@ const defaultTestProps = {
isFullStackCert: false,
isHonest: false,
isInfosecQaCert: false,
isQaCert: false,
isInfosecCert: false,
isJsAlgoDataStructCert: false,
isRespWebDesignCert: false,
isSciCompPyCert: false,

View File

@ -1,7 +0,0 @@
---
title: Information Security and Quality Assurance
superBlock: Information Security and Quality Assurance
---
## Introduction to Information Security and Quality Assurance
This is a stub introduction for Information Security and Quality Assurance

View File

@ -1,19 +0,0 @@
---
title: Introduction to the Information Security and Quality Assurance Projects
block: Information Security and Quality Assurance Projects
superBlock: Information Security and Quality Assurance
---
## Introduction to the Information Security and Quality Assurance Projects
Now its time to put your newly learned skills to work! By working on these projects, you will have the opportunity of applying all the skills, principles, and concepts from the Information Security and Quality Assurance sections.
In this section you get the chance to:
* Build a Metric-Imperial Converter
* Build an Issue Tracker
* Build a Personal Library
* Build a Stock Price Checker
* Build an Anonymous Message Board
* Port Scanner
* Packet Capturer
When you are done, you will have plenty of Information Security & Quality Assurance projects under your belt along with a certification that you can show off to friends, family, and employers. Have fun and remember to use the [Read-Search-Ask](https://www.freecodecamp.org/forum/t/how-to-get-help-when-you-are-stuck/19514) method if you get stuck.

View File

@ -0,0 +1,7 @@
---
title: Information Security
superBlock: Information Security
---
## Introduction to Information Security
This is a stub introduction for Information Security

View File

@ -0,0 +1,17 @@
---
title: Introduction to the Information Security Projects
block: Information Security Projects
superBlock: Information Security
---
## Introduction to the Information Security Projects
Now its time to put your newly learned skills to work! By working on these projects, you will have the opportunity of applying all the skills, principles, and concepts from the Information Security sections.
In this section you get the chance to:
* Build a Stock Price Checker
* Build an Anonymous Message Board
* Secure Real Time Multiplayer Game
* Port Scanner
* Packet Capturer
When you are done, you will have plenty of Information Security projects under your belt along with a certification that you can show off to friends, family, and employers. Have fun and remember to use the [Read-Search-Ask](https://www.freecodecamp.org/forum/t/how-to-get-help-when-you-are-stuck/19514) method if you get stuck.

View File

@ -1,7 +1,7 @@
---
title: Introduction to Information Security with HelmetJS Challenges
block: Information Security with HelmetJS
superBlock: Information Security and Quality Assurance
superBlock: Information Security
---
## Introduction to Information Security with HelmetJS Challenges

View File

@ -1,7 +1,7 @@
---
title: Introduction to the Advanced Node and Express Challenges
block: Advanced Node and Express
superBlock: Information Security and Quality Assurance
superBlock: Quality Assurance
---
## Introduction to Advanced Node and Express Challenges

View File

@ -0,0 +1,7 @@
---
title: Quality Assurance
superBlock: Quality Assurance
---
## Introduction to Quality Assurance
This is a stub introduction for Quality Assurance

View File

@ -1,7 +1,7 @@
---
title: Introduction to the Quality Assurance with Chai Challenges
block: Quality Assurance and Testing with Chai
superBlock: Information Security and Quality Assurance
superBlock: Quality Assurance
---
## Introduction to Quality Assurance with Chai Challenges

View File

@ -0,0 +1,17 @@
---
title: Introduction to the Quality Assurance Projects
block: Quality Assurance Projects
superBlock: Quality Assurance
---
## Introduction to the Quality Assurance Projects
Now its time to put your newly learned skills to work! By working on these projects, you will have the opportunity of applying all the skills, principles, and concepts from the Quality Assurance sections.
In this section you get the chance to:
* Build a Metric-Imperial Converter
* Build an Issue Tracker
* Build a Personal Library
* Sudoku Solver
* American British Translator
When you are done, you will have plenty of Quality Assurance projects under your belt along with a certification that you can show off to friends, family, and employers. Have fun and remember to use the [Read-Search-Ask](https://www.freecodecamp.org/forum/t/how-to-get-help-when-you-are-stuck/19514) method if you get stuck.

View File

@ -0,0 +1,12 @@
---
title: Introduction to Python for Everybody
block: Python for Everybody
superBlock: Scientific Computing with Python
---
## Introduction to Python for Everybody
Python for Everybody is a video course that teaches the basics of programming computers using Python 3.
The course was created by Dr. Charles Severance (a.k.a. Dr. Chuck). He is a Clinical Professor at the University of Michigan School of Information, where he teaches various technology-oriented courses including programming, database design, and Web development.

View File

@ -213,6 +213,8 @@ export const certificatesByNameSelector = username => state => {
isJsAlgoDataStructCert,
isApisMicroservicesCert,
isInfosecQaCert,
isQaCert,
isInfosecCert,
isFrontEndCert,
isBackEndCert,
isDataVisCert,
@ -229,6 +231,8 @@ export const certificatesByNameSelector = username => state => {
isJsAlgoDataStructCert ||
isApisMicroservicesCert ||
isInfosecQaCert ||
isQaCert ||
isInfosecCert ||
isFullStackCert ||
isSciCompPyCert ||
isDataAnalysisPyCert ||
@ -270,6 +274,16 @@ export const certificatesByNameSelector = username => state => {
title: 'Information Security and Quality Assurance Certification',
showURL: 'information-security-and-quality-assurance'
},
{
show: isQaCert,
title: ' Quality Assurance Certification',
showURL: 'information-security-and-quality-assurance'
},
{
show: isInfosecCert,
title: 'Information Security Certification',
showURL: 'information-security-and-quality-assurance'
},
{
show: isSciCompPyCert,
title: 'Scientific Computing with Python Certification',

View File

@ -7,9 +7,8 @@ const feLibsBase = '/learn/front-end-libraries/front-end-libraries-projects';
const dataVisBase = '/learn/data-visualization/data-visualization-projects';
const apiMicroBase =
'/learn/apis-and-microservices/apis-and-microservices-projects';
const infoSecBase =
'/learn/information-security-and-quality-assurance/' +
'information-security-and-quality-assurance-projects';
const qaBase = 'learn/quality-assurance/quality-assurance-projects';
const infoSecBase = '/learn/information-security/information-security-projects';
const sciCompPyBase =
'/learn/scientific-computing-with-python/' +
'scientific-computing-with-python-projects';
@ -371,48 +370,68 @@ export const projectMap = {
superBlock: 'apis-and-microservices'
}
],
'Information Security And Quality Assurance': [
'Quality Assurance': [
{
id: '587d8249367417b2b2512c41',
title: 'Metric-Imperial Converter',
link: `${infoSecBase}/metric-imperial-converter`,
superBlock: 'information-security-and-quality-assurance'
link: `${qaBase}/metric-imperial-converter`,
superBlock: 'quality-assurance'
},
{
id: '587d8249367417b2b2512c42',
title: 'Issue Tracker',
link: `${infoSecBase}/issue-tracker`,
superBlock: 'information-security-and-quality-assurance'
link: `${qaBase}/issue-tracker`,
superBlock: 'quality-assurance'
},
{
id: '587d824a367417b2b2512c43',
title: 'Personal Library',
link: `${infoSecBase}/personal-library`,
superBlock: 'information-security-and-quality-assurance'
link: `${qaBase}/personal-library`,
superBlock: 'quality-assurance'
},
{
id: '5e601bf95ac9d0ecd8b94afd',
title: 'Sudoku Solver',
link: `${qaBase}/sudoku-solver`,
superBlock: 'quality-assurance'
},
{
id: '5e601c0d5ac9d0ecd8b94afe',
title: 'American British Translator',
link: `${qaBase}/american-british-translator`,
superBlock: 'quality-assurance'
}
],
'Information Security': [
{
id: '587d824a367417b2b2512c44',
title: 'Stock Price Checker',
link: `${infoSecBase}/stock-price-checker`,
superBlock: 'information-security-and-quality-assurance'
superBlock: 'information-security'
},
{
id: '587d824a367417b2b2512c45',
title: 'Anonymous Message Board',
link: `${infoSecBase}/anonymous-message-board`,
superBlock: 'information-security-and-quality-assurance'
superBlock: 'information-security'
},
{
id: '5e46f979ac417301a38fb932',
title: 'Port Scanner',
link: `${infoSecBase}/port-scanner`,
superBlock: 'information-security-and-quality-assurance'
superBlock: 'information-security'
},
{
id: '5e46f983ac417301a38fb933',
title: 'SHA-1 Password Cracker',
link: `${infoSecBase}/sha-1-password-cracker`,
superBlock: 'information-security-and-quality-assurance'
superBlock: 'information-security'
},
{
id: '5e601c775ac9d0ecd8b94aff',
title: 'Real Time Multiplayer Game',
link: `${infoSecBase}/real-time-multiplayer-game`,
superBlock: 'information-security'
}
],
'Scientific Computing with Python': [

View File

@ -0,0 +1,301 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import noop from 'lodash/noop';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { Button, Modal } from '@freecodecamp/react-bootstrap';
import { useStaticQuery, graphql } from 'gatsby';
import SanitizedSpan from '../components/SanitizedSpan';
import Login from '../../../components/Header/components/Login';
import './completion-modal.css';
import {
closeModal,
submitChallenge,
completedChallengesIds,
isCompletionModalOpenSelector,
challengeFilesSelector,
challengeMetaSelector,
lastBlockChalSubmitted
} from '../redux';
import { isSignedInSelector, executeGA } from '../../../redux';
const mapStateToProps = createSelector(
challengeFilesSelector,
challengeMetaSelector,
completedChallengesIds,
isCompletionModalOpenSelector,
isSignedInSelector,
(files, { title, id }, completedChallengesIds, isOpen, isSignedIn) => ({
files,
title,
id,
completedChallengesIds,
isOpen,
isSignedIn
})
);
const mapDispatchToProps = function(dispatch) {
const dispatchers = {
close: () => dispatch(closeModal('completion')),
submitChallenge: () => {
dispatch(submitChallenge());
},
lastBlockChalSubmitted: () => {
dispatch(lastBlockChalSubmitted());
},
executeGA
};
return () => dispatchers;
};
const propTypes = {
answers: PropTypes.array,
blockName: PropTypes.string,
close: PropTypes.func.isRequired,
completedChallengesIds: PropTypes.array,
currentBlockIds: PropTypes.array,
executeGA: PropTypes.func,
files: PropTypes.object.isRequired,
id: PropTypes.string,
isOpen: PropTypes.bool,
isSignedIn: PropTypes.bool.isRequired,
lastBlockChalSubmitted: PropTypes.func,
question: PropTypes.string,
solution: PropTypes.number,
submitChallenge: PropTypes.func.isRequired,
title: PropTypes.string
};
export function getCompletedPercent(
completedChallengesIds = [],
currentBlockIds = [],
currentChallengeId
) {
completedChallengesIds = completedChallengesIds.includes(currentChallengeId)
? completedChallengesIds
: [...completedChallengesIds, currentChallengeId];
const completedChallengesInBlock = completedChallengesIds.filter(id => {
return currentBlockIds.includes(id);
});
const completedPercent = Math.round(
(completedChallengesInBlock.length / currentBlockIds.length) * 100
);
return completedPercent > 100 ? 100 : completedPercent;
}
export class CompletionModalInner extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleKeypress = this.handleKeypress.bind(this);
}
state = {
downloadURL: null,
completedPercent: 0,
selectedOption: 0,
answer: 1,
showWrong: false
};
static getDerivedStateFromProps(props, state) {
const { files, isOpen } = props;
if (!isOpen) {
return null;
}
const { downloadURL } = state;
if (downloadURL) {
URL.revokeObjectURL(downloadURL);
}
let newURL = null;
if (Object.keys(files).length) {
const filesForDownload = Object.keys(files)
.map(key => files[key])
.reduce((allFiles, { path, contents }) => {
const beforeText = `** start of ${path} **\n\n`;
const afterText = `\n\n** end of ${path} **\n\n`;
allFiles +=
files.length > 1 ? beforeText + contents + afterText : contents;
return allFiles;
}, '');
const blob = new Blob([filesForDownload], {
type: 'text/json'
});
newURL = URL.createObjectURL(blob);
}
const { completedChallengesIds, currentBlockIds, id, isSignedIn } = props;
let completedPercent = isSignedIn
? getCompletedPercent(completedChallengesIds, currentBlockIds, id)
: 0;
return { downloadURL: newURL, completedPercent: completedPercent };
}
handleKeypress(e) {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
// Since Hotkeys also listens to Ctrl + Enter we have to stop this event
// getting to it.
e.stopPropagation();
this.handleSubmit();
}
}
handleSubmit() {
if (this.props.solution - 1 === this.state.selectedOption) {
this.setState({
showWrong: false
});
this.props.submitChallenge();
this.checkBlockCompletion();
} else {
this.setState({
showWrong: true
});
}
}
// check block completion for donation
checkBlockCompletion() {
if (
this.state.completedPercent === 100 &&
!this.props.completedChallengesIds.includes(this.props.id)
) {
this.props.lastBlockChalSubmitted();
}
}
componentWillUnmount() {
if (this.state.downloadURL) {
URL.revokeObjectURL(this.state.downloadURL);
}
this.props.close();
}
handleOptionChange = changeEvent => {
console.log(this.state.selectedOption);
this.setState({
selectedOption: parseInt(changeEvent.target.value, 10)
});
};
render() {
const { close, isOpen, isSignedIn, question, answers } = this.props;
if (isOpen) {
executeGA({ type: 'modal', data: '/completion-modal' });
}
return (
<Modal
animation={false}
bsSize='lg'
dialogClassName='challenge-success-modal'
keyboard={true}
onHide={close}
onKeyDown={isOpen ? this.handleKeypress : noop}
show={isOpen}
>
<Modal.Header
className='challenge-list-header fcc-modal'
closeButton={true}
>
<Modal.Title className='completion-message'>Video Quiz</Modal.Title>
</Modal.Header>
<Modal.Body className='question-modal-body'>
<SanitizedSpan text={question} />
<form>
{answers.map((option, index) => (
<div className='form-check'>
<label>
<input
checked={this.state.selectedOption === index}
name='quiz'
onChange={this.handleOptionChange}
type='radio'
value={index}
/>{' '}
<SanitizedSpan text={option} />
</label>
</div>
))}
</form>
<div
style={{
visibility: this.state.showWrong ? 'visible' : 'hidden'
}}
>
Wrong. Try again.
</div>
</Modal.Body>
<Modal.Footer>
{isSignedIn ? null : (
<Login
block={true}
bsSize='lg'
bsStyle='primary'
className='btn-cta'
>
Sign in to save your progress
</Login>
)}
<Button
block={true}
bsSize='large'
bsStyle='primary'
onClick={this.handleSubmit}
>
Submit answer <span className='hidden-xs'>(Ctrl + Enter)</span>
</Button>
</Modal.Footer>
</Modal>
);
}
}
CompletionModalInner.propTypes = propTypes;
const useCurrentBlockIds = blockName => {
const {
allChallengeNode: { edges }
} = useStaticQuery(graphql`
query getCurrentBlockNodesVid {
allChallengeNode(sort: { fields: [superOrder, order, challengeOrder] }) {
edges {
node {
fields {
blockName
}
id
}
}
}
}
`);
const currentBlockIds = edges
.filter(edge => edge.node.fields.blockName === blockName)
.map(edge => edge.node.id);
return currentBlockIds;
};
const CompletionModal = props => {
const currentBlockIds = useCurrentBlockIds(props.blockName || '');
return <CompletionModalInner currentBlockIds={currentBlockIds} {...props} />;
};
CompletionModal.displayName = 'CompletionModal';
CompletionModal.propTypes = propTypes;
export default connect(
mapStateToProps,
mapDispatchToProps
)(CompletionModal);

View File

@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import sanitizeHtml from 'sanitize-html';
const propTypes = {
text: PropTypes.string.isRequired
};
function SanitizedSpan({ text = '' }) {
const sanitizedText = sanitizeHtml(text, {
allowedTags: ['b', 'i', 'em', 'strong', 'code', 'wbr', 'br', 'pre']
});
return <span dangerouslySetInnerHTML={{ __html: sanitizedText }} />;
}
SanitizedSpan.displayName = 'SanitizedSpan';
SanitizedSpan.propTypes = propTypes;
export default SanitizedSpan;

View File

@ -0,0 +1,41 @@
/* global expect */
import React from 'react';
import { render } from '@testing-library/react';
import SanitizedSpan from './SanitizedSpan';
describe('<SanitizedSpan />', () => {
it('matches the snapshot', () => {
const { container } = render(
<SanitizedSpan
text={`some text <script>dangerous code</script>
more <wbr>text <br> <img alt='danger' src='https://attack.com'>`}
/>
);
expect(container).toMatchSnapshot();
});
it('removes scripts, images, etc', () => {
const { queryByAltText, queryByText } = render(
<SanitizedSpan
text={`some text <script>dangerous code</script>
more text <img alt='danger' src='https://attack.com'>`}
/>
);
expect(queryByText('dangerous code', { ignore: false })).toBeNull();
expect(queryByAltText('danger')).toBeNull();
});
it('leaves in line breaks', () => {
const { container } = render(
<SanitizedSpan
text={`some text <br>
more text`}
/>
);
expect(container.querySelector('br')).not.toBeNull();
});
});

View File

@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<SanitizedSpan /> matches the snapshot 1`] = `
<div>
<span>
some text
more
<wbr />
text
<br />
</span>
</div>
`;

View File

@ -11,6 +11,14 @@
justify-content: space-evenly;
}
.video-modal-body {
min-height: 400px;
display: flex;
flex-direction: column;
justify-content: space-evenly;
padding-left: 30px;
}
.completion-challenge-details {
display: flex;
flex-direction: column;

View File

@ -0,0 +1,205 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Button, Grid, Col, Row } from '@freecodecamp/react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { graphql } from 'gatsby';
import Helmet from 'react-helmet';
import YouTube from 'react-youtube';
import { ChallengeNode } from '../../../redux/propTypes';
import {
challengeMounted,
updateChallengeMeta,
openModal,
updateProjectFormValues
} from '../redux';
import LearnLayout from '../../../components/layouts/Learn';
import ChallengeTitle from '../components/Challenge-Title';
import ChallengeDescription from '../components/Challenge-Description';
import Spacer from '../../../components/helpers/Spacer';
import CompletionVideoModal from '../components/CompletionVideoModal';
import HelpModal from '../components/HelpModal';
import Hotkeys from '../components/Hotkeys';
const mapStateToProps = () => ({});
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
updateChallengeMeta,
challengeMounted,
updateProjectFormValues,
openCompletionModal: () => openModal('completion')
},
dispatch
);
const propTypes = {
challengeMounted: PropTypes.func.isRequired,
data: PropTypes.shape({
challengeNode: ChallengeNode
}),
description: PropTypes.string,
openCompletionModal: PropTypes.func.isRequired,
pageContext: PropTypes.shape({
challengeMeta: PropTypes.object
}),
updateChallengeMeta: PropTypes.func.isRequired,
updateProjectFormValues: PropTypes.func.isRequired
};
export class Project extends Component {
constructor(props) {
super(props);
this.state = {
subtitles: ''
};
}
componentDidMount() {
const {
challengeMounted,
data: {
challengeNode: { title, challengeType }
},
pageContext: { challengeMeta },
updateChallengeMeta
} = this.props;
updateChallengeMeta({ ...challengeMeta, title, challengeType });
challengeMounted(challengeMeta.id);
this._container.focus();
}
componentDidUpdate(prevProps) {
const {
data: {
challengeNode: { title: prevTitle }
}
} = prevProps;
const {
challengeMounted,
data: {
challengeNode: { title: currentTitle, challengeType }
},
pageContext: { challengeMeta },
updateChallengeMeta
} = this.props;
if (prevTitle !== currentTitle) {
updateChallengeMeta({
...challengeMeta,
title: currentTitle,
challengeType
});
challengeMounted(challengeMeta.id);
}
}
render() {
const {
data: {
challengeNode: {
fields: { blockName },
title,
description,
videoId,
question: { text, answers, solution }
}
},
openCompletionModal,
pageContext: {
challengeMeta: { introPath, nextChallengePath, prevChallengePath }
}
} = this.props;
const blockNameTitle = `${blockName} - ${title}`;
return (
<Hotkeys
innerRef={c => (this._container = c)}
introPath={introPath}
nextChallengePath={nextChallengePath}
prevChallengePath={prevChallengePath}
>
<LearnLayout>
<Helmet title={`${blockNameTitle} | Learn | freeCodeCamp.org`} />
<Grid>
<Row>
<Col md={8} mdOffset={2} sm={10} smOffset={1} xs={12}>
<Spacer />
<ChallengeTitle>{blockNameTitle}</ChallengeTitle>
<div style={{ textAlign: 'center' }}>
<YouTube
onEnd={openCompletionModal}
opts={{ rel: 0 }}
videoId={videoId}
/>
<i>
<a
href={
'https://www.youtube.com/timedtext_editor?action_mde_edit_form=1&v=' +
videoId +
'&lang=en&bl=watch&ui=hd&ref=wt&tab=captions'
}
rel='noopener noreferrer'
target='_blank'
>
Help improve or add subtitles
</a>
.
</i>
</div>
<Spacer />
<ChallengeDescription description={description} />
<Button
block={true}
bsSize='large'
bsStyle='primary'
onClick={openCompletionModal}
>
Complete
</Button>
<Spacer />
</Col>
<CompletionVideoModal
answers={answers}
blockName={blockName}
question={text}
solution={solution}
/>
<HelpModal />
</Row>
</Grid>
</LearnLayout>
</Hotkeys>
);
}
}
Project.displayName = 'Project';
Project.propTypes = propTypes;
export default connect(
mapStateToProps,
mapDispatchToProps
)(Project);
export const query = graphql`
query VideoChallenge($slug: String!) {
challengeNode(fields: { slug: { eq: $slug } }) {
videoId
title
description
challengeType
fields {
blockName
slug
}
question {
text
answers
solution
}
}
}
`;

View File

@ -14,13 +14,12 @@ const preFormattedBlockNames = {
'apis-and-microservices': 'APIs and Microservices',
'apis-and-microservices-projects': 'APIs and Microservices Projects',
'scientific-computing-with-python': 'Scientific Computing with Python',
'scientific-computing-with-python-projects':
'Scientific Computing with Python Projects',
'scientific-computing-with-python-projects': 'Certification Projects',
'data-analysis-with-python': 'Data Analysis with Python',
'data-analysis-with-python-projects': 'Data Analysis with Python Projects',
'data-analysis-with-python-projects': 'Certification Projects',
'machine-learning-with-python': 'Machine Learning with Python',
'machine-learning-with-python-projects':
'Machine Learning with Python Projects'
'machine-learning-with-python-projects': 'Certification Projects',
'information-security-projects': 'Certification Projects'
};
const noFormatting = ['and', 'for', 'of', 'the', 'up', 'with'];

View File

@ -10,6 +10,7 @@ const step = 7;
const quiz = 8;
const invalid = 9;
const pythonProject = 10;
const video = 11;
// individual exports
exports.backend = backend;
@ -29,7 +30,8 @@ exports.challengeTypes = {
modern,
step,
quiz,
invalid
invalid,
video
};
// turn challengeType to file ext
@ -45,11 +47,12 @@ exports.viewTypes = {
[bonfire]: 'classic',
[frontEndProject]: 'frontend',
[backEndProject]: 'backend',
[pythonProject]: 'backend',
[pythonProject]: 'frontend',
[modern]: 'modern',
[step]: 'step',
[quiz]: 'quiz',
[backend]: 'backend'
[backend]: 'backend',
[video]: 'video'
};
// determine the type of submit function to use for the challenge on completion
@ -68,7 +71,8 @@ exports.submitTypes = {
[step]: 'step',
[quiz]: 'quiz',
[backend]: 'backend',
[modern]: 'tests'
[modern]: 'tests',
[video]: 'tests'
};
// determine which help forum questions should be posted to
@ -109,8 +113,8 @@ exports.helpCategory = {
'information-security-with-helmetjs': 'JavaScript',
'quality-assurance-and-testing-with-chai': 'JavaScript',
'advanced-node-and-express': 'JavaScript',
'information-security-and-quality-assurance-projects':
'Certification Projects',
'quality-assurance-projects': 'Certification Projects',
'information-security-projects': 'Certification Projects',
algorithms: 'JavaScript',
'data-structures': 'JavaScript',
'take-home-projects': 'Certification Projects',

View File

@ -23,12 +23,17 @@ const superBlockIntro = path.resolve(
__dirname,
'../../src/templates/Introduction/SuperBlockIntro.js'
);
const video = path.resolve(
__dirname,
'../../src/templates/Challenges/video/Show.js'
);
const views = {
backend,
classic,
modern: classic,
frontend
frontend,
video
// quiz: Quiz
};

View File

@ -1,11 +1,11 @@
{
"name": "Advanced Node and Express",
"dashedName": "advanced-node-and-express",
"order": 3,
"order": 2,
"time": "5 hours",
"template": "",
"required": [],
"superBlock": "information-security-and-quality-assurance",
"superBlock": "quality-assurance",
"superOrder": 6,
"challengeOrder": [
[

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "coding-interview-prep",
"superOrder": 10,
"superOrder": 11,
"challengeOrder": [
[
"a3f503de51cf954ede28891d",

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"561add10cb82ac38a17523bc",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/apis-and-microservices-certificate.json"
"fileName": "12-certificates/apis-and-microservices-certificate.json"
}

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"5e46fc95ac417301a38fb934",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/data-analysis-with-python-certificate.json"
"fileName": "12-certificates/data-analysis-with-python-certificate.json"
}

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "coding-interview-prep",
"superOrder": 10,
"superOrder": 11,
"challengeOrder": [
[
"587d8253367417b2b2512c6a",

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 10,
"superOrder": 12,
"challengeOrder": [
[
"5a553ca864b52e1d8bceea14",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/data-visualization-certificate.json"
"fileName": "12-certificates/data-visualization-certificate.json"
}

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"561acd10cb82ac38a17513bc",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/front-end-libraries-certificate.json"
"fileName": "12-certificates/front-end-libraries-certificate.json"
}

View File

@ -1,12 +1,12 @@
{
"name": "Information Security and Quality Assurance Projects",
"dashedName": "information-security-and-quality-assurance-projects",
"order": 4,
"order": 0,
"time": "150 hours",
"template": "",
"required": [],
"superBlock": "information-security-and-quality-assurance",
"superOrder": 6,
"superOrder": 13,
"challengeOrder": [
[
"587d8249367417b2b2512c41",
@ -27,16 +27,8 @@
[
"587d824a367417b2b2512c45",
"Anonymous Message Board"
],
[
"5e46f979ac417301a38fb932",
"Port Scanner"
],
[
"5e46f983ac417301a38fb933",
"SHA-1 Password Cracker"
]
],
"helpRoom": "HelpBackend",
"fileName": "06-information-security-and-quality-assurance/quality-assurance-and-information-security-projects.json"
"fileName": "12-information-security-and-quality-assurance/quality-assurance-and-information-security-projects.json"
}

View File

@ -0,0 +1,18 @@
{
"name": "Information Security Certificate",
"dashedName": "information-security-certificate",
"order": 9,
"time": "",
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 12,
"challengeOrder": [
[
"5e6021435ac9d0ecd8b94b00",
"Information Security Certificate"
]
],
"isPrivate": true,
"fileName": "12-certificates/information-security-certificate.json"
}

View File

@ -0,0 +1,34 @@
{
"name": "Information Security Projects",
"dashedName": "information-security-projects",
"order": 2,
"time": "150 hours",
"template": "",
"required": [],
"superBlock": "information-security",
"superOrder": 9,
"challengeOrder": [
[
"587d824a367417b2b2512c44",
"Stock Price Checker"
],
[
"587d824a367417b2b2512c45",
"Anonymous Message Board"
],
[
"5e601c775ac9d0ecd8b94aff",
"Real Time Multiplayer Game"
],
[
"5e46f979ac417301a38fb932",
"Port Scanner"
],
[
"5e46f983ac417301a38fb933",
"SHA-1 Password Cracker"
]
],
"helpRoom": "HelpBackend",
"fileName": "09-information-security/information-security-projects.json"
}

View File

@ -5,8 +5,8 @@
"time": "5 hours",
"template": "",
"required": [],
"superBlock": "information-security-and-quality-assurance",
"superOrder": 6,
"superBlock": "information-security",
"superOrder": 9,
"challengeOrder": [
[
"587d8247367417b2b2512c36",

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"561add10cb82ac38a17213bc",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/information-security-and-quality-assurance-certificate.json"
"fileName": "12-certificates/information-security-and-quality-assurance-certificate.json"
}

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"561abd10cb81ac38a17513bc",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/javascript-algorithms-and-data-structures-certificate.json"
"fileName": "12-certificates/javascript-algorithms-and-data-structures-certificate.json"
}

View File

@ -0,0 +1,262 @@
{
"name": "Python for Everybody",
"dashedName": "lectures-python-for-everybody",
"order": 1,
"time": "15 hours",
"superBlock": "scientific-computing-with-python",
"superOrder": 7,
"challengeOrder": [
[
"5e6a54a558d3af90110a60a0",
"Introduction A"
],
[
"5e6a54af58d3af90110a60a1",
"Introduction B"
],
[
"5e6a54ba58d3af90110a60a2",
"Introduction C"
],
[
"5e6a54c358d3af90110a60a3",
"Introduction D"
],
[
"5e7b9f050b6c005b0e76f056",
"Expressions A"
],
[
"5e7b9f050b6c005b0e76f057",
"Expressions B"
],
[
"5e7b9f050b6c005b0e76f058",
"Conditional A"
],
[
"5e7b9f060b6c005b0e76f059",
"Conditional B"
],
[
"5e7b9f060b6c005b0e76f05a",
"Functions A"
],
[
"5e7b9f060b6c005b0e76f05b",
"Functions B"
],
[
"5e7b9f060b6c005b0e76f05c",
"Iterations A"
],
[
"5e7b9f070b6c005b0e76f05d",
"Iterations B"
],
[
"5e7b9f070b6c005b0e76f05e",
"Iterations C"
],
[
"5e7b9f070b6c005b0e76f05f",
"Iterations D"
],
[
"5e7b9f070b6c005b0e76f060",
"Strings A"
],
[
"5e7b9f070b6c005b0e76f061",
"Strings B"
],
[
"5e7b9f080b6c005b0e76f062",
"Files A"
],
[
"5e7b9f080b6c005b0e76f063",
"Files B"
],
[
"5e7b9f080b6c005b0e76f064",
"Lists A"
],
[
"5e7b9f090b6c005b0e76f065",
"Lists B"
],
[
"5e7b9f090b6c005b0e76f066",
"Lists C"
],
[
"5e7b9f090b6c005b0e76f067",
"Dictionaries A"
],
[
"5e7b9f090b6c005b0e76f068",
"Dictionaries B"
],
[
"5e7b9f0a0b6c005b0e76f069",
"Dictionaries C"
],
[
"5e7b9f0a0b6c005b0e76f06c",
"Tuples A"
],
[
"5e7b9f0b0b6c005b0e76f06d",
"Tuples B"
],
[
"5e7b9f0b0b6c005b0e76f06e",
"RegEx A"
],
[
"5e7b9f0b0b6c005b0e76f06f",
"RegEx B"
],
[
"5e7b9f0b0b6c005b0e76f070",
"RegEx C"
],
[
"5e7b9f0c0b6c005b0e76f071",
"HTTP A"
],
[
"5e7b9f0c0b6c005b0e76f072",
"HTTP B"
],
[
"5e7b9f0c0b6c005b0e76f073",
"HTTP C"
],
[
"5e7b9f0c0b6c005b0e76f074",
"HTTP D"
],
[
"5e7b9f0d0b6c005b0e76f075",
"HTTP E"
],
[
"5e7b9f0d0b6c005b0e76f076",
"HTTP F"
],
[
"5e7b9f0e0b6c005b0e76f07a",
"Web Services A"
],
[
"5e7b9f0e0b6c005b0e76f07b",
"Web Services B"
],
[
"5e7b9f0e0b6c005b0e76f07c",
"Web Services C"
],
[
"5e7b9f140b6c005b0e76f07d",
"Web Services D"
],
[
"5e7b9f140b6c005b0e76f07e",
"Web Services E"
],
[
"5e7b9f150b6c005b0e76f07f",
"Web Services F"
],
[
"5e7b9f150b6c005b0e76f080",
"Web Services G"
],
[
"5e7b9f160b6c005b0e76f085",
"Objects A"
],
[
"5e7b9f160b6c005b0e76f086",
"Objects B"
],
[
"5e7b9f170b6c005b0e76f087",
"Objects C"
],
[
"5e7b9f170b6c005b0e76f088",
"Objects D"
],
[
"5e7b9f170b6c005b0e76f08a",
"Databases A"
],
[
"5e7b9f170b6c005b0e76f08b",
"Databases B"
],
[
"5e7b9f180b6c005b0e76f08c",
"Databases C"
],
[
"5e7b9f180b6c005b0e76f08d",
"Databases D"
],
[
"5e7b9f180b6c005b0e76f08e",
"Databases E"
],
[
"5e7b9f180b6c005b0e76f08f",
"Databases F"
],
[
"5e7b9f190b6c005b0e76f090",
"Databases G"
],
[
"5e7b9f690b6c005b0e76f095",
"Viz A"
],
[
"5e7b9f6a0b6c005b0e76f096",
"Viz B"
],
[
"5e7b9f6a0b6c005b0e76f097",
"Viz C"
],
[
"5e7b9f6a0b6c005b0e76f098",
"Viz Geodata"
],
[
"5e7b9f6a0b6c005b0e76f099",
"Viz Gmane Model"
],
[
"5e7b9f6b0b6c005b0e76f09a",
"Viz Gmane Spider"
],
[
"5e7b9f6b0b6c005b0e76f09b",
"Viz Gmane Viz"
],
[
"5e7b9f6b0b6c005b0e76f09c",
"Viz Page Rank"
],
[
"5e7b9f6b0b6c005b0e76f09d",
"Viz Page Spider"
],
[
"5e7b9f6c0b6c005b0e76f09e",
"Viz Page Viz"
]
]
}

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"660add10cb82ac38a17513be",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/legacy-back-end-certificate.json"
"fileName": "12-certificates/legacy-back-end-certificate.json"
}

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"561add10cb82ac39a17513bc",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/legacy-data-visualization-certificate.json"
"fileName": "12-certificates/legacy-data-visualization-certificate.json"
}

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"561add10cb82ac38a17513be",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/legacy-front-end-certificate.json"
"fileName": "12-certificates/legacy-front-end-certificate.json"
}

View File

@ -1,12 +1,12 @@
{
"name": "Machine Learning with Python Certificate",
"dashedName": "machine-learning-with-python-certificate",
"order": 9,
"order": 10,
"time": "",
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"5e46fc95ac417301a38fb935",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/machine-learning-with-python-certificate.json"
"fileName": "12-certificates/machine-learning-with-python-certificate.json"
}

View File

@ -4,7 +4,7 @@
"order": 1,
"time": "150 hours",
"superBlock": "machine-learning-with-python",
"superOrder": 9,
"superOrder": 10,
"challengeOrder": [
[
"5e46f8d6ac417301a38fb92d",

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "coding-interview-prep",
"superOrder": 10,
"superOrder": 11,
"challengeOrder": [
[
"5900f36e1000cf542c50fe80",

View File

@ -1,11 +1,11 @@
{
"name": "Quality Assurance and Testing with Chai",
"dashedName": "quality-assurance-and-testing-with-chai",
"order": 2,
"order": 1,
"time": "5 hours",
"template": "",
"required": [],
"superBlock": "information-security-and-quality-assurance",
"superBlock": "quality-assurance",
"superOrder": 6,
"challengeOrder": [
[
@ -106,5 +106,5 @@
]
],
"helpRoom": "Help",
"fileName": "06-information-security-and-quality-assurance/testing-with-chai.json"
"fileName": "06-quality-assurance/quality-assurance-and-testing-with-chai.json"
}

View File

@ -0,0 +1,18 @@
{
"name": "Quality Assurance Certificate",
"dashedName": "quality-assurance-certificate",
"order": 6,
"time": "",
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 12,
"challengeOrder": [
[
"5e611829481575a52dc59c0e",
"Quality Assurance Certificate"
]
],
"isPrivate": true,
"fileName": "12-certificates/quality-assurance-certificate.json"
}

View File

@ -0,0 +1,34 @@
{
"name": "Quality Assurance Projects",
"dashedName": "quality-assurance-projects",
"order": 3,
"time": "150 hours",
"template": "",
"required": [],
"superBlock": "quality-assurance",
"superOrder": 6,
"challengeOrder": [
[
"587d8249367417b2b2512c41",
"Metric-Imperial Converter"
],
[
"587d8249367417b2b2512c42",
"Issue Tracker"
],
[
"587d824a367417b2b2512c43",
"Personal Library"
],
[
"5e601bf95ac9d0ecd8b94afd",
"Sudoku Solver"
],
[
"5e601c0d5ac9d0ecd8b94afe",
"American British Translator"
]
],
"helpRoom": "HelpBackend",
"fileName": "06-quality-assurance/quality-assurance-projects.json"
}

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"561add10cb82ac38a17513bc",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/responsive-web-design-certificate.json"
"fileName": "12-certificates/responsive-web-design-certificate.json"
}

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "coding-interview-prep",
"superOrder": 10,
"superOrder": 11,
"challengeOrder": [
[
"594810f028c0303b75339acb",

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "certificates",
"superOrder": 11,
"superOrder": 12,
"challengeOrder": [
[
"5e44431b903586ffb414c951",
@ -14,5 +14,5 @@
]
],
"isPrivate": true,
"fileName": "11-certificates/scientific-computing-with-python-certificate.json"
"fileName": "12-certificates/scientific-computing-with-python-certificate.json"
}

View File

@ -1,7 +1,7 @@
{
"name": "Scientific Computing with Python Projects",
"dashedName": "scientific-computing-with-python-projects",
"order": 1,
"order": 2,
"time": "150 hours",
"superBlock": "scientific-computing-with-python",
"superOrder": 7,

View File

@ -6,7 +6,7 @@
"template": "",
"required": [],
"superBlock": "coding-interview-prep",
"superOrder": 10,
"superOrder": 11,
"challengeOrder": [
[
"bd7158d8c442eddfaeb5bd10",

Some files were not shown because too many files have changed in this diff Show More