Files
freeCodeCamp/tools/daily-challenges/seed-daily-challenges.ts
2025-06-18 20:29:49 +02:00

111 lines
3.6 KiB
TypeScript

/*
Script to seed daily challenges to freeCodeCamp database, DailyCodingChallenges collection.
It gets the daily challenge data from the dev-playground superblock using GraphQL.
The main client needs to be running with upcoming changes shown to get the info from GraphQL.
Run the curriculum tests on the dev-playground superblock before seeding to make sure they pass.
*/
import 'dotenv/config';
import { MongoClient } from 'mongodb';
import { combineChallenges, fetchChallenges, handleError } from './helpers';
const { MONGOHQ_URL } = process.env;
// Number challenges in the dev-playground blocks
// Update this if the number of challenges changes
const EXPECTED_CHALLENGE_COUNT = 24;
// Date to set for the first challenge, second challenge will be one day later, etc...
// **DO NOT CHANGE THIS AFTER RELEASE**
const year = 2025;
const monthIndex = 5; // 0-indexed -> 5 = June
const day = 10;
const START_DATE = new Date(Date.UTC(year, monthIndex, day));
const ONE_DAY_IN_MS = 24 * 60 * 60 * 1000;
// Sanity check to make sure the start date hasn't unintentionally changed
// **IT SHOULD NOT CHANGE AFTER RELEASE**
const startDateString = '2025-06-10T00:00:00.000Z';
if (START_DATE.toISOString() !== startDateString) {
throw new Error(
`It appears the start date has changed from "${startDateString}".
Are you sure you want to change that? If you are seeding production,
you should not change the start date after the daily challenges have been release.
`
);
}
const client = new MongoClient(
MONGOHQ_URL || 'mongodb://127.0.0.1:27017/freecodecamp?directConnection=true'
);
const seed = async () => {
await client.db('admin').command({ ping: 1 });
console.log('Successfully connected to mongo');
const db = client.db('freecodecamp');
const dailyCodingChallenges = db.collection('DailyCodingChallenges');
console.log('Fetching challenges...');
const jsChallenges = await fetchChallenges('javascript');
const pyChallenges = await fetchChallenges('python');
if (jsChallenges.length !== pyChallenges.length) {
throw new Error(
`Number of challenges do not match: ${jsChallenges.length} JavaScript vs ${pyChallenges.length} Python challenges found`
);
}
if (jsChallenges.length !== EXPECTED_CHALLENGE_COUNT) {
throw new Error(
`Expected ${EXPECTED_CHALLENGE_COUNT} challenges, but found ${jsChallenges.length} challenges`
);
}
console.log(`${jsChallenges.length} challenges found for each language`);
console.log('Creating new challenges...');
const newChallenges = [];
for (let i = 0; i < jsChallenges.length; i++) {
const jsChallenge = jsChallenges[i];
const pyChallenge = pyChallenges[i];
const newChallenge = combineChallenges({
jsChallenge,
pyChallenge,
challengeNumber: i + 1,
date: new Date(START_DATE.getTime() + i * ONE_DAY_IN_MS)
});
newChallenges.push(newChallenge);
}
console.log('Finished creating new challenges');
console.log(`Writing ${newChallenges.length} challenges to database...`);
// Replace if the object exists, create new one if it doesn't
const bulkOps = newChallenges.map(challenge => ({
replaceOne: {
filter: { _id: challenge._id },
replacement: challenge,
upsert: true
}
}));
await dailyCodingChallenges.bulkWrite(bulkOps);
console.log(`Finished writing challenges to database`);
const count = await dailyCodingChallenges.countDocuments();
if (count !== EXPECTED_CHALLENGE_COUNT) {
throw new Error(
`Expected ${EXPECTED_CHALLENGE_COUNT} challenges in the database, but found ${count} documents`
);
}
};
seed()
.then(() => client.close())
.catch(err => handleError(err, client));