mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat: save submission to db (#64450)
This commit is contained in:
@@ -2,7 +2,7 @@ import type { CompletedChallenge } from '@prisma/client';
|
||||
import validator from 'validator';
|
||||
import type { FastifyPluginCallbackTypebox } from '@fastify/type-provider-typebox';
|
||||
|
||||
import { getChallenges } from '../../utils/get-challenges.js';
|
||||
import { challenges, getChallenges } from '../../utils/get-challenges.js';
|
||||
import {
|
||||
Certification,
|
||||
certSlugTypeMap,
|
||||
@@ -216,7 +216,6 @@ export const protectedCertificateRoutes: FastifyPluginCallbackTypebox = (
|
||||
_options,
|
||||
done
|
||||
) => {
|
||||
const challenges = getChallenges();
|
||||
const certLookup = createCertLookup(challenges);
|
||||
|
||||
// TODO(POST_MVP): Response should not include updated user. If a client wants the updated user, it should make a separate request
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
formatCoderoadChallengeCompletedValidation,
|
||||
formatProjectCompletedValidation
|
||||
} from '../../utils/error-formatting.js';
|
||||
import { getChallenges } from '../../utils/get-challenges.js';
|
||||
import { challenges } from '../../utils/get-challenges.js';
|
||||
import { ProgressTimestamp, getPoints } from '../../utils/progress.js';
|
||||
import {
|
||||
validateExamFromDbSchema,
|
||||
@@ -57,8 +57,6 @@ const userChallengeSelect = {
|
||||
savedChallenges: true
|
||||
};
|
||||
|
||||
const challenges = getChallenges();
|
||||
|
||||
/**
|
||||
* Plugin for the challenge submission endpoints.
|
||||
*
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { ExamResults, user, Prisma } from '@prisma/client';
|
||||
import { FastifyInstance } from 'fastify';
|
||||
import { omit, pick } from 'lodash-es';
|
||||
import { challengeTypes } from '../../../shared/config/challenge-types.js';
|
||||
import { getChallenges } from './get-challenges.js';
|
||||
import { challenges, savableChallenges } from './get-challenges.js';
|
||||
import { normalizeDate } from './normalize.js';
|
||||
|
||||
export const jsCertProjectIds = [
|
||||
@@ -13,15 +13,15 @@ export const jsCertProjectIds = [
|
||||
'aa2e6f85cab2ab736c9a9b24'
|
||||
];
|
||||
|
||||
export const multifileCertProjectIds = getChallenges()
|
||||
export const multifileCertProjectIds = challenges
|
||||
.filter(c => c.challengeType === challengeTypes.multifileCertProject)
|
||||
.map(c => c.id);
|
||||
|
||||
export const multifilePythonCertProjectIds = getChallenges()
|
||||
export const multifilePythonCertProjectIds = challenges
|
||||
.filter(c => c.challengeType === challengeTypes.multifilePythonCertProject)
|
||||
.map(c => c.id);
|
||||
|
||||
export const msTrophyChallenges = getChallenges()
|
||||
export const msTrophyChallenges = challenges
|
||||
.filter(challenge => challenge.challengeType === challengeTypes.msTrophy)
|
||||
.map(({ id, msTrophyId }) => ({ id, msTrophyId }));
|
||||
|
||||
@@ -133,11 +133,7 @@ export async function updateUserChallengeData(
|
||||
_completedChallenge;
|
||||
let completedChallenge: CompletedChallenge;
|
||||
|
||||
if (
|
||||
jsCertProjectIds.includes(challengeId) ||
|
||||
multifileCertProjectIds.includes(challengeId) ||
|
||||
multifilePythonCertProjectIds.includes(challengeId)
|
||||
) {
|
||||
if (savableChallenges.has(challengeId)) {
|
||||
completedChallenge = {
|
||||
..._completedChallenge,
|
||||
files: files?.map(
|
||||
|
||||
@@ -21,6 +21,7 @@ interface Block {
|
||||
challengeType: number;
|
||||
url?: string;
|
||||
msTrophyId?: string;
|
||||
saveSubmissionToDB?: boolean;
|
||||
}[];
|
||||
}
|
||||
|
||||
@@ -51,3 +52,13 @@ export function getChallenges(): Block['challenges'] {
|
||||
return [...acc, ...challengesForBlock.flat()];
|
||||
}, []);
|
||||
}
|
||||
|
||||
export const challenges = getChallenges();
|
||||
|
||||
export const savableChallenges = challenges.reduce((acc, curr) => {
|
||||
if (curr.saveSubmissionToDB) {
|
||||
acc.add(curr.id);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, new Set<string>());
|
||||
|
||||
@@ -103,6 +103,7 @@ exports.createPages = async function createPages({
|
||||
history
|
||||
fileKey
|
||||
}
|
||||
saveSubmissionToDB
|
||||
solutions {
|
||||
contents
|
||||
ext
|
||||
@@ -334,6 +335,7 @@ exports.createSchemaCustomization = ({ actions }) => {
|
||||
questions: [Question]
|
||||
quizzes: [Quiz]
|
||||
required: [RequiredResource]
|
||||
saveSubmissionToDB: Boolean
|
||||
scene: Scene
|
||||
solutions: [[FileContents]]
|
||||
suborder: Int
|
||||
|
||||
@@ -246,6 +246,7 @@ export type ChallengeNode = {
|
||||
quizzes: Quiz[];
|
||||
assignments: string[];
|
||||
required: Required[];
|
||||
saveSubmissionToDB?: boolean;
|
||||
scene: FullScene;
|
||||
solutions: {
|
||||
[T: string]: FileKeyChallenge;
|
||||
@@ -309,6 +310,7 @@ export type DailyCodingChallengeNode = {
|
||||
notes: string;
|
||||
videoUrl?: string;
|
||||
translationPending: false;
|
||||
saveSubmissionToDB?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -534,6 +536,7 @@ export type ChallengeMeta = {
|
||||
helpCategory: string;
|
||||
disableLoopProtectTests: boolean;
|
||||
disableLoopProtectPreview: boolean;
|
||||
saveSubmissionToDB?: boolean;
|
||||
} & NavigationPaths;
|
||||
|
||||
export type NavigationPaths = {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { call, put, select, takeEvery } from 'redux-saga/effects';
|
||||
|
||||
import { canSaveToDB } from '../../../shared-dist/config/challenge-types';
|
||||
import { createFlashMessage } from '../components/Flash/redux';
|
||||
import { FlashMessages } from '../components/Flash/redux/flash-messages';
|
||||
import {
|
||||
@@ -19,7 +18,9 @@ import { saveChallengeComplete } from './actions';
|
||||
import { savedChallengesSelector } from './selectors';
|
||||
|
||||
function* saveChallengeSaga() {
|
||||
const { id, challengeType } = yield select(challengeMetaSelector);
|
||||
const { id, challengeType, saveSubmissionToDB } = yield select(
|
||||
challengeMetaSelector
|
||||
);
|
||||
const { challengeFiles } = yield select(challengeDataSelector);
|
||||
const savedChallenges = yield select(savedChallengesSelector);
|
||||
const savedChallenge = savedChallenges.find(challenge => challenge.id === id);
|
||||
@@ -34,7 +35,7 @@ function* saveChallengeSaga() {
|
||||
);
|
||||
}
|
||||
|
||||
if (canSaveToDB(challengeType)) {
|
||||
if (saveSubmissionToDB) {
|
||||
const body = standardizeRequestBody({ id, challengeFiles, challengeType });
|
||||
const bodySizeInBytes = getStringSizeInBytes(body);
|
||||
|
||||
|
||||
@@ -34,10 +34,7 @@ import type {
|
||||
} from '../../../redux/prop-types';
|
||||
import { editorToneOptions } from '../../../utils/tone/editor-config';
|
||||
import { editorNotes } from '../../../utils/tone/editor-notes';
|
||||
import {
|
||||
canSaveToDB,
|
||||
challengeTypes
|
||||
} from '../../../../../shared-dist/config/challenge-types';
|
||||
import { challengeTypes } from '../../../../../shared-dist/config/challenge-types';
|
||||
import {
|
||||
executeChallenge,
|
||||
saveEditorContent,
|
||||
@@ -106,6 +103,7 @@ export interface EditorProps {
|
||||
resizeProps: ResizeProps;
|
||||
saveChallenge: () => void;
|
||||
saveEditorContent: () => void;
|
||||
saveSubmissionToDB?: boolean;
|
||||
setEditorFocusability: (isFocusable: boolean) => void;
|
||||
submitChallenge: () => void;
|
||||
stopResetting: () => void;
|
||||
@@ -154,7 +152,10 @@ const mapStateToProps = createSelector(
|
||||
(
|
||||
attempts: number,
|
||||
canFocus: boolean,
|
||||
{ challengeType }: { challengeType: number },
|
||||
{
|
||||
challengeType,
|
||||
saveSubmissionToDB
|
||||
}: { challengeType: number; saveSubmissionToDB?: boolean },
|
||||
open,
|
||||
previewOpen: boolean,
|
||||
isResetting: boolean,
|
||||
@@ -166,6 +167,7 @@ const mapStateToProps = createSelector(
|
||||
attempts,
|
||||
canFocus: open ? false : canFocus,
|
||||
challengeType,
|
||||
saveSubmissionToDB,
|
||||
previewOpen,
|
||||
isResetting,
|
||||
isSignedIn,
|
||||
@@ -615,7 +617,7 @@ const Editor = (props: EditorProps): JSX.Element => {
|
||||
monaco.KeyMod.WinCtrl | monaco.KeyCode.KeyS
|
||||
],
|
||||
run:
|
||||
canSaveToDB(props.challengeType) && props.isSignedIn
|
||||
props.saveSubmissionToDB && props.isSignedIn
|
||||
? // save to database
|
||||
props.saveChallenge
|
||||
: // save to local storage
|
||||
|
||||
@@ -212,7 +212,8 @@ function ShowClassic({
|
||||
usesMultifileEditor,
|
||||
notes,
|
||||
videoUrl,
|
||||
translationPending
|
||||
translationPending,
|
||||
saveSubmissionToDB
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -551,7 +552,10 @@ function ShowClassic({
|
||||
superBlock={superBlock}
|
||||
/>
|
||||
<VideoModal videoUrl={videoUrl} />
|
||||
<ResetModal challengeType={challengeType} challengeTitle={title} />
|
||||
<ResetModal
|
||||
saveSubmissionToDB={saveSubmissionToDB}
|
||||
challengeTitle={title}
|
||||
/>
|
||||
<ProjectPreviewModal
|
||||
challengeData={challengeData}
|
||||
closeText={t('buttons.start-coding')}
|
||||
@@ -613,6 +617,7 @@ export const query = graphql`
|
||||
editableRegionBoundaries
|
||||
history
|
||||
}
|
||||
saveSubmissionToDB
|
||||
tests {
|
||||
text
|
||||
testString
|
||||
|
||||
@@ -8,12 +8,11 @@ import { Button, Modal } from '@freecodecamp/ui';
|
||||
import { closeModal, resetChallenge } from '../redux/actions';
|
||||
import { isResetModalOpenSelector } from '../redux/selectors';
|
||||
import callGA from '../../../analytics/call-ga';
|
||||
import { canSaveToDB } from '../../../../../shared-dist/config/challenge-types';
|
||||
|
||||
interface ResetModalProps {
|
||||
close: () => void;
|
||||
isOpen: boolean;
|
||||
challengeType: number;
|
||||
saveSubmissionToDB?: boolean;
|
||||
reset: () => void;
|
||||
challengeTitle: string;
|
||||
}
|
||||
@@ -41,7 +40,7 @@ function withActions(...fns: Array<() => void>) {
|
||||
function ResetModal({
|
||||
reset,
|
||||
close,
|
||||
challengeType,
|
||||
saveSubmissionToDB,
|
||||
isOpen,
|
||||
challengeTitle
|
||||
}: ResetModalProps): JSX.Element {
|
||||
@@ -56,7 +55,7 @@ function ResetModal({
|
||||
</Modal.Header>
|
||||
<Modal.Body alignment='center'>
|
||||
<p>
|
||||
{canSaveToDB(challengeType)
|
||||
{saveSubmissionToDB
|
||||
? t('learn.revert-warn')
|
||||
: t('learn.reset-warn', {
|
||||
title: challengeTitle
|
||||
@@ -73,7 +72,7 @@ function ResetModal({
|
||||
variant='danger'
|
||||
onClick={withActions(reset, close)}
|
||||
>
|
||||
{canSaveToDB(challengeType)
|
||||
{saveSubmissionToDB
|
||||
? t('buttons.revert-to-saved-code')
|
||||
: t('buttons.reset-lesson')}
|
||||
</Button>
|
||||
|
||||
@@ -7,7 +7,6 @@ import { connect } from 'react-redux';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
import { canSaveToDB } from '../../../../../shared-dist/config/challenge-types';
|
||||
import { openModal, executeChallenge } from '../redux/actions';
|
||||
import { challengeMetaSelector } from '../redux/selectors';
|
||||
import { saveChallenge } from '../../../redux/actions';
|
||||
@@ -18,11 +17,8 @@ import './tool-panel.css';
|
||||
const mapStateToProps = createSelector(
|
||||
challengeMetaSelector,
|
||||
isSignedInSelector,
|
||||
(
|
||||
{ challengeType }: { challengeId: string; challengeType: number },
|
||||
isSignedIn
|
||||
) => ({
|
||||
challengeType,
|
||||
({ saveSubmissionToDB }: { saveSubmissionToDB?: boolean }, isSignedIn) => ({
|
||||
saveSubmissionToDB,
|
||||
isSignedIn
|
||||
})
|
||||
);
|
||||
@@ -39,7 +35,7 @@ const mapDispatchToProps = (dispatch: Dispatch) =>
|
||||
);
|
||||
|
||||
interface ToolPanelProps {
|
||||
challengeType: number;
|
||||
saveSubmissionToDB?: boolean;
|
||||
executeChallenge: (options?: { showCompletionModal: boolean }) => void;
|
||||
saveChallenge: () => void;
|
||||
isMobile?: boolean;
|
||||
@@ -52,7 +48,7 @@ interface ToolPanelProps {
|
||||
}
|
||||
|
||||
function ToolPanel({
|
||||
challengeType,
|
||||
saveSubmissionToDB,
|
||||
executeChallenge,
|
||||
saveChallenge,
|
||||
isMobile,
|
||||
@@ -76,7 +72,7 @@ function ToolPanel({
|
||||
<Button block={true} variant='primary' onClick={handleRunTests}>
|
||||
{isMobile ? t('buttons.run') : t('buttons.run-test')}
|
||||
</Button>
|
||||
{isSignedIn && canSaveToDB(challengeType) && (
|
||||
{isSignedIn && saveSubmissionToDB && (
|
||||
<>
|
||||
<Spacer size='xxs' />
|
||||
<Button block={true} variant='primary' onClick={saveChallenge}>
|
||||
@@ -87,9 +83,9 @@ function ToolPanel({
|
||||
<Spacer size='xxs' />
|
||||
<Button block={true} variant='primary' onClick={openResetModal}>
|
||||
{isMobile
|
||||
? t(canSaveToDB(challengeType) ? 'buttons.revert' : 'buttons.reset')
|
||||
? t(saveSubmissionToDB ? 'buttons.revert' : 'buttons.reset')
|
||||
: t(
|
||||
canSaveToDB(challengeType)
|
||||
saveSubmissionToDB
|
||||
? 'buttons.revert-to-saved-code'
|
||||
: 'buttons.reset-lesson'
|
||||
)}
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
msTrophyVerified
|
||||
} from '../../../utils/error-messages';
|
||||
import {
|
||||
canSaveToDB,
|
||||
challengeTypes,
|
||||
getIsDailyCodingChallenge,
|
||||
getDailyCodingChallengeLanguage,
|
||||
@@ -119,7 +118,8 @@ function submitModern(type, state) {
|
||||
}
|
||||
|
||||
if (type === actionTypes.submitChallenge) {
|
||||
const { id, block, challengeType } = challengeMetaSelector(state);
|
||||
const { id, challengeType, saveSubmissionToDB } =
|
||||
challengeMetaSelector(state);
|
||||
|
||||
let update;
|
||||
|
||||
@@ -140,10 +140,7 @@ function submitModern(type, state) {
|
||||
const challengeFiles = challengeFilesSelector(state);
|
||||
|
||||
let body;
|
||||
if (
|
||||
block === 'javascript-algorithms-and-data-structures-projects' ||
|
||||
canSaveToDB(challengeType)
|
||||
) {
|
||||
if (saveSubmissionToDB) {
|
||||
body = standardizeRequestBody({ id, challengeType, challengeFiles });
|
||||
} else {
|
||||
body = {
|
||||
|
||||
@@ -14,10 +14,7 @@ import {
|
||||
takeLatest
|
||||
} from 'redux-saga/effects';
|
||||
|
||||
import {
|
||||
canSaveToDB,
|
||||
challengeTypes
|
||||
} from '../../../../../shared-dist/config/challenge-types';
|
||||
import { challengeTypes } from '../../../../../shared-dist/config/challenge-types';
|
||||
import { createFlashMessage } from '../../../components/Flash/redux';
|
||||
import { FlashMessages } from '../../../components/Flash/redux/flash-messages';
|
||||
import {
|
||||
@@ -77,11 +74,13 @@ const LOGS_TO_IGNORE = [
|
||||
|
||||
// when 'run tests' is clicked, do this first
|
||||
function* executeCancellableChallengeSaga(payload) {
|
||||
const { challengeType, id } = yield select(challengeMetaSelector);
|
||||
const { challengeType, id, saveSubmissionToDB } = yield select(
|
||||
challengeMetaSelector
|
||||
);
|
||||
const { challengeFiles } = yield select(challengeDataSelector);
|
||||
|
||||
// if canSaveToDB, see if body/code size is submittable
|
||||
if (canSaveToDB(challengeType)) {
|
||||
if (saveSubmissionToDB) {
|
||||
const body = standardizeRequestBody({ id, challengeFiles, challengeType });
|
||||
const bodySizeInBytes = getStringSizeInBytes(body);
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ const initialState = {
|
||||
isLastChallengeInBlock: false,
|
||||
nextChallengePath: '/',
|
||||
prevChallengePath: '/',
|
||||
challengeType: -1
|
||||
challengeType: -1,
|
||||
saveSubmissionToDB: false
|
||||
},
|
||||
challengeTests: [],
|
||||
consoleOut: [],
|
||||
|
||||
@@ -100,7 +100,8 @@ exports.createChallengePages = function (
|
||||
template,
|
||||
challengeType,
|
||||
id,
|
||||
isLastChallengeInBlock
|
||||
isLastChallengeInBlock,
|
||||
saveSubmissionToDB
|
||||
} = node.challenge;
|
||||
|
||||
createPage({
|
||||
@@ -123,7 +124,8 @@ exports.createChallengePages = function (
|
||||
isLastChallengeInBlock: isLastChallengeInBlock,
|
||||
nextChallengePath: idToNextPathCurrentCurriculum[node.id],
|
||||
prevChallengePath: idToPrevPathCurrentCurriculum[node.id],
|
||||
id
|
||||
id,
|
||||
saveSubmissionToDB
|
||||
},
|
||||
projectPreview: getProjectPreviewConfig(
|
||||
node.challenge,
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 5e44413e903586ffb414c94e
|
||||
title: Build a Budget App Project
|
||||
challengeType: 23
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 462361
|
||||
dashedName: build-a-budget-app-project
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 657bdcc3a322aae1eac38392
|
||||
title: Build a Cash Register
|
||||
challengeType: 14
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 16012
|
||||
dashedName: build-a-cash-register
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 657bdc55a322aae1eac3838f
|
||||
title: Build a Palindrome Checker
|
||||
challengeType: 14
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 16004
|
||||
dashedName: build-a-palindrome-checker
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: bd7158d8c242eddfaeb5bd13
|
||||
title: Build a Personal Portfolio Webpage
|
||||
challengeType: 14
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 301143
|
||||
dashedName: build-a-personal-portfolio-webpage
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 5e444147903586ffb414c94f
|
||||
title: Build a Polygon Area Calculator Project
|
||||
challengeType: 23
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 462363
|
||||
dashedName: build-a-polygon-area-calculator-project
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 5e44414f903586ffb414c950
|
||||
title: Build a Probability Calculator Project
|
||||
challengeType: 23
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 462364
|
||||
dashedName: build-a-probability-calculator-project
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 587d78af367417b2b2512b04
|
||||
title: Build a Product Landing Page
|
||||
challengeType: 14
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 301144
|
||||
dashedName: build-a-product-landing-page
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 657bdc8ba322aae1eac38390
|
||||
title: Build a Roman Numeral Converter
|
||||
challengeType: 14
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 16044
|
||||
dashedName: build-a-roman-numeral-converter
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 587d78af367417b2b2512b03
|
||||
title: Build a Survey Form
|
||||
challengeType: 14
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 301145
|
||||
dashedName: build-a-survey-form
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 587d78b0367417b2b2512b05
|
||||
title: Build a Technical Documentation Page
|
||||
challengeType: 14
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 301146
|
||||
dashedName: build-a-technical-documentation-page
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 657bdcb9a322aae1eac38391
|
||||
title: Build a Telephone Number Validator
|
||||
challengeType: 14
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 16090
|
||||
dashedName: build-a-telephone-number-validator
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 5e444136903586ffb414c94d
|
||||
title: Build a Time Calculator Project
|
||||
challengeType: 23
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 462360
|
||||
dashedName: build-a-time-calculator-project
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: bd7158d8c442eddfaeb5bd18
|
||||
title: Build a Tribute Page
|
||||
challengeType: 14
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 301147
|
||||
dashedName: build-a-tribute-page
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 5e44412c903586ffb414c94c
|
||||
title: Build an Arithmetic Formatter Project
|
||||
challengeType: 23
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 462359
|
||||
dashedName: build-an-arithmetic-formatter-project
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 6555c1d3e11a1574434cf8b5
|
||||
title: Build an RPG Creature Search App
|
||||
challengeType: 14
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 16003
|
||||
dashedName: build-an-rpg-creature-search-app
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: 56533eb9ac21ba0edf2244e2
|
||||
title: Caesars Cipher
|
||||
challengeType: 5
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 16003
|
||||
dashedName: caesars-cipher
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: a7f4d8f2483413a6ce226cac
|
||||
title: Roman Numeral Converter
|
||||
challengeType: 5
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 16044
|
||||
dashedName: roman-numeral-converter
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: aa2e6f85cab2ab736c9a9b24
|
||||
title: Cash Register
|
||||
challengeType: 5
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 16012
|
||||
dashedName: cash-register
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: aaa48de84e1ecc7c742e1124
|
||||
title: Palindrome Checker
|
||||
challengeType: 5
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 16004
|
||||
dashedName: palindrome-checker
|
||||
---
|
||||
|
||||
+1
@@ -2,6 +2,7 @@
|
||||
id: aff0395860f5d3034dc0bfc9
|
||||
title: Telephone Number Validator
|
||||
challengeType: 5
|
||||
saveSubmissionToDB: true
|
||||
forumTopicId: 16090
|
||||
dashedName: telephone-number-validator
|
||||
---
|
||||
|
||||
+1
@@ -3,6 +3,7 @@ id: 6718d2d59337c822ecb697ff
|
||||
title: Build a Bank Account Management Program
|
||||
challengeType: 26
|
||||
dashedName: build-a-bank-account-management-program
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Book Inventory App
|
||||
challengeType: 25
|
||||
dashedName: build-a-book-inventory-app
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 5e44413e903586ffb414c94e
|
||||
title: Build a Budget App
|
||||
challengeType: 27
|
||||
dashedName: build-a-budget-app
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Celestial Bodies Database
|
||||
challengeType: 13
|
||||
url: freeCodeCamp/learn-celestial-bodies-database
|
||||
dashedName: lab-celestial-bodies-database
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Currency Converter
|
||||
challengeType: 25
|
||||
dashedName: build-a-currency-converter
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -4,6 +4,7 @@ title: Build a Drum Machine
|
||||
challengeType: 25
|
||||
dashedName: build-drum-machine
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 67ed03ac474c48692f41749e
|
||||
title: Build a Hash Table
|
||||
challengeType: 27
|
||||
dashedName: build-a-hash-table
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Markdown to HTML Converter
|
||||
challengeType: 25
|
||||
dashedName: build-a-markdown-to-html-converter
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Number Guessing Game
|
||||
challengeType: 13
|
||||
url: freeCodeCamp/learn-number-guessing-game
|
||||
dashedName: lab-number-guessing-game
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Page of Playing Cards
|
||||
challengeType: 25
|
||||
dashedName: build-a-page-of-playing-cards
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Palindrome Checker
|
||||
challengeType: 25
|
||||
dashedName: build-a-palindrome-checker
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Periodic Table Database
|
||||
challengeType: 13
|
||||
url: freeCodeCamp/learn-periodic-table-database
|
||||
dashedName: lab-periodic-table-database
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Personal Portfolio
|
||||
challengeType: 25
|
||||
dashedName: build-a-personal-portfolio
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -3,6 +3,7 @@ id: 5e444147903586ffb414c94f
|
||||
title: Build a Polygon Area Calculator
|
||||
challengeType: 27
|
||||
dashedName: build-a-polygon-area-calculator
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Product Landing Page
|
||||
challengeType: 25
|
||||
dashedName: build-a-product-landing-page
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Salon Appointment Scheduler
|
||||
challengeType: 13
|
||||
url: freeCodeCamp/learn-salon-appointment-scheduler
|
||||
dashedName: lab-salon-appointment-scheduler
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -4,6 +4,7 @@ title: Build a Survey Form
|
||||
challengeType: 25
|
||||
dashedName: build-a-survey-form
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a Technical Documentation Page
|
||||
challengeType: 25
|
||||
demoType: onClick
|
||||
dashedName: build-a-technical-documentation-page
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -4,6 +4,7 @@ title: Build a Tic-Tac-Toe Game
|
||||
challengeType: 25
|
||||
dashedName: build-a-tic-tac-toe-game
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
id: 68773ee26f332a80bc0295db
|
||||
title: Implement the Tower of Hanoi Algorithm
|
||||
challengeType: 23
|
||||
saveSubmissionToDB: true
|
||||
dashedName: implement-the-tower-of-hanoi-algorithm
|
||||
---
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ title: Build a Tribute Page
|
||||
challengeType: 25
|
||||
demoType: onClick
|
||||
dashedName: build-a-tribute-page
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -3,6 +3,7 @@ id: 684aaf9ec670c68d20efd0d0
|
||||
title: Build a User Configuration Manager
|
||||
challengeType: 27
|
||||
dashedName: build-a-user-configuration-manager
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -3,6 +3,7 @@ id: 673b567e3ba535dda140d278
|
||||
title: Build a Voting System
|
||||
challengeType: 26
|
||||
dashedName: build-a-voting-system
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -4,6 +4,7 @@ title: Build a Weather App
|
||||
challengeType: 25
|
||||
dashedName: lab-weather-app
|
||||
demoType: onClick
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
+1
@@ -4,6 +4,7 @@ title: Build a World Cup Database
|
||||
challengeType: 13
|
||||
url: freeCodeCamp/learn-world-cup-database
|
||||
dashedName: lab-world-cup-database
|
||||
saveSubmissionToDB: true
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
@@ -315,6 +315,7 @@ const schema = Joi.object().keys({
|
||||
then: Joi.array().items(Joi.string()).required(),
|
||||
otherwise: Joi.array().items(Joi.string())
|
||||
}),
|
||||
saveSubmissionToDB: Joi.bool(),
|
||||
scene: Joi.object().keys({
|
||||
setup: setupJoi.required(),
|
||||
commands: Joi.array()
|
||||
|
||||
@@ -170,10 +170,6 @@ export const submitTypes = {
|
||||
[review]: 'tests'
|
||||
};
|
||||
|
||||
export const canSaveToDB = (challengeType: number): boolean =>
|
||||
challengeType === challengeTypes.multifileCertProject ||
|
||||
challengeType === challengeTypes.multifilePythonCertProject;
|
||||
|
||||
const dailyCodingChallengeTypes = [
|
||||
challengeTypes.dailyChallengeJs,
|
||||
challengeTypes.dailyChallengePy
|
||||
|
||||
Reference in New Issue
Block a user