diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d3c5d85 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +fcc_progress.json diff --git a/README.md b/README.md index dd4a99b..8ff1174 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,27 @@ +# Greenville Codes Student Progress Tracker + ## Overview -This script takes a newline-delimited list of FreeCodeCamp profile URLs and returns a report on their progress as a newline-delimited list of tab-delimited percentages. +This script takes a newline-delimited list of FreeCodeCamp profile URLs and returns a report on their progress as a newline-delimited list of comma-separated percentages. These can be easily pasted into a Google sheet used for tracking a cohort's progress through the Greenville Codes program. ## Installation -1. [Install Tampermonkey.](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo) -2. Navigate to the Utilities tab in Tampermonkey options. -3. Paste the following address into the input marked "URL" and click "Import" -4. Confirm the installation. - -![Installation](assets/install.gif) +Requires Node v7.6 or higher (testing done with v10.1.0). +Clone the repo and cd into it. +Install the dependencies with `npm install`. ## Usage -_Note: This script sends a request for all these URLs essentially at the same time, so and hasn't been tested for more than ~30 at a time. Too many more and their serers might not appreciate it._ - -1. Copy a spreadsheet column (or newline-separated list) of URLs to be scraped to the clipboard. - - empty cells / lines are okay and will be preserved on paste. -2. Invoke the script via the Tampermonkey menu. -3. A prompt should come up. Paste the addresses into the prompt and submit it. -4. At this point, it will start retrieving results. -5. A notification should appear to let you know the process is complete. When this happens select the top-left destination cell and paste the result. - -## Misc. Notes +Create the FreeCodeCamp curriculum map with `node map_fcc_path.js`. +It will create fcc_path.json or overwrite the existing file. -The main functionality is in `bulk-fcc-progress.js`. +Use `node get_fcc_progress.js` to get a detailed progress report for a single FCC profile. You can specify to track the overall FCC curriculum or the Greenville Codes specific path in the file head. -`fcc-progress.js` scrapes a single profile and must be run directly from that page. +Use `node get_bulk_fcc_progress.js` to get a csv report of a full list of student profiles. By default, these are pulled from fcc_profiles.txt, but this can be modified in the file head. -This was developed and tested using Tampermonkey and Chrome 60. YMMV. +## Troubleshooting +Running `node get_bulk_fcc_progress.js` with an invalid url will return an error. This includes newline characters at the end of the file! I'll work on fixing this. -The lessons are currently hard-coded in the files. This can be pulled dynamically in the future, but they don't seem to change too often. +I ran into a few problems getting this to work on another computer. My errors were related to dependencies for Chrome. I resolved them by following the advice [here](https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md). -Also, it's a web scraper, which is likely to break at some point when they change their website, but that's okay, becuase the next version will probably use an API, +`npm install` loads the dependencies locally, so `ldd chrome | grep not` won't work in the base directory. You'll have to navigate all the way down to the node_modules directory for puppeteer, through the .local-chromium directory until you find the chrome binary, then run the command. This should give you the missing dependencies that need to be installed. \ No newline at end of file diff --git a/assets/install.gif b/assets/install.gif deleted file mode 100644 index 807a4d4..0000000 Binary files a/assets/install.gif and /dev/null differ diff --git a/bulk-fcc-progress.js b/bulk-fcc-progress.js deleted file mode 100644 index c2f25a3..0000000 --- a/bulk-fcc-progress.js +++ /dev/null @@ -1,464 +0,0 @@ -// ==UserScript== -// @name FCC Progress -// @namespace http://tampermonkey.net/ -// @version 0.4 -// @description try to take over the world! -// @author David Cohen -// @updateURL https://raw.githubusercontent.com/dacohenii/fcc-progress/master/bulk-fcc-progress.js -// @match https://docs.google.com/spreadsheets/* -// @grant GM_registerMenuCommand -// @grant GM_setClipboard -// @grant GM_notification -// @grant GM_xmlhttpRequest -// @connect freecodecamp.org -// @connect freecodecamp.com -// ==/UserScript== - - -(function() { - function sanitizeText(text) { - return text.split('').map(char => char.toLowerCase()).filter(char => /[a-z]/.test(char)).join(''); - } - - Set.prototype.intersection = function(setB) { - var intersection = new Set(); - for (var elem of setB) { - if (this.has(elem)) { - intersection.add(elem); - } - } - return intersection; - } - - const sections = [ - [ - "HTML5 and CSS", - [ - "Learn How freeCodeCamp Works", - "Say Hello to HTML Elements", - "Headline with the h2 Element", - "Inform with the Paragraph Element", - "Uncomment HTML", - "Comment out HTML", - "Fill in the Blank with Placeholder Text", - "Delete HTML Elements", - "Change the Color of Text", - "Join a freeCodeCamp Study Group in Your City", - "Become a Supporter", - "Use CSS Selectors to Style Elements", - "Use a CSS Class to Style an Element", - "Style Multiple Elements with a CSS Class", - "Change the Font Size of an Element", - "Set the Font Family of an Element", - "Import a Google Font", - "Specify How Fonts Should Degrade", - "Add Images to your Website", - "Size your Images", - "Add Borders Around your Elements", - "Add Rounded Corners with a Border Radius", - "Make Circular Images with a Border Radius", - "Link to External Pages with Anchor Elements", - "Nest an Anchor Element within a Paragraph", - "Make Dead Links using the Hash Symbol", - "Turn an Image into a Link", - "Create a Bulleted Unordered List", - "Create an Ordered List", - "Create a Text Field", - "Add Placeholder Text to a Text Field", - "Create a Form Element", - "Add a Submit Button to a Form", - "Use HTML5 to Require a Field", - "Create a Set of Radio Buttons", - "Create a Set of Checkboxes", - "Check Radio Buttons and Checkboxes by Default", - "Nest Many Elements within a Single Div Element", - "Give a Background Color to a Div Element", - "Set the ID of an Element", - "Use an ID Attribute to Style an Element", - "Adjusting the Padding of an Element", - "Adjust the Margin of an Element", - "Add a Negative Margin to an Element", - "Add Different Padding to Each Side of an Element", - "Add Different Margins to Each Side of an Element", - "Use Clockwise Notation to Specify the Padding of an Element", - "Use Clockwise Notation to Specify the Margin of an Element", - "Style the HTML Body Element", - "Inherit Styles from the Body Element", - "Prioritize One Style Over Another", - "Override Styles in Subsequent CSS", - "Override Class Declarations by Styling ID Attributes", - "Override Class Declarations with Inline Styles", - "Override All Other Styles by using Important", - "Use Hex Code for Specific Colors", - "Use Hex Code to Mix Colors", - "Use Abbreviated Hex Code", - "Use RGB values to Color Elements", - "Use RGB to Mix Colors" - ] - ], - [ - "Responsive Design with Bootstrap", - [ - "Use Responsive Design with Bootstrap Fluid Containers", - "Make Images Mobile Responsive", - "Center Text with Bootstrap", - "Create a Bootstrap Button", - "Create a Block Element Bootstrap Button", - "Taste the Bootstrap Button Color Rainbow", - "Call out Optional Actions with Button Info", - "Warn your Users of a Dangerous Action", - "Use the Bootstrap Grid to Put Elements Side By Side", - "Ditch Custom CSS for Bootstrap", - "Use Spans for Inline Elements", - "Create a Custom Heading", - "Add Font Awesome Icons to our Buttons", - "Add Font Awesome Icons to all of our Buttons", - "Responsively Style Radio Buttons", - "Responsively Style Checkboxes", - "Style Text Inputs as Form Controls", - "Line up Form Elements Responsively with Bootstrap", - "Create a Bootstrap Headline", - "House our page within a Bootstrap Container Fluid Div", - "Create a Bootstrap Row", - "Split your Bootstrap Row", - "Create Bootstrap Wells", - "Add Elements within your Bootstrap Wells", - "Apply the Default Bootstrap Button Style", - "Create a Class to Target with jQuery Selectors", - "Add ID Attributes to Bootstrap Elements", - "Label Bootstrap Wells", - "Give Each Element a Unique ID", - "Label Bootstrap Buttons", - "Use Comments to Clarify Code" - ] - ], - [ - "jQuery", - [ - "Learn how Script Tags and Document Ready Work", - "Target HTML Elements with Selectors Using jQuery", - "Target Elements by Class Using jQuery", - "Target Elements by ID Using jQuery", - "Delete your jQuery Functions", - "Target the same element with multiple jQuery Selectors", - "Remove Classes from an element with jQuery", - "Change the CSS of an Element Using jQuery", - "Disable an Element Using jQuery", - "Change Text Inside an Element Using jQuery", - "Remove an Element Using jQuery", - "Use appendTo to Move Elements with jQuery", - "Clone an Element Using jQuery", - "Target the Parent of an Element Using jQuery", - "Target the Children of an Element Using jQuery", - "Target a Specific Child of an Element Using jQuery", - "Target Even Numbered Elements Using jQuery", - "Use jQuery to Modify the Entire Page" - ] - ], - [ - "Basic Front End Development Projects", - [ - "Get Set for our Front End Development Projects", - "Build a Tribute Page", - "Build a Personal Portfolio Webpage" - ] - ], - [ - "Basic JavaScript", - [ - "Comment your JavaScript Code", - "Declare JavaScript Variables", - "Storing Values with the Assignment Operator", - "Initializing Variables with the Assignment Operator", - "Understanding Uninitialized Variables", - "Understanding Case Sensitivity in Variables", - "Add Two Numbers with JavaScript", - "Subtract One Number from Another with JavaScript", - "Multiply Two Numbers with JavaScript", - "Divide One Number by Another with JavaScript", - "Increment a Number with JavaScript", - "Decrement a Number with JavaScript", - "Create Decimal Numbers with JavaScript", - "Multiply Two Decimals with JavaScript", - "Divide one Decimal by Another with JavaScript", - "Finding a Remainder in JavaScript", - "Compound Assignment With Augmented Addition", - "Compound Assignment With Augmented Subtraction", - "Compound Assignment With Augmented Multiplication", - "Compound Assignment With Augmented Division", - "Convert Celsius to Fahrenheit", - "Declare String Variables", - "Escaping Literal Quotes in Strings", - "Quoting Strings with Single Quotes", - "Escape Sequences in Strings", - "Concatenating Strings with Plus Operator", - "Concatenating Strings with the Plus Equals Operator", - "Constructing Strings with Variables", - "Appending Variables to Strings", - "Find the Length of a String", - "Use Bracket Notation to Find the First Character in a String", - "Understand String Immutability", - "Use Bracket Notation to Find the Nth Character in a String", - "Use Bracket Notation to Find the Last Character in a String", - "Use Bracket Notation to Find the Nth-to-Last Character in a String", - "Word Blanks", - "Store Multiple Values in one Variable using JavaScript Arrays", - "Nest one Array within Another Array", - "Access Array Data with Indexes", - "Modify Array Data With Indexes", - "Access Multi-Dimensional Arrays With Indexes", - "Manipulate Arrays With push()", - "Manipulate Arrays With pop()", - "Manipulate Arrays With shift()", - "Manipulate Arrays With unshift()", - "Shopping List", - "Write Reusable JavaScript with Functions", - "Passing Values to Functions with Arguments", - "Global Scope and Functions", - "Local Scope and Functions", - "Global vs. Local Scope in Functions", - "Return a Value from a Function with Return", - "Assignment with a Returned Value", - "Stand in Line", - "Understanding Boolean Values", - "Use Conditional Logic with If Statements", - "Comparison with the Equality Operator", - "Comparison with the Strict Equality Operator", - "Comparison with the Inequality Operator", - "Comparison with the Strict Inequality Operator", - "Comparison with the Greater Than Operator", - "Comparison with the Greater Than Or Equal To Operator", - "Comparison with the Less Than Operator", - "Comparison with the Less Than Or Equal To Operator", - "Comparisons with the Logical And Operator", - "Comparisons with the Logical Or Operator", - "Introducing Else Statements", - "Introducing Else If Statements", - "Logical Order in If Else Statements", - "Chaining If Else Statements", - "Golf Code", - "Selecting from many options with Switch Statements", - "Adding a default option in Switch statements", - "Multiple Identical Options in Switch Statements", - "Replacing If Else Chains with Switch", - "Returning Boolean Values from Functions", - "Return Early Pattern for Functions", - "Counting Cards", - "Build JavaScript Objects", - "Accessing Objects Properties with the Dot Operator", - "Accessing Objects Properties with Bracket Notation", - "Accessing Objects Properties with Variables", - "Updating Object Properties", - "Add New Properties to a JavaScript Object", - "Delete Properties from a JavaScript Object", - "Using Objects for Lookups", - "Testing Objects for Properties", - "Manipulating Complex Objects", - "Accessing Nested Objects", - "Accessing Nested Arrays", - "Iterate with JavaScript For Loops", - "Iterate Odd Numbers With a For Loop", - "Count Backwards With a For Loop", - "Iterate Through an Array with a For Loop", - "Nesting For Loops", - "Iterate with JavaScript While Loops", - "Profile Lookup", - "Generate Random Fractions with JavaScript", - "Generate Random Whole Numbers with JavaScript", - "Generate Random Whole Numbers within a Range", - "Sift through Text with Regular Expressions", - "Find Numbers with Regular Expressions", - "Find Whitespace with Regular Expressions", - "Invert Regular Expression Matches with JavaScript" - ] - ], - [ - "Object Oriented and Functional Programming", - [ - "Declare JavaScript Objects as Variables", - "Construct JavaScript Objects with Functions", - "Make Instances of Objects with a Constructor Function", - "Make Unique Objects by Passing Parameters to our Constructor", - "Make Object Properties Private", - "Iterate over Arrays with map", - "Condense arrays with reduce", - "Filter Arrays with filter", - "Sort Arrays with sort", - "Reverse Arrays with reverse", - "Concatenate Arrays with concat", - "Split Strings with split", - "Join Strings with join" - ] - ], - [ - "Basic Algorithm Scripting", - [ - "Get Set for our Algorithm Challenges", - "Reverse a String", - "Factorialize a Number", - "Check for Palindromes", - "Find the Longest Word in a String", - "Title Case a Sentence", - "Return Largest Numbers in Arrays", - "Confirm the Ending", - "Repeat a string repeat a string", - "Truncate a string", - "Chunky Monkey", - "Slasher Flick", - "Mutations", - "Falsy Bouncer", - "Seek and Destroy", - "Where do I belong", - "Caesars Cipher" - ] - ], - [ - "JSON APIs and Ajax", - [ - "Trigger Click Events with jQuery", - "Change Text with Click Events", - "Get JSON with the jQuery getJSON Method", - "Convert JSON Data to HTML", - "Render Images from Data Sources", - "Prefilter JSON", - "Get Geo-location Data" - ] - ], - [ - "Intermediate Front End Development Projects", - [ - "Build a Random Quote Machine", - "Show the Local Weather", - "Build a Wikipedia Viewer", - "Use the Twitch.tv JSON API" - ] - ], - [ - "Intermediate Algorithm Scripting", - [ - "Sum All Numbers in a Range", - "Diff Two Arrays", - "Roman Numeral Converter", - "Wherefore art thou", - "Search and Replace", - "Pig Latin", - "DNA Pairing", - "Missing letters", - "Boo who", - "Sorted Union", - "Convert HTML Entities", - "Spinal Tap Case", - "Sum All Odd Fibonacci Numbers", - "Sum All Primes", - "Smallest Common Multiple", - "Finders Keepers", - "Drop it", - "Steamroller", - "Binary Agents", - "Everything Be True", - "Arguments Optional" - ] - ], - [ - "Advanced Algorithm Scripting", - [ - "Validate US Telephone Numbers", - "Record Collection", - "Symmetric Difference", - "Exact Change", - "Inventory Update", - "No repeats please", - "Make a Person", - "Map the Debris", - "Pairwise" - ] - ], - [ - "Advanced Front End Development Projects", - [ - "Build a JavaScript Calculator", - "Build a Pomodoro Clock", - "Build a Tic Tac Toe Game", - "Build a Simon Game" - ] - ] - ]; - - /** - * take a string and turn it into a DOM node - */ - function parseDOM(str){ - const parser = new DOMParser(); - return parser.parseFromString(str, "text/html"); - } - - /** - * takes a parent DOM node (e.g. document) - * and scrapes the HTML to determine completion of all sections. - * - * returns an array of respective completion percentages for each section - */ - function getStudentProgress(node = document){ - - // total complete over all sections - const arrComplete = Array.from(node.querySelectorAll('body div table > tbody > tr > td:nth-child(1)')).map(x => sanitizeText(x.innerText)); - const setComplete = new Set(arrComplete); - - return sections.map(section => { - const assignments = section[1].map(sanitizeText); - // this is the important part... - const sectionProgress = setComplete.intersection(assignments).size; - - // expressed as a percentage: - return `${Math.round(100 * sectionProgress / assignments.length)}%`; - }); - } - - function init(){ - const strUrls = prompt('Paste all URLs here and click OK'); - if(!strUrls){ return false; } - - const urls = strUrls.split(/[ \n]+/); - - // create a result container array - const result = urls.map(url => ""); - - const responsesExpected = urls.filter(u => u.length > 0).length; - - let responsesReceived = 0; - - // asynchronously fill the container - urls.forEach(function(u, i){ - // skip blank lines - if(u.length){ - GM_xmlhttpRequest({ - "method": "GET", - "url": u, - "onerror": function(response){ - responsesReceived++; - result[i] = 'Error - check the console.'; - console.error(`Bad response from ${url}:`, response); - }, - "onload": function(response) { - responsesReceived++; - result[i] = getStudentProgress(parseDOM(response.responseText)).join('\t'); - - // this is how we find out whether we're done. - if(responsesReceived >= responsesExpected){ - GM_setClipboard(result.join('\n'), { type: 'text', mimetype: 'text/plain'}); - - GM_notification({ - title: "Results Copied.", - text: "Select the top-left destination cell and paste." - }); - } - } - }); - } - }); - - } - - GM_registerMenuCommand("Get Student Progress", init); - -})(); diff --git a/fcc-progress.js b/fcc-progress.js deleted file mode 100644 index 9818ff7..0000000 --- a/fcc-progress.js +++ /dev/null @@ -1,404 +0,0 @@ -// ==UserScript== -// @name Get FCC Progress -// @namespace http://tampermonkey.net/ -// @version 0.1 -// @description try to take over the world! -// @author David Cohen -// @match https://www.freecodecamp.org/* -// @match https://www.freecodecamp.com/* -// @grant GM_registerMenuCommand -// @grant GM_setClipboard -// ==/UserScript== - -(function() { - function sanitizeText(text) { - return text.split('').map(char => char.toLowerCase()).filter(char => /[a-z]/.test(char)).join(''); - } - - Set.prototype.intersection = function(setB) { - var intersection = new Set(); - for (var elem of setB) { - if (this.has(elem)) { - intersection.add(elem); - } - } - return intersection; - } - - const sections = [ - [ - "HTML5 and CSS", - [ - "Learn How freeCodeCamp Works", - "Say Hello to HTML Elements", - "Headline with the h2 Element", - "Inform with the Paragraph Element", - "Uncomment HTML", - "Comment out HTML", - "Fill in the Blank with Placeholder Text", - "Delete HTML Elements", - "Change the Color of Text", - "Join a freeCodeCamp Study Group in Your City", - "Become a Supporter", - "Use CSS Selectors to Style Elements", - "Use a CSS Class to Style an Element", - "Style Multiple Elements with a CSS Class", - "Change the Font Size of an Element", - "Set the Font Family of an Element", - "Import a Google Font", - "Specify How Fonts Should Degrade", - "Add Images to your Website", - "Size your Images", - "Add Borders Around your Elements", - "Add Rounded Corners with a Border Radius", - "Make Circular Images with a Border Radius", - "Link to External Pages with Anchor Elements", - "Nest an Anchor Element within a Paragraph", - "Make Dead Links using the Hash Symbol", - "Turn an Image into a Link", - "Create a Bulleted Unordered List", - "Create an Ordered List", - "Create a Text Field", - "Add Placeholder Text to a Text Field", - "Create a Form Element", - "Add a Submit Button to a Form", - "Use HTML5 to Require a Field", - "Create a Set of Radio Buttons", - "Create a Set of Checkboxes", - "Check Radio Buttons and Checkboxes by Default", - "Nest Many Elements within a Single Div Element", - "Give a Background Color to a Div Element", - "Set the ID of an Element", - "Use an ID Attribute to Style an Element", - "Adjusting the Padding of an Element", - "Adjust the Margin of an Element", - "Add a Negative Margin to an Element", - "Add Different Padding to Each Side of an Element", - "Add Different Margins to Each Side of an Element", - "Use Clockwise Notation to Specify the Padding of an Element", - "Use Clockwise Notation to Specify the Margin of an Element", - "Style the HTML Body Element", - "Inherit Styles from the Body Element", - "Prioritize One Style Over Another", - "Override Styles in Subsequent CSS", - "Override Class Declarations by Styling ID Attributes", - "Override Class Declarations with Inline Styles", - "Override All Other Styles by using Important", - "Use Hex Code for Specific Colors", - "Use Hex Code to Mix Colors", - "Use Abbreviated Hex Code", - "Use RGB values to Color Elements", - "Use RGB to Mix Colors" - ] - ], - [ - "Responsive Design with Bootstrap", - [ - "Use Responsive Design with Bootstrap Fluid Containers", - "Make Images Mobile Responsive", - "Center Text with Bootstrap", - "Create a Bootstrap Button", - "Create a Block Element Bootstrap Button", - "Taste the Bootstrap Button Color Rainbow", - "Call out Optional Actions with Button Info", - "Warn your Users of a Dangerous Action", - "Use the Bootstrap Grid to Put Elements Side By Side", - "Ditch Custom CSS for Bootstrap", - "Use Spans for Inline Elements", - "Create a Custom Heading", - "Add Font Awesome Icons to our Buttons", - "Add Font Awesome Icons to all of our Buttons", - "Responsively Style Radio Buttons", - "Responsively Style Checkboxes", - "Style Text Inputs as Form Controls", - "Line up Form Elements Responsively with Bootstrap", - "Create a Bootstrap Headline", - "House our page within a Bootstrap Container Fluid Div", - "Create a Bootstrap Row", - "Split your Bootstrap Row", - "Create Bootstrap Wells", - "Add Elements within your Bootstrap Wells", - "Apply the Default Bootstrap Button Style", - "Create a Class to Target with jQuery Selectors", - "Add ID Attributes to Bootstrap Elements", - "Label Bootstrap Wells", - "Give Each Element a Unique ID", - "Label Bootstrap Buttons", - "Use Comments to Clarify Code" - ] - ], - [ - "jQuery", - [ - "Learn how Script Tags and Document Ready Work", - "Target HTML Elements with Selectors Using jQuery", - "Target Elements by Class Using jQuery", - "Target Elements by ID Using jQuery", - "Delete your jQuery Functions", - "Target the same element with multiple jQuery Selectors", - "Remove Classes from an element with jQuery", - "Change the CSS of an Element Using jQuery", - "Disable an Element Using jQuery", - "Change Text Inside an Element Using jQuery", - "Remove an Element Using jQuery", - "Use appendTo to Move Elements with jQuery", - "Clone an Element Using jQuery", - "Target the Parent of an Element Using jQuery", - "Target the Children of an Element Using jQuery", - "Target a Specific Child of an Element Using jQuery", - "Target Even Numbered Elements Using jQuery", - "Use jQuery to Modify the Entire Page" - ] - ], - [ - "Basic Front End Development Projects", - [ - "Get Set for our Front End Development Projects", - "Build a Tribute Page", - "Build a Personal Portfolio Webpage" - ] - ], - [ - "Basic JavaScript", - [ - "Comment your JavaScript Code", - "Declare JavaScript Variables", - "Storing Values with the Assignment Operator", - "Initializing Variables with the Assignment Operator", - "Understanding Uninitialized Variables", - "Understanding Case Sensitivity in Variables", - "Add Two Numbers with JavaScript", - "Subtract One Number from Another with JavaScript", - "Multiply Two Numbers with JavaScript", - "Divide One Number by Another with JavaScript", - "Increment a Number with JavaScript", - "Decrement a Number with JavaScript", - "Create Decimal Numbers with JavaScript", - "Multiply Two Decimals with JavaScript", - "Divide one Decimal by Another with JavaScript", - "Finding a Remainder in JavaScript", - "Compound Assignment With Augmented Addition", - "Compound Assignment With Augmented Subtraction", - "Compound Assignment With Augmented Multiplication", - "Compound Assignment With Augmented Division", - "Convert Celsius to Fahrenheit", - "Declare String Variables", - "Escaping Literal Quotes in Strings", - "Quoting Strings with Single Quotes", - "Escape Sequences in Strings", - "Concatenating Strings with Plus Operator", - "Concatenating Strings with the Plus Equals Operator", - "Constructing Strings with Variables", - "Appending Variables to Strings", - "Find the Length of a String", - "Use Bracket Notation to Find the First Character in a String", - "Understand String Immutability", - "Use Bracket Notation to Find the Nth Character in a String", - "Use Bracket Notation to Find the Last Character in a String", - "Use Bracket Notation to Find the Nth-to-Last Character in a String", - "Word Blanks", - "Store Multiple Values in one Variable using JavaScript Arrays", - "Nest one Array within Another Array", - "Access Array Data with Indexes", - "Modify Array Data With Indexes", - "Access Multi-Dimensional Arrays With Indexes", - "Manipulate Arrays With push()", - "Manipulate Arrays With pop()", - "Manipulate Arrays With shift()", - "Manipulate Arrays With unshift()", - "Shopping List", - "Write Reusable JavaScript with Functions", - "Passing Values to Functions with Arguments", - "Global Scope and Functions", - "Local Scope and Functions", - "Global vs. Local Scope in Functions", - "Return a Value from a Function with Return", - "Assignment with a Returned Value", - "Stand in Line", - "Understanding Boolean Values", - "Use Conditional Logic with If Statements", - "Comparison with the Equality Operator", - "Comparison with the Strict Equality Operator", - "Comparison with the Inequality Operator", - "Comparison with the Strict Inequality Operator", - "Comparison with the Greater Than Operator", - "Comparison with the Greater Than Or Equal To Operator", - "Comparison with the Less Than Operator", - "Comparison with the Less Than Or Equal To Operator", - "Comparisons with the Logical And Operator", - "Comparisons with the Logical Or Operator", - "Introducing Else Statements", - "Introducing Else If Statements", - "Logical Order in If Else Statements", - "Chaining If Else Statements", - "Golf Code", - "Selecting from many options with Switch Statements", - "Adding a default option in Switch statements", - "Multiple Identical Options in Switch Statements", - "Replacing If Else Chains with Switch", - "Returning Boolean Values from Functions", - "Return Early Pattern for Functions", - "Counting Cards", - "Build JavaScript Objects", - "Accessing Objects Properties with the Dot Operator", - "Accessing Objects Properties with Bracket Notation", - "Accessing Objects Properties with Variables", - "Updating Object Properties", - "Add New Properties to a JavaScript Object", - "Delete Properties from a JavaScript Object", - "Using Objects for Lookups", - "Testing Objects for Properties", - "Manipulating Complex Objects", - "Accessing Nested Objects", - "Accessing Nested Arrays", - "Iterate with JavaScript For Loops", - "Iterate Odd Numbers With a For Loop", - "Count Backwards With a For Loop", - "Iterate Through an Array with a For Loop", - "Nesting For Loops", - "Iterate with JavaScript While Loops", - "Profile Lookup", - "Generate Random Fractions with JavaScript", - "Generate Random Whole Numbers with JavaScript", - "Generate Random Whole Numbers within a Range", - "Sift through Text with Regular Expressions", - "Find Numbers with Regular Expressions", - "Find Whitespace with Regular Expressions", - "Invert Regular Expression Matches with JavaScript" - ] - ], - [ - "Object Oriented and Functional Programming", - [ - "Declare JavaScript Objects as Variables", - "Construct JavaScript Objects with Functions", - "Make Instances of Objects with a Constructor Function", - "Make Unique Objects by Passing Parameters to our Constructor", - "Make Object Properties Private", - "Iterate over Arrays with map", - "Condense arrays with reduce", - "Filter Arrays with filter", - "Sort Arrays with sort", - "Reverse Arrays with reverse", - "Concatenate Arrays with concat", - "Split Strings with split", - "Join Strings with join" - ] - ], - [ - "Basic Algorithm Scripting", - [ - "Get Set for our Algorithm Challenges", - "Reverse a String", - "Factorialize a Number", - "Check for Palindromes", - "Find the Longest Word in a String", - "Title Case a Sentence", - "Return Largest Numbers in Arrays", - "Confirm the Ending", - "Repeat a string repeat a string", - "Truncate a string", - "Chunky Monkey", - "Slasher Flick", - "Mutations", - "Falsy Bouncer", - "Seek and Destroy", - "Where do I belong", - "Caesars Cipher" - ] - ], - [ - "JSON APIs and Ajax", - [ - "Trigger Click Events with jQuery", - "Change Text with Click Events", - "Get JSON with the jQuery getJSON Method", - "Convert JSON Data to HTML", - "Render Images from Data Sources", - "Prefilter JSON", - "Get Geo-location Data" - ] - ], - [ - "Intermediate Front End Development Projects", - [ - "Build a Random Quote Machine", - "Show the Local Weather", - "Build a Wikipedia Viewer", - "Use the Twitch.tv JSON API" - ] - ], - [ - "Intermediate Algorithm Scripting", - [ - "Sum All Numbers in a Range", - "Diff Two Arrays", - "Roman Numeral Converter", - "Wherefore art thou", - "Search and Replace", - "Pig Latin", - "DNA Pairing", - "Missing letters", - "Boo who", - "Sorted Union", - "Convert HTML Entities", - "Spinal Tap Case", - "Sum All Odd Fibonacci Numbers", - "Sum All Primes", - "Smallest Common Multiple", - "Finders Keepers", - "Drop it", - "Steamroller", - "Binary Agents", - "Everything Be True", - "Arguments Optional" - ] - ], - [ - "Advanced Algorithm Scripting", - [ - "Validate US Telephone Numbers", - "Record Collection", - "Symmetric Difference", - "Exact Change", - "Inventory Update", - "No repeats please", - "Make a Person", - "Map the Debris", - "Pairwise" - ] - ], - [ - "Advanced Front End Development Projects", - [ - "Build a JavaScript Calculator", - "Build a Pomodoro Clock", - "Build a Tic Tac Toe Game", - "Build a Simon Game" - ] - ] - ]; - - - // total complete over all sections - const arrComplete = Array.from(document.querySelectorAll('body > div:nth-child(10) > div.col-md-12 > div > table > tbody > tr > td:nth-child(1)')).map(x => sanitizeText(x.innerText)); - const setComplete = new Set(arrComplete); - - const allSectionsProgress = sections.map(section => { - const assignments = section[1].map(sanitizeText); - // this is the important part... - const sectionProgress = setComplete.intersection(assignments).size; - - // expressed as a percentage: - return `${Math.round(100 * sectionProgress / assignments.length)}%`; - }); - - function copyProgressRow(){ - // join w/ tab to copy text suitable for pasting into a spreadsheet - const row = allSectionsProgress.join('\t'); - GM_setClipboard(row, { type: 'text', mimetype: 'text/plain'}); - } - - GM_registerMenuCommand("Copy Student Progress", copyProgressRow); - -})(); \ No newline at end of file diff --git a/fcc_path.json b/fcc_path.json new file mode 100644 index 0000000..14e2656 --- /dev/null +++ b/fcc_path.json @@ -0,0 +1,1507 @@ +{ + "Responsive Web Design Certification (300 hours)": { + "Basic HTML and HTML5": [ + "Say Hello to HTML Elements", + "Headline with the h2 Element", + "Inform with the Paragraph Element", + "Fill in the Blank with Placeholder Text", + "Uncomment HTML", + "Comment out HTML", + "Delete HTML Elements", + "Introduction to HTML5 Elements", + "Add Images to Your Website", + "Link to External Pages with Anchor Elements", + "Link to Internal Sections of a Page with Anchor Elements", + "Nest an Anchor Element within a Paragraph", + "Make Dead Links Using the Hash Symbol", + "Turn an Image into a Link", + "Create a Bulleted Unordered List", + "Create an Ordered List", + "Create a Text Field", + "Add Placeholder Text to a Text Field", + "Create a Form Element", + "Add a Submit Button to a Form", + "Use HTML5 to Require a Field", + "Create a Set of Radio Buttons", + "Create a Set of Checkboxes", + "Check Radio Buttons and Checkboxes by Default", + "Nest Many Elements within a Single div Element", + "Declare the Doctype of an HTML Document", + "Define the Head and Body of an HTML Document" + ], + "Basic CSS": [ + "Change the Color of Text", + "Use CSS Selectors to Style Elements", + "Use a CSS Class to Style an Element", + "Style Multiple Elements with a CSS Class", + "Change the Font Size of an Element", + "Set the Font Family of an Element", + "Import a Google Font", + "Specify How Fonts Should Degrade", + "Size Your Images", + "Add Borders Around Your Elements", + "Add Rounded Corners with border-radius", + "Make Circular Images with a border-radius", + "Give a Background Color to a div Element", + "Set the id of an Element", + "Use an id Attribute to Style an Element", + "Adjust the Padding of an Element", + "Adjust the Margin of an Element", + "Add a Negative Margin to an Element", + "Add Different Padding to Each Side of an Element", + "Add Different Margins to Each Side of an Element", + "Use Clockwise Notation to Specify the Padding of an Element", + "Use Clockwise Notation to Specify the Margin of an Element", + "Use Attribute Selectors to Style Elements", + "Understand Absolute versus Relative Units", + "Style the HTML Body Element", + "Inherit Styles from the Body Element", + "Prioritize One Style Over Another", + "Override Styles in Subsequent CSS", + "Override Class Declarations by Styling ID Attributes", + "Override Class Declarations with Inline Styles", + "Override All Other Styles by using Important", + "Use Hex Code for Specific Colors", + "Use Hex Code to Mix Colors", + "Use Abbreviated Hex Code", + "Use RGB values to Color Elements", + "Use RGB to Mix Colors", + "Use CSS Variables to change several elements at once", + "Create a custom CSS Variable", + "Use a custom CSS Variable", + "Attach a Fallback value to a CSS Variable", + "Improve Compatibility with Browser Fallbacks", + "Cascading CSS variables", + "Change a variable for a specific area", + "Use a media query to change a variable" + ], + "Applied Visual Design": [ + "Create Visual Balance Using the text-align Property", + "Adjust the Width of an Element Using the width Property", + "Adjust the Height of an Element Using the height Property", + "Use the strong Tag to Make Text Bold", + "Use the u Tag to Underline Text", + "Use the em Tag to Italicize Text", + "Use the s Tag to Strikethrough Text", + "Create a Horizontal Line Using the hr Element", + "Adjust the background-color Property of Text", + "Adjust the Size of a Header Versus a Paragraph Tag", + "Add a box-shadow to a Card-like Element", + "Decrease the Opacity of an Element", + "Use the text-transform Property to Make Text Uppercase", + "Set the font-size for Multiple Heading Elements", + "Set the font-weight for Multiple Heading Elements", + "Set the font-size of Paragraph Text", + "Set the line-height of Paragraphs", + "Adjust the Hover State of an Anchor Tag", + "Change an Element's Relative Position", + "Move a Relatively Positioned Element with CSS Offsets", + "Lock an Element to its Parent with Absolute Positioning", + "Lock an Element to the Browser Window with Fixed Positioning", + "Push Elements Left or Right with the float Property", + "Change the Position of Overlapping Elements with the z-index Property", + "Center an Element Horizontally Using the margin Property", + "Learn about Complementary Colors", + "Learn about Tertiary Colors", + "Adjust the Color of Various Elements to Complementary Colors", + "Adjust the Hue of a Color", + "Adjust the Tone of a Color", + "Create a Gradual CSS Linear Gradient", + "Use a CSS Linear Gradient to Create a Striped Element", + "Create Texture by Adding a Subtle Pattern as a Background Image", + "Use the CSS Transform scale Property to Change the Size of an Element", + "Use the CSS Transform scale Property to Scale an Element on Hover", + "Use the CSS Transform Property skewX to Skew an Element Along the X-Axis", + "Use the CSS Transform Property skewY to Skew an Element Along the Y-Axis", + "Create a Graphic Using CSS", + "Create a More Complex Shape Using CSS and HTML", + "Learn How the CSS @keyframes and animation Properties Work", + "Use CSS Animation to Change the Hover State of a Button", + "Modify Fill Mode of an Animation", + "Create Movement Using CSS Animation", + "Create Visual Direction by Fading an Element from Left to Right", + "Animate Elements Continually Using an Infinite Animation Count", + "Make a CSS Heartbeat using an Infinite Animation Count", + "Animate Elements at Variable Rates", + "Animate Multiple Elements at Variable Rates", + "Change Animation Timing with Keywords", + "Learn How Bezier Curves Work", + "Use a Bezier Curve to Move a Graphic", + "Make Motion More Natural Using a Bezier Curve" + ], + "Applied Accessibility": [ + "Add a Text Alternative to Images for Visually Impaired Accessibility", + "Know When Alt Text Should be Left Blank", + "Use Headings to Show Hierarchical Relationships of Content", + "Jump Straight to the Content Using the main Element", + "Wrap Content in the article Element", + "Make Screen Reader Navigation Easier with the header Landmark", + "Make Screen Reader Navigation Easier with the nav Landmark", + "Make Screen Reader Navigation Easier with the footer Landmark", + "Improve Accessibility of Audio Content with the audio Element", + "Improve Chart Accessibility with the figure Element", + "Improve Form Field Accessibility with the label Element", + "Wrap Radio Buttons in a fieldset Element for Better Accessibility", + "Add an Accessible Date Picker", + "Standardize Times with the HTML5 datetime Attribute", + "Make Elements Only Visible to a Screen Reader by Using Custom CSS", + "Improve Readability with High Contrast Text", + "Avoid Colorblindness Issues by Using Sufficient Contrast", + "Avoid Colorblindness Issues by Carefully Choosing Colors that Convey Information", + "Give Links Meaning by Using Descriptive Link Text", + "Make Links Navigatable with HTML Access Keys", + "Use tabindex to Add Keyboard Focus to an Element", + "Use tabindex to Specify the Order of Keyboard Focus for Several Elements" + ], + "Responsive Web Design Principles": [ + "Create a Media Query", + "Make an Image Responsive", + "Use a Retina Image for Higher Resolution Displays", + "Make Typography Responsive" + ], + "CSS Flexbox": [ + "Use display: flex to Position Two Boxes", + "Add Flex Superpowers to the Tweet Embed", + "Use the flex-direction Property to Make a Row", + "Apply the flex-direction Property to Create Rows in the Tweet Embed", + "Use the flex-direction Property to Make a Column", + "Apply the flex-direction Property to Create a Column in the Tweet Embed", + "Align Elements Using the justify-content Property", + "Use the justify-content Property in the Tweet Embed", + "Align Elements Using the align-items Property", + "Use the align-items Property in the Tweet Embed", + "Use the flex-wrap Property to Wrap a Row or Column", + "Use the flex-shrink Property to Shrink Items", + "Use the flex-grow Property to Expand Items", + "Use the flex-basis Property to Set the Initial Size of an Item", + "Use the flex Shorthand Property", + "Use the order Property to Rearrange Items", + "Use the align-self Property" + ], + "CSS Grid": [ + "Create Your First CSS Grid", + "Add Columns with grid-template-columns", + "Add Rows with grid-template-rows", + "Use CSS Grid units to Change the Size of Columns and Rows", + "Create a Column Gap Using grid-column-gap", + "Create a Row Gap using grid-row-gap", + "Add Gaps Faster with grid-gap", + "Use grid-column to Control Spacing", + "Use grid-row to Control Spacing", + "Align an Item Horizontally using justify-self", + "Align an Item Vertically using align-self", + "Align All Items Horizontally using justify-items", + "Align All Items Vertically using align-items", + "Divide the Grid Into an Area Template", + "Place Items in Grid Areas Using the grid-area Property", + "Use grid-area Without Creating an Areas Template", + "Reduce Repetition Using the repeat Function", + "Limit Item Size Using the minmax Function", + "Create Flexible Layouts Using auto-fill", + "Create Flexible Layouts Using auto-fit", + "Use Media Queries to Create Responsive Layouts", + "Create Grids within Grids" + ], + "Responsive Web Design Projects": [ + "Build a Tribute Page", + "Build a Survey Form", + "Build a Product Landing Page", + "Build a Technical Documentation Page", + "Build a Personal Portfolio Webpage" + ] + }, + "Javascript Algorithms And Data Structures Certification (300 hours)": { + "Basic JavaScript": [ + "Comment Your JavaScript Code", + "Declare JavaScript Variables", + "Storing Values with the Assignment Operator", + "Initializing Variables with the Assignment Operator", + "Understanding Uninitialized Variables", + "Understanding Case Sensitivity in Variables", + "Add Two Numbers with JavaScript", + "Subtract One Number from Another with JavaScript", + "Multiply Two Numbers with JavaScript", + "Divide One Number by Another with JavaScript", + "Increment a Number with JavaScript", + "Decrement a Number with JavaScript", + "Create Decimal Numbers with JavaScript", + "Multiply Two Decimals with JavaScript", + "Divide One Decimal by Another with JavaScript", + "Finding a Remainder in JavaScript", + "Compound Assignment With Augmented Addition", + "Compound Assignment With Augmented Subtraction", + "Compound Assignment With Augmented Multiplication", + "Compound Assignment With Augmented Division", + "Declare String Variables", + "Escaping Literal Quotes in Strings", + "Quoting Strings with Single Quotes", + "Escape Sequences in Strings", + "Concatenating Strings with Plus Operator", + "Concatenating Strings with the Plus Equals Operator", + "Constructing Strings with Variables", + "Appending Variables to Strings", + "Find the Length of a String", + "Use Bracket Notation to Find the First Character in a String", + "Understand String Immutability", + "Use Bracket Notation to Find the Nth Character in a String", + "Use Bracket Notation to Find the Last Character in a String", + "Use Bracket Notation to Find the Nth-to-Last Character in a String", + "Word Blanks", + "Store Multiple Values in one Variable using JavaScript Arrays", + "Nest one Array within Another Array", + "Access Array Data with Indexes", + "Modify Array Data With Indexes", + "Access Multi-Dimensional Arrays With Indexes", + "Manipulate Arrays With push()", + "Manipulate Arrays With pop()", + "Manipulate Arrays With shift()", + "Manipulate Arrays With unshift()", + "Shopping List", + "Write Reusable JavaScript with Functions", + "Passing Values to Functions with Arguments", + "Global Scope and Functions", + "Local Scope and Functions", + "Global vs. Local Scope in Functions", + "Return a Value from a Function with Return", + "Understanding Undefined Value returned from a Function", + "Assignment with a Returned Value", + "Stand in Line", + "Understanding Boolean Values", + "Use Conditional Logic with If Statements", + "Comparison with the Equality Operator", + "Comparison with the Strict Equality Operator", + "Practice comparing different values", + "Comparison with the Inequality Operator", + "Comparison with the Strict Inequality Operator", + "Comparison with the Greater Than Operator", + "Comparison with the Greater Than Or Equal To Operator", + "Comparison with the Less Than Operator", + "Comparison with the Less Than Or Equal To Operator", + "Comparisons with the Logical And Operator", + "Comparisons with the Logical Or Operator", + "Introducing Else Statements", + "Introducing Else If Statements", + "Logical Order in If Else Statements", + "Chaining If Else Statements", + "Golf Code", + "Selecting from Many Options with Switch Statements", + "Adding a Default Option in Switch Statements", + "Multiple Identical Options in Switch Statements", + "Replacing If Else Chains with Switch", + "Returning Boolean Values from Functions", + "Return Early Pattern for Functions", + "Counting Cards", + "Build JavaScript Objects", + "Accessing Object Properties with Dot Notation", + "Accessing Object Properties with Bracket Notation", + "Accessing Object Properties with Variables", + "Updating Object Properties", + "Add New Properties to a JavaScript Object", + "Delete Properties from a JavaScript Object", + "Using Objects for Lookups", + "Testing Objects for Properties", + "Manipulating Complex Objects", + "Accessing Nested Objects", + "Accessing Nested Arrays", + "Record Collection", + "Iterate with JavaScript While Loops", + "Iterate with JavaScript For Loops", + "Iterate Odd Numbers With a For Loop", + "Count Backwards With a For Loop", + "Iterate Through an Array with a For Loop", + "Nesting For Loops", + "Iterate with JavaScript Do...While Loops", + "Profile Lookup", + "Generate Random Fractions with JavaScript", + "Generate Random Whole Numbers with JavaScript", + "Generate Random Whole Numbers within a Range", + "Use the parseInt Function", + "Use the parseInt Function with a Radix", + "Use the Conditional (Ternary) Operator", + "Use Multiple Conditional (Ternary) Operators" + ], + "ES6": [ + "Explore Differences Between the var and let Keywords", + "Compare Scopes of the var and let Keywords", + "Declare a Read-Only Variable with the const Keyword", + "Mutate an Array Declared with const", + "Prevent Object Mutation", + "Use Arrow Functions to Write Concise Anonymous Functions", + "Write Arrow Functions with Parameters", + "Write Higher Order Arrow Functions", + "Set Default Parameters for Your Functions", + "Use the Rest Operator with Function Parameters", + "Use the Spread Operator to Evaluate Arrays In-Place", + "Use Destructuring Assignment to Assign Variables from Objects", + "Use Destructuring Assignment to Assign Variables from Nested Objects", + "Use Destructuring Assignment to Assign Variables from Arrays", + "Use Destructuring Assignment with the Rest Operator to Reassign Array Elements", + "Use Destructuring Assignment to Pass an Object as a Function's Parameters", + "Create Strings using Template Literals", + "Write Concise Object Literal Declarations Using Simple Fields", + "Write Concise Declarative Functions with ES6", + "Use class Syntax to Define a Constructor Function", + "Use getters and setters to Control Access to an Object", + "Understand the Differences Between import and require", + "Use export to Reuse a Code Block", + "Use * to Import Everything from a File", + "Create an Export Fallback with export default", + "Import a Default Export" + ], + "Regular Expressions": [ + "Using the Test Method", + "Match Literal Strings", + "Match a Literal String with Different Possibilities", + "Ignore Case While Matching", + "Extract Matches", + "Find More Than the First Match", + "Match Anything with Wildcard Period", + "Match Single Character with Multiple Possibilities", + "Match Letters of the Alphabet", + "Match Numbers and Letters of the Alphabet", + "Match Single Characters Not Specified", + "Match Characters that Occur One or More Times", + "Match Characters that Occur Zero or More Times", + "Find Characters with Lazy Matching", + "Find One or More Criminals in a Hunt", + "Match Beginning String Patterns", + "Match Ending String Patterns", + "Match All Letters and Numbers", + "Match Everything But Letters and Numbers", + "Match All Numbers", + "Match All Non-Numbers", + "Restrict Possible Usernames", + "Match Whitespace", + "Match Non-Whitespace Characters", + "Specify Upper and Lower Number of Matches", + "Specify Only the Lower Number of Matches", + "Specify Exact Number of Matches", + "Check for All or None", + "Positive and Negative Lookahead", + "Reuse Patterns Using Capture Groups", + "Use Capture Groups to Search and Replace", + "Remove Whitespace from Start and End" + ], + "Debugging": [ + "Use the JavaScript Console to Check the Value of a Variable", + "Understanding the Differences between the freeCodeCamp and Browser Console", + "Use typeof to Check the Type of a Variable", + "Catch Misspelled Variable and Function Names", + "Catch Unclosed Parentheses, Brackets, Braces and Quotes", + "Catch Mixed Usage of Single and Double Quotes", + "Catch Use of Assignment Operator Instead of Equality Operator", + "Catch Missing Open and Closing Parenthesis After a Function Call", + "Catch Arguments Passed in the Wrong Order When Calling a Function", + "Catch Off By One Errors When Using Indexing", + "Use Caution When Reinitializing Variables Inside a Loop", + "Prevent Infinite Loops with a Valid Terminal Condition" + ], + "Basic Data Structures": [ + "Use an Array to Store a Collection of Data", + "Access an Array's Contents Using Bracket Notation", + "Add Items to an Array with push() and unshift()", + "Remove Items from an Array with pop() and shift()", + "Remove Items Using splice()", + "Add Items Using splice()", + "Copy Array Items Using slice()", + "Copy an Array with the Spread Operator", + "Combine Arrays with the Spread Operator", + "Check For The Presence of an Element With indexOf()", + "Iterate Through All an Array's Items Using For Loops", + "Create complex multi-dimensional arrays", + "Add Key-Value Pairs to JavaScript Objects", + "Modify an Object Nested Within an Object", + "Access Property Names with Bracket Notation", + "Use the delete Keyword to Remove Object Properties", + "Check if an Object has a Property", + " Iterate Through the Keys of an Object with a for...in Statement", + "Generate an Array of All Object Keys with Object.keys()", + "Modify an Array Stored in an Object" + ], + "Basic Algorithm Scripting": [ + "Convert Celsius to Fahrenheit", + "Reverse a String", + "Factorialize a Number", + "Find the Longest Word in a String", + "Return Largest Numbers in Arrays", + "Confirm the Ending", + "Repeat a String Repeat a String", + "Truncate a String", + "Finders Keepers", + "Boo who", + "Title Case a Sentence", + "Slice and Splice", + "Falsy Bouncer", + "Where do I Belong", + "Mutations", + "Chunky Monkey" + ], + "Object Oriented Programming": [ + "Create a Basic JavaScript Object", + "Use Dot Notation to Access the Properties of an Object", + "Create a Method on an Object", + "Make Code More Reusable with the this Keyword", + "Define a Constructor Function", + "Use a Constructor to Create Objects", + "Extend Constructors to Receive Arguments", + "Verify an Object's Constructor with instanceof", + "Understand Own Properties", + "Use Prototype Properties to Reduce Duplicate Code", + "Iterate Over All Properties", + "Understand the Constructor Property", + "Change the Prototype to a New Object", + "Remember to Set the Constructor Property when Changing the Prototype", + "Understand Where an Object’s Prototype Comes From", + "Understand the Prototype Chain", + "Use Inheritance So You Don't Repeat Yourself", + "Inherit Behaviors from a Supertype", + "Set the Child's Prototype to an Instance of the Parent", + "Reset an Inherited Constructor Property", + "Add Methods After Inheritance", + "Override Inherited Methods", + "Use a Mixin to Add Common Behavior Between Unrelated Objects", + "Use Closure to Protect Properties Within an Object from Being Modified Externally", + "Understand the Immediately Invoked Function Expression (IIFE)", + "Use an IIFE to Create a Module" + ], + "Functional Programming": [ + "Learn About Functional Programming", + "Understand Functional Programming Terminology", + "Understand the Hazards of Using Imperative Code", + "Avoid Mutations and Side Effects Using Functional Programming", + "Pass Arguments to Avoid External Dependence in a Function", + "Refactor Global Variables Out of Functions", + "Use the map Method to Extract Data from an Array", + "Implement map on a Prototype", + "Use the filter Method to Extract Data from an Array", + "Implement the filter Method on a Prototype", + "Return Part of an Array Using the slice Method", + "Remove Elements from an Array Using slice Instead of splice", + "Combine Two Arrays Using the concat Method", + "Add Elements to the End of an Array Using concat Instead of push", + "Use the reduce Method to Analyze Data", + "Sort an Array Alphabetically using the sort Method", + "Return a Sorted Array Without Changing the Original Array", + "Split a String into an Array Using the split Method", + "Combine an Array into a String Using the join Method", + "Apply Functional Programming to Convert Strings to URL Slugs", + "Use the every Method to Check that Every Element in an Array Meets a Criteria", + "Use the some Method to Check that Any Elements in an Array Meet a Criteria", + "Introduction to Currying and Partial Application" + ], + "Intermediate Algorithm Scripting": [ + "Sum All Numbers in a Range", + "Diff Two Arrays", + "Seek and Destroy", + "Wherefore art thou", + "Spinal Tap Case", + "Pig Latin", + "Search and Replace", + "DNA Pairing", + "Missing letters", + "Sorted Union", + "Convert HTML Entities", + "Sum All Odd Fibonacci Numbers", + "Sum All Primes", + "Smallest Common Multiple", + "Drop it", + "Steamroller", + "Binary Agents", + "Everything Be True", + "Arguments Optional", + "Make a Person", + "Map the Debris" + ], + "JavaScript Algorithms and Data Structures Projects": [ + "Palindrome Checker", + "Roman Numeral Converter", + "Caesars Cipher", + "Telephone Number Validator", + "Cash Register" + ] + }, + "Front End Libraries Certification (300 hours)": { + "Bootstrap": [ + "Use Responsive Design with Bootstrap Fluid Containers", + "Make Images Mobile Responsive", + "Center Text with Bootstrap", + "Create a Bootstrap Button", + "Create a Block Element Bootstrap Button", + "Taste the Bootstrap Button Color Rainbow", + "Call out Optional Actions with btn-info", + "Warn Your Users of a Dangerous Action with btn-danger", + "Use the Bootstrap Grid to Put Elements Side By Side", + "Ditch Custom CSS for Bootstrap", + "Use a span to Target Inline Elements", + "Create a Custom Heading", + "Add Font Awesome Icons to our Buttons", + "Add Font Awesome Icons to all of our Buttons", + "Responsively Style Radio Buttons", + "Responsively Style Checkboxes", + "Style Text Inputs as Form Controls", + "Line up Form Elements Responsively with Bootstrap", + "Create a Bootstrap Headline", + "House our page within a Bootstrap container-fluid div", + "Create a Bootstrap Row", + "Split Your Bootstrap Row", + "Create Bootstrap Wells", + "Add Elements within Your Bootstrap Wells", + "Apply the Default Bootstrap Button Style", + "Create a Class to Target with jQuery Selectors", + "Add id Attributes to Bootstrap Elements", + "Label Bootstrap Wells", + "Give Each Element a Unique id", + "Label Bootstrap Buttons", + "Use Comments to Clarify Code" + ], + "jQuery": [ + "Learn How Script Tags and Document Ready Work", + "Target HTML Elements with Selectors Using jQuery", + "Target Elements by Class Using jQuery", + "Target Elements by id Using jQuery", + "Delete Your jQuery Functions", + "Target the Same Element with Multiple jQuery Selectors", + "Remove Classes from an Element with jQuery", + "Change the CSS of an Element Using jQuery", + "Disable an Element Using jQuery", + "Change Text Inside an Element Using jQuery", + "Remove an Element Using jQuery", + "Use appendTo to Move Elements with jQuery", + "Clone an Element Using jQuery", + "Target the Parent of an Element Using jQuery", + "Target the Children of an Element Using jQuery", + "Target a Specific Child of an Element Using jQuery", + "Target Even Elements Using jQuery", + "Use jQuery to Modify the Entire Page" + ], + "Sass": [ + "Store Data with Sass Variables", + "Nest CSS with Sass", + "Create Reusable CSS with Mixins", + "Use @if and @else to Add Logic To Your Styles", + "Use @for to Create a Sass Loop", + "Use @each to Map Over Items in a List", + "Apply a Style Until a Condition is Met with @while", + "Split Your Styles into Smaller Chunks with Partials", + "Extend One Set of CSS Styles to Another Element" + ], + "React": [ + "Create a Simple JSX Element", + "Create a Complex JSX Element", + "Add Comments in JSX", + "Render HTML Elements to the DOM", + "Define an HTML Class in JSX", + "Learn About Self-Closing JSX Tags", + "Create a Stateless Functional Component", + "Create a React Component", + "Create a Component with Composition", + "Use React to Render Nested Components", + "Compose React Components", + "Render a Class Component to the DOM", + "Write a React Component from Scratch", + "Pass Props to a Stateless Functional Component", + "Pass an Array as Props", + "Use Default Props", + "Override Default Props", + "Use PropTypes to Define the Props You Expect", + "Access Props Using this.props", + "Review Using Props with Stateless Functional Components", + "Create a Stateful Component", + "Render State in the User Interface", + "Render State in the User Interface Another Way", + "Set State with this.setState", + "Bind 'this' to a Class Method", + "Use State to Toggle an Element", + "Write a Simple Counter", + "Create a Controlled Input", + "Create a Controlled Form", + "Pass State as Props to Child Components", + "Pass a Callback as Props", + "Use the Lifecycle Method componentWillMount", + "Use the Lifecycle Method componentDidMount", + "Add Event Listeners", + "Manage Updates with Lifecycle Methods", + "Optimize Re-Renders with shouldComponentUpdate", + "Introducing Inline Styles", + "Add Inline Styles in React", + "Use Advanced JavaScript in React Render Method", + "Render with an If/Else Condition", + "Use && for a More Concise Conditional", + "Use a Ternary Expression for Conditional Rendering", + "Render Conditionally from Props", + "Change Inline CSS Conditionally Based on Component State", + "Use Array.map() to Dynamically Render Elements", + "Give Sibling Elements a Unique Key Attribute", + "Use Array.filter() to Dynamically Filter an Array", + "Render React on the Server with renderToString" + ], + "Redux": [ + "Create a Redux Store", + "Get State from the Redux Store", + "Define a Redux Action", + "Define an Action Creator", + "Dispatch an Action Event", + "Handle an Action in the Store", + "Use a Switch Statement to Handle Multiple Actions", + "Use const for Action Types", + "Register a Store Listener", + "Combine Multiple Reducers", + "Send Action Data to the Store", + "Use Middleware to Handle Asynchronous Actions", + "Write a Counter with Redux", + "Never Mutate State", + "Use the Spread Operator on Arrays", + "Remove an Item from an Array", + "Copy an Object with Object.assign" + ], + "React and Redux": [ + "Getting Started with React Redux", + "Manage State Locally First", + "Extract State Logic to Redux", + "Use Provider to Connect Redux to React", + "Map State to Props", + "Map Dispatch to Props", + "Connect Redux to React", + "Connect Redux to the Messages App", + "Extract Local State into Redux", + "Moving Forward From Here" + ], + "Front End Libraries Projects": [ + "Build a Random Quote Machine", + "Build a Markdown Previewer", + "Build a Drum Machine", + "Build a JavaScript Calculator", + "Build a Pomodoro Clock" + ] + }, + "Data Visualization Certification (300 hours)": { + "Data Visualization with D3": [ + "Add Document Elements with D3", + "Select a Group of Elements with D3", + "Work with Data in D3", + "Work with Dynamic Data in D3", + "Add Inline Styling to Elements", + "Change Styles Based on Data", + "Add Classes with D3", + "Update the Height of an Element Dynamically", + "Change the Presentation of a Bar Chart", + "Learn About SVG in D3", + "Display Shapes with SVG", + "Create a Bar for Each Data Point in the Set", + "Dynamically Set the Coordinates for Each Bar", + "Dynamically Change the Height of Each Bar", + "Invert SVG Elements", + "Change the Color of an SVG Element", + "Add Labels to D3 Elements", + "Style D3 Labels", + "Add a Hover Effect to a D3 Element", + "Add a Tooltip to a D3 Element", + "Create a Scatterplot with SVG Circles", + "Add Attributes to the Circle Elements", + "Add Labels to Scatter Plot Circles", + "Create a Linear Scale with D3", + "Set a Domain and a Range on a Scale", + "Use the d3.max and d3.min Functions to Find Minimum and Maximum Values in a Dataset", + "Use Dynamic Scales", + "Use a Pre-Defined Scale to Place Elements", + "Add Axes to a Visualization" + ], + "JSON APIs and Ajax": [ + "Handle Click Events with JavaScript using the onclick property", + "Change Text with click Events", + "Get JSON with the JavaScript XMLHttpRequest Method", + "Access the JSON Data from an API", + "Convert JSON Data to HTML", + "Render Images from Data Sources", + "Pre-filter JSON to Get the Data You Need", + "Get Geolocation Data to Find A User's GPS Coordinates", + "Post Data with the JavaScript XMLHttpRequest Method" + ], + "Data Visualization Projects": [ + "Visualize Data with a Bar Chart", + "Visualize Data with a Scatterplot Graph", + "Visualize Data with a Heat Map", + "Visualize Data with a Choropleth Map", + "Visualize Data with a Treemap Diagram" + ] + }, + "Apis And Microservices Certification (300 hours)": { + "Managing Packages with Npm": [ + "How to Use package.json, the Core of Any Node.js Project or npm Package", + "Add a Description to Your package.json", + "Add Keywords to Your package.json", + "Add a License to Your package.json", + "Add a Version to Your package.json", + "Expand Your Project with External Packages from npm", + "Manage npm Dependencies By Understanding Semantic Versioning", + "Use the Tilde-Character to Always Use the Latest Patch Version of a Dependency", + "Use the Caret-Character to Use the Latest Minor Version of a Dependency", + "Remove a Package from Your Dependencies" + ], + "Basic Node and Express": [ + "Meet the Node console", + "Start a Working Express Server", + "Serve an HTML File", + "Serve Static Assets", + "Serve JSON on a Specific Route", + "Use the .env File", + "Implement a Root-Level Request Logger Middleware", + "Chain Middleware to Create a Time Server", + "Get Route Parameter Input from the Client", + "Get Query Parameter Input from the Client", + "Use body-parser to Parse POST Requests", + "Get Data from POST Requests" + ], + "MongoDB and Mongoose": [ + "Install and Set Up Mongoose", + "Create a Model", + "Create and Save a Record of a Model", + "Create Many Records with model.create()", + "Use model.find() to Search Your Database", + "Use model.findOne() to Return a Single Matching Document from Your Database", + "Use model.findById() to Search Your Database By _id", + "Perform Classic Updates by Running Find, Edit, then Save", + "Perform New Updates on a Document Using model.findOneAndUpdate()", + "Delete One Document Using model.findByIdAndRemove", + "Delete Many Documents with model.remove()", + "Chain Search Query Helpers to Narrow Search Results" + ], + "Apis and Microservices Projects": [ + "Timestamp Microservice", + "Request Header Parser Microservice", + "URL Shortener Microservice", + "Exercise Tracker", + "File Metadata Microservice" + ] + }, + "Information Security And Quality Assurance Certification (300 hours)": { + "Information Security with HelmetJS": [ + "Install and Require Helmet", + "Hide Potentially Dangerous Information Using helmet.hidePoweredBy()", + "Mitigate the Risk of Clickjacking with helmet.frameguard()", + "Mitigate the Risk of Cross Site Scripting (XSS) Attacks with helmet.xssFilter()", + "Avoid Inferring the Response MIME Type with helmet.noSniff()", + "Prevent IE from Opening Untrusted HTML with helmet.ieNoOpen()", + "Ask Browsers to Access Your Site via HTTPS Only with helmet.hsts()", + "Disable DNS Prefetching with helmet.dnsPrefetchControl()", + "Disable Client-Side Caching with helmet.noCache()", + "Set a Content Security Policy with helmet.contentSecurityPolicy()", + "Configure Helmet Using the ‘parent’ helmet() Middleware", + "Understand BCrypt Hashes", + "Hash and Compare Passwords Asynchronously", + "Hash and Compare Passwords Synchronously" + ], + "Quality Assurance and Testing with Chai": [ + "Learn How JavaScript Assertions Work", + "Test if a Variable or Function is Defined", + "Use Assert.isOK and Assert.isNotOK", + "Test for Truthiness", + "Use the Double Equals to Assert Equality", + "Use the Triple Equals to Assert Strict Equality", + "Assert Deep Equality with .deepEqual and .notDeepEqual", + "Compare the Properties of Two Elements", + "Test if One Value is Below or At Least as Large as Another", + "Test if a Value Falls within a Specific Range", + "Test if a Value is an Array", + "Test if an Array Contains an Item", + "Test if a Value is a String", + "Test if a String Contains a Substring", + "Use Regular Expressions to Test a String", + "Test if an Object has a Property", + "Test if a Value is of a Specific Data Structure Type", + "Test if an Object is an Instance of a Constructor", + "Run Functional Tests on API Endpoints using Chai-HTTP", + "Run Functional Tests on API Endpoints using Chai-HTTP II", + "Run Functional Tests on an API Response using Chai-HTTP III - PUT method", + "Run Functional Tests on an API Response using Chai-HTTP IV - PUT method", + "Run Functional Tests using a Headless Browser", + "Run Functional Tests using a Headless Browser II" + ], + "Advanced Node and Express": [ + "Set up a Template Engine", + "Use a Template Engine's Powers", + "Set up Passport", + "Serialization of a User Object", + "Implement the Serialization of a Passport User", + "Authentication Strategies", + "How to Use Passport Strategies", + "Create New Middleware", + "How to Put a Profile Together", + "Logging a User Out", + "Registration of New Users", + "Hashing Your Passwords", + "Clean Up Your Project with Modules", + "Implementation of Social Authentication", + "Implementation of Social Authentication II", + "Implementation of Social Authentication III", + "Set up the Environment", + "Communicate by Emitting", + "Handle a Disconnect", + "Authentication with Socket.IO", + "Announce New Users", + "Send and Display Chat Messages" + ], + "Information Security and Quality Assurance Projects": [ + "Metric-Imperial Converter", + "Issue Tracker", + "Personal Library", + "Stock Price Checker", + "Anonymous Message Board" + ] + }, + "Coding Interview Prep (Thousands of hours of challenges)": { + "Algorithms": [ + "Find the Symmetric Difference", + "Inventory Update", + "No Repeats Please", + "Pairwise", + "Implement Bubble Sort", + "Implement Selection Sort", + "Implement Insertion Sort", + "Implement Quick Sort", + "Implement Merge Sort" + ], + "Data Structures": [ + "Typed Arrays", + "Learn how a Stack Works", + "Create a Stack Class", + "Create a Queue Class", + "Create a Priority Queue Class", + "Create a Circular Queue", + "Create a Set Class", + "Remove from a Set", + "Size of the Set", + "Perform a Union on Two Sets", + "Perform an Intersection on Two Sets of Data", + "Perform a Difference on Two Sets of Data", + "Perform a Subset Check on Two Sets of Data", + "Create and Add to Sets in ES6", + "Remove items from a set in ES6", + "Use .has and .size on an ES6 Set", + "Use Spread and Notes for ES5 Set() Integration", + "Create a Map Data Structure", + "Create an ES6 JavaScript Map", + "Create a Hash Table", + "Work with Nodes in a Linked List", + "Create a Linked List Class", + "Remove Elements from a Linked List", + "Search within a Linked List", + "Remove Elements from a Linked List by Index", + "Add Elements at a Specific Index in a Linked List", + "Create a Doubly Linked List", + "Reverse a Doubly Linked List", + "Find the Minimum and Maximum Value in a Binary Search Tree", + "Add a New Element to a Binary Search Tree", + "Check if an Element is Present in a Binary Search Tree", + "Find the Minimum and Maximum Height of a Binary Search Tree", + "Use Depth First Search in a Binary Search Tree", + "Use Breadth First Search in a Binary Search Tree", + "Delete a Leaf Node in a Binary Search Tree", + "Delete a Node with One Child in a Binary Search Tree", + "Delete a Node with Two Children in a Binary Search Tree", + "Invert a Binary Tree", + "Create a Trie Search Tree", + "Insert an Element into a Max Heap", + "Remove an Element from a Max Heap", + "Implement Heap Sort with a Min Heap", + "Adjacency List", + "Adjacency Matrix", + "Incidence Matrix", + "Breadth-First Search", + "Depth-First Search" + ], + "Take Home Projects": [ + "Show the Local Weather", + "Build a Wikipedia Viewer", + "Use the Twitch JSON API", + "Build an Image Search Abstraction Layer", + "Build a Tic Tac Toe Game", + "Build a Simon Game", + "Build a Camper Leaderboard", + "Build a Recipe Box", + "Build the Game of Life", + "Build a Roguelike Dungeon Crawler Game", + "P2P Video Chat Application", + "Show National Contiguity with a Force Directed Graph", + "Map Data Across the Globe", + "Manage a Book Trading Club", + "Build a Pinterest Clone", + "Build a Nightlife Coordination App", + "Chart the Stock Market", + "Build a Voting App", + "Build a Pong Game", + "Build a Light-Bright App" + ], + "Rosetta Code": [ + "100 doors", + "24 game", + "9 billion names of God the integer", + "ABC Problem", + "Abundant, deficient and perfect number classifications", + "Accumulator factory", + "Ackermann function", + "Align columns", + "Amicable pairs", + "Averages/Mode", + "Averages/Pythagorean means", + "Averages/Root mean square", + "Babbage problem", + "Balanced brackets", + "Circles of given radius through two points", + "Closest-pair problem", + "Combinations", + "Comma quibbling", + "Compare a list of strings", + "Convert seconds to compound duration", + "Count occurrences of a substring", + "Count the coins", + "Cramer's rule", + "Date format", + "Date manipulation", + "Day of the week", + "Deal cards for FreeCell", + "Deepcopy", + "Define a primitive data type", + "Department Numbers", + "Discordian date", + "Element-wise operations", + "Emirp primes", + "Entropy", + "Equilibrium index", + "Ethiopian multiplication", + "Euler method", + "Evaluate binomial coefficients", + "Execute a Markov algorithm", + "Execute Brain****", + "Extensible prime generator", + "Factorial", + "Factors of a Mersenne number", + "Factors of an integer", + "Farey sequence", + "Fibonacci n-step number sequences", + "Fibonacci sequence", + "Fibonacci word", + "Fractran", + "Gamma function", + "Gaussian elimination", + "General FizzBuzz", + "Generate lower case ASCII alphabet", + "Generator/Exponential", + "Gray code", + "Greatest common divisor", + "Greatest subsequential sum", + "Hailstone sequence", + "Happy numbers", + "Harshad or Niven series", + "Hash from two arrays", + "Hash join", + "Heronian triangles", + "Hofstadter Figure-Figure sequences", + "Hofstadter Q sequence", + "I before E except after C", + "IBAN", + "Identity matrix", + "Iterated digits squaring", + "Jaro distance", + "JortSort", + "Josephus problem", + "Sailors, coconuts and a monkey problem", + "SEDOLs", + "S-Expressions", + "Taxicab numbers", + "Tokenize a string with escaping", + "Topological sort", + "Top rank per group", + "Towers of Hanoi", + "Vector cross product", + "Vector dot product", + "Word wrap", + "Y combinator", + "Zeckendorf number representation", + "Zhang-Suen thinning algorithm", + "Zig-zag matrix" + ], + "Project Euler": [ + "Problem 1: Multiples of 3 and 5", + "Problem 2: Even Fibonacci Numbers", + "Problem 3: Largest prime factor", + "Problem 4: Largest palindrome product", + "Problem 5: Smallest multiple", + "Problem 6: Sum square difference", + "Problem 7: 10001st prime", + "Problem 8: Largest product in a series", + "Problem 9: Special Pythagorean triplet", + "Problem 10: Summation of primes", + "Problem 11: Largest product in a grid", + "Problem 12: Highly divisible triangular number", + "Problem 13: Large sum", + "Problem 14: Longest Collatz sequence", + "Problem 15: Lattice paths", + "Problem 16: Power digit sum", + "Problem 17: Number letter counts", + "Problem 18: Maximum path sum I", + "Problem 19: Counting Sundays", + "Problem 20: Factorial digit sum", + "Problem 21: Amicable numbers", + "Problem 22: Names scores", + "Problem 23: Non-abundant sums", + "Problem 24: Lexicographic permutations", + "Problem 25: 1000-digit Fibonacci number", + "Problem 26: Reciprocal cycles", + "Problem 27: Quadratic primes", + "Problem 28: Number spiral diagonals", + "Problem 29: Distinct powers", + "Problem 30: Digit n powers", + "Problem 31: Coin sums", + "Problem 32: Pandigital products", + "Problem 33: Digit cancelling fractions", + "Problem 34: Digit factorials", + "Problem 35: Circular primes", + "Problem 36: Double-base palindromes", + "Problem 37: Truncatable primes", + "Problem 38: Pandigital multiples", + "Problem 39: Integer right triangles", + "Problem 40: Champernowne's constant", + "Problem 41: Pandigital prime", + "Problem 42: Coded triangle numbers", + "Problem 43: Sub-string divisibility", + "Problem 44: Pentagon numbers", + "Problem 45: Triangular, pentagonal, and hexagonal", + "Problem 46: Goldbach's other conjecture", + "Problem 47: Distinct primes factors", + "Problem 48: Self powers", + "Problem 49: Prime permutations", + "Problem 50: Consecutive prime sum", + "Problem 51: Prime digit replacements", + "Problem 52: Permuted multiples", + "Problem 53: Combinatoric selections", + "Problem 54: Poker hands", + "Problem 55: Lychrel numbers", + "Problem 56: Powerful digit sum", + "Problem 57: Square root convergents", + "Problem 58: Spiral primes", + "Problem 59: XOR decryption", + "Problem 60: Prime pair sets", + "Problem 61: Cyclical figurate numbers", + "Problem 62: Cubic permutations", + "Problem 63: Powerful digit counts", + "Problem 64: Odd period square roots", + "Problem 65: Convergents of e", + "Problem 66: Diophantine equation", + "Problem 67: Maximum path sum II", + "Problem 68: Magic 5-gon ring", + "Problem 69: Totient maximum", + "Problem 70: Totient permutation", + "Problem 71: Ordered fractions", + "Problem 72: Counting fractions", + "Problem 73: Counting fractions in a range", + "Problem 74: Digit factorial chains", + "Problem 75: Singular integer right triangles", + "Problem 76: Counting summations", + "Problem 77: Prime summations", + "Problem 78: Coin partitions", + "Problem 79: Passcode derivation", + "Problem 80: Square root digital expansion", + "Problem 81: Path sum: two ways", + "Problem 82: Path sum: three ways", + "Problem 83: Path sum: four ways", + "Problem 84: Monopoly odds", + "Problem 85: Counting rectangles", + "Problem 86: Cuboid route", + "Problem 87: Prime power triples", + "Problem 88: Product-sum numbers", + "Problem 89: Roman numerals", + "Problem 90: Cube digit pairs", + "Problem 91: Right triangles with integer coordinates", + "Problem 92: Square digit chains", + "Problem 93: Arithmetic expressions", + "Problem 94: Almost equilateral triangles", + "Problem 95: Amicable chains", + "Problem 96: Su Doku", + "Problem 97: Large non-Mersenne prime", + "Problem 98: Anagramic squares", + "Problem 99: Largest exponential", + "Problem 100: Arranged probability", + "Problem 101: Optimum polynomial", + "Problem 102: Triangle containment", + "Problem 103: Special subset sums: optimum", + "Problem 104: Pandigital Fibonacci ends", + "Problem 105: Special subset sums: testing", + "Problem 106: Special subset sums: meta-testing", + "Problem 107: Minimal network", + "Problem 108: Diophantine Reciprocals I", + "Problem 109: Darts", + "Problem 110: Diophantine Reciprocals II", + "Problem 111: Primes with runs", + "Problem 112: Bouncy numbers", + "Problem 113: Non-bouncy numbers", + "Problem 114: Counting block combinations I", + "Problem 115: Counting block combinations II", + "Problem 116: Red, green or blue tiles", + "Problem 117: Red, green, and blue tiles", + "Problem 118: Pandigital prime sets", + "Problem 119: Digit power sum", + "Problem 120: Square remainders", + "Problem 121: Disc game prize fund", + "Problem 122: Efficient exponentiation", + "Problem 123: Prime square remainders", + "Problem 124: Ordered radicals", + "Problem 125: Palindromic sums", + "Problem 126: Cuboid layers", + "Problem 127: abc-hits", + "Problem 128: Hexagonal tile differences", + "Problem 129: Repunit divisibility", + "Problem 130: Composites with prime repunit property", + "Problem 131: Prime cube partnership", + "Problem 132: Large repunit factors", + "Problem 133: Repunit nonfactors", + "Problem 134: Prime pair connection", + "Problem 135: Same differences", + "Problem 136: Singleton difference", + "Problem 137: Fibonacci golden nuggets", + "Problem 138: Special isosceles triangles", + "Problem 139: Pythagorean tiles", + "Problem 140: Modified Fibonacci golden nuggets", + "Problem 141: Investigating progressive numbers, n, which are also square", + "Problem 142: Perfect Square Collection", + "Problem 143: Investigating the Torricelli point of a triangle", + "Problem 144: Investigating multiple reflections of a laser beam", + "Problem 145: How many reversible numbers are there below one-billion?", + "Problem 146: Investigating a Prime Pattern", + "Problem 147: Rectangles in cross-hatched grids", + "Problem 148: Exploring Pascal's triangle", + "Problem 149: Searching for a maximum-sum subsequence", + "Problem 150: Searching a triangular array for a sub-triangle having minimum-sum", + "Problem 151: Paper sheets of standard sizes: an expected-value problem", + "Problem 152: Writing 1/2 as a sum of inverse squares", + "Problem 153: Investigating Gaussian Integers", + "Problem 154: Exploring Pascal's pyramid", + "Problem 155: Counting Capacitor Circuits", + "Problem 156: Counting Digits", + "Problem 157: Solving the diophantine equation 1/a+1/b= p/10n", + "Problem 158: Exploring strings for which only one character comes lexicographically after its neighbour to the left", + "Problem 159: Digital root sums of factorisations", + "Problem 160: Factorial trailing digits", + "Problem 161: Triominoes", + "Problem 162: Hexadecimal numbers", + "Problem 163: Cross-hatched triangles", + "Problem 164: Numbers for which no three consecutive digits have a sum greater than a given value", + "Problem 165: Intersections", + "Problem 166: Criss Cross", + "Problem 167: Investigating Ulam sequences", + "Problem 168: Number Rotations", + "Problem 169: Exploring the number of different ways a number can be expressed as a sum of powers of 2", + "Problem 170: Find the largest 0 to 9 pandigital that can be formed by concatenating products", + "Problem 171: Finding numbers for which the sum of the squares of the digits is a square", + "Problem 172: Investigating numbers with few repeated digits", + "Problem 173: Using up to one million tiles how many different \"hollow\" square laminae can be formed?", + "Problem 174: Counting the number of \"hollow\" square laminae that can form one, two, three, ... distinct arrangements", + "Problem 175: Fractions involving the number of different ways a number can be expressed as a sum of powers of 2", + "Problem 176: Right-angled triangles that share a cathetus", + "Problem 177: Integer angled Quadrilaterals", + "Problem 178: Step Numbers", + "Problem 179: Consecutive positive divisors", + "Problem 180: Rational zeros of a function of three variables", + "Problem 181: Investigating in how many ways objects of two different colours can be grouped", + "Problem 182: RSA encryption", + "Problem 183: Maximum product of parts", + "Problem 184: Triangles containing the origin", + "Problem 185: Number Mind", + "Problem 186: Connectedness of a network", + "Problem 187: Semiprimes", + "Problem 188: The hyperexponentiation of a number", + "Problem 189: Tri-colouring a triangular grid", + "Problem 190: Maximising a weighted product", + "Problem 191: Prize Strings", + "Problem 192: Best Approximations", + "Problem 193: Squarefree Numbers", + "Problem 194: Coloured Configurations", + "Problem 195: Inscribed circles of triangles with one angle of 60 degrees", + "Problem 196: Prime triplets", + "Problem 197: Investigating the behaviour of a recursively defined sequence", + "Problem 198: Ambiguous Numbers", + "Problem 199: Iterative Circle Packing", + "Problem 200: Find the 200th prime-proof sqube containing the contiguous sub-string \"200\"", + "Problem 201: Subsets with a unique sum", + "Problem 202: Laserbeam", + "Problem 203: Squarefree Binomial Coefficients", + "Problem 204: Generalised Hamming Numbers", + "Problem 205: Dice Game", + "Problem 206: Concealed Square", + "Problem 207: Integer partition equations", + "Problem 208: Robot Walks", + "Problem 209: Circular Logic", + "Problem 210: Obtuse Angled Triangles", + "Problem 211: Divisor Square Sum", + "Problem 212: Combined Volume of Cuboids", + "Problem 213: Flea Circus", + "Problem 214: Totient Chains", + "Problem 215: Crack-free Walls", + "Problem 216: Investigating the primality of numbers of the form 2n2-1", + "Problem 217: Balanced Numbers", + "Problem 218: Perfect right-angled triangles", + "Problem 219: Skew-cost coding", + "Problem 220: Heighway Dragon", + "Problem 221: Alexandrian Integers", + "Problem 222: Sphere Packing", + "Problem 223: Almost right-angled triangles I", + "Problem 224: Almost right-angled triangles II", + "Problem 225: Tribonacci non-divisors", + "Problem 226: A Scoop of Blancmange", + "Problem 227: The Chase", + "Problem 228: Minkowski Sums", + "Problem 229: Four Representations using Squares", + "Problem 230: Fibonacci Words", + "Problem 231: The prime factorisation of binomial coefficients", + "Problem 232: The Race", + "Problem 233: Lattice points on a circle", + "Problem 234: Semidivisible numbers", + "Problem 235: An Arithmetic Geometric sequence", + "Problem 236: Luxury Hampers", + "Problem 237: Tours on a 4 x n playing board", + "Problem 238: Infinite string tour", + "Problem 239: Twenty-two Foolish Primes", + "Problem 240: Top Dice", + "Problem 241: Perfection Quotients", + "Problem 242: Odd Triplets", + "Problem 243: Resilience", + "Problem 244: Sliders", + "Problem 245: Coresilience", + "Problem 246: Tangents to an ellipse", + "Problem 247: Squares under a hyperbola", + "Problem 248: Numbers for which Euler’s totient function equals 13!", + "Problem 249: Prime Subset Sums", + "Problem 250: 250250", + "Problem 251: Cardano Triplets", + "Problem 252: Convex Holes", + "Problem 253: Tidying up", + "Problem 254: Sums of Digit Factorials", + "Problem 255: Rounded Square Roots", + "Problem 256: Tatami-Free Rooms", + "Problem 257: Angular Bisectors", + "Problem 258: A lagged Fibonacci sequence", + "Problem 259: Reachable Numbers", + "Problem 260: Stone Game", + "Problem 261: Pivotal Square Sums", + "Problem 262: Mountain Range", + "Problem 263: An engineers' dream come true", + "Problem 264: Triangle Centres", + "Problem 265: Binary Circles", + "Problem 266: Pseudo Square Root", + "Problem 267: Billionaire", + "Problem 268: Counting numbers with at least four distinct prime factors less than 100", + "Problem 269: Polynomials with at least one integer root", + "Problem 270: Cutting Squares", + "Problem 271: Modular Cubes, part 1", + "Problem 272: Modular Cubes, part 2", + "Problem 273: Sum of Squares", + "Problem 274: Divisibility Multipliers", + "Problem 275: Balanced Sculptures", + "Problem 276: Primitive Triangles", + "Problem 277: A Modified Collatz sequence", + "Problem 278: Linear Combinations of Semiprimes", + "Problem 279: Triangles with integral sides and an integral angle", + "Problem 280: Ant and seeds", + "Problem 281: Pizza Toppings", + "Problem 282: The Ackermann function", + "Problem 283: Integer sided triangles for which the area/perimeter ratio is integral", + "Problem 284: Steady Squares", + "Problem 285: Pythagorean odds", + "Problem 286: Scoring probabilities", + "Problem 287: Quadtree encoding (a simple compression algorithm)", + "Problem 288: An enormous factorial", + "Problem 289: Eulerian Cycles", + "Problem 290: Digital Signature", + "Problem 291: Panaitopol Primes", + "Problem 292: Pythagorean Polygons", + "Problem 293: Pseudo-Fortunate Numbers", + "Problem 294: Sum of digits - experience #23", + "Problem 295: Lenticular holes", + "Problem 296: Angular Bisector and Tangent", + "Problem 297: Zeckendorf Representation", + "Problem 298: Selective Amnesia", + "Problem 299: Three similar triangles", + "Problem 300: Protein folding", + "Problem 301: Nim", + "Problem 302: Strong Achilles Numbers", + "Problem 303: Multiples with small digits", + "Problem 304: Primonacci", + "Problem 305: Reflexive Position", + "Problem 306: Paper-strip Game", + "Problem 307: Chip Defects", + "Problem 308: An amazing Prime-generating Automaton", + "Problem 309: Integer Ladders", + "Problem 310: Nim Square", + "Problem 311: Biclinic Integral Quadrilaterals", + "Problem 312: Cyclic paths on Sierpiński graphs", + "Problem 313: Sliding game", + "Problem 314: The Mouse on the Moon", + "Problem 315: Digital root clocks", + "Problem 316: Numbers in decimal expansions", + "Problem 317: Firecracker", + "Problem 318: 2011 nines", + "Problem 319: Bounded Sequences", + "Problem 320: Factorials divisible by a huge integer", + "Problem 321: Swapping Counters", + "Problem 322: Binomial coefficients divisible by 10", + "Problem 323: Bitwise-OR operations on random integers", + "Problem 324: Building a tower", + "Problem 325: Stone Game II", + "Problem 326: Modulo Summations", + "Problem 327: Rooms of Doom", + "Problem 328: Lowest-cost Search", + "Problem 329: Prime Frog", + "Problem 330: Euler's Number", + "Problem 331: Cross flips", + "Problem 332: Spherical triangles", + "Problem 333: Special partitions", + "Problem 334: Spilling the beans", + "Problem 335: Gathering the beans", + "Problem 336: Maximix Arrangements", + "Problem 337: Totient Stairstep Sequences", + "Problem 338: Cutting Rectangular Grid Paper", + "Problem 339: Peredur fab Efrawg", + "Problem 340: Crazy Function", + "Problem 341: Golomb's self-describing sequence", + "Problem 342: The totient of a square is a cube", + "Problem 343: Fractional Sequences", + "Problem 344: Silver dollar game", + "Problem 345: Matrix Sum", + "Problem 346: Strong Repunits", + "Problem 347: Largest integer divisible by two primes", + "Problem 348: Sum of a square and a cube", + "Problem 349: Langton's ant", + "Problem 350: Constraining the least greatest and the greatest least", + "Problem 351: Hexagonal orchards", + "Problem 352: Blood tests", + "Problem 353: Risky moon", + "Problem 354: Distances in a bee's honeycomb", + "Problem 355: Maximal coprime subset", + "Problem 356: Largest roots of cubic polynomials", + "Problem 357: Prime generating integers", + "Problem 358: Cyclic numbers", + "Problem 359: Hilbert's New Hotel", + "Problem 360: Scary Sphere", + "Problem 361: Subsequence of Thue-Morse sequence", + "Problem 362: Squarefree factors", + "Problem 363: Bézier Curves", + "Problem 364: Comfortable distance", + "Problem 365: A huge binomial coefficient", + "Problem 366: Stone Game III", + "Problem 367: Bozo sort", + "Problem 368: A Kempner-like series", + "Problem 369: Badugi", + "Problem 370: Geometric triangles", + "Problem 371: Licence plates", + "Problem 372: Pencils of rays", + "Problem 373: Circumscribed Circles", + "Problem 374: Maximum Integer Partition Product", + "Problem 375: Minimum of subsequences", + "Problem 376: Nontransitive sets of dice", + "Problem 377: Sum of digits, experience 13", + "Problem 378: Triangle Triples", + "Problem 379: Least common multiple count", + "Problem 380: Amazing Mazes!", + "Problem 381: (prime-k) factorial", + "Problem 382: Generating polygons", + "Problem 383: Divisibility comparison between factorials", + "Problem 384: Rudin-Shapiro sequence", + "Problem 385: Ellipses inside triangles", + "Problem 386: Maximum length of an antichain", + "Problem 387: Harshad Numbers", + "Problem 388: Distinct Lines", + "Problem 389: Platonic Dice", + "Problem 390: Triangles with non rational sides and integral area", + "Problem 391: Hopping Game", + "Problem 392: Enmeshed unit circle", + "Problem 393: Migrating ants", + "Problem 394: Eating pie", + "Problem 395: Pythagorean tree", + "Problem 396: Weak Goodstein sequence", + "Problem 397: Triangle on parabola", + "Problem 398: Cutting rope", + "Problem 399: Squarefree Fibonacci Numbers", + "Problem 400: Fibonacci tree game", + "Problem 401: Sum of squares of divisors", + "Problem 402: Integer-valued polynomials", + "Problem 403: Lattice points enclosed by parabola and line", + "Problem 404: Crisscross Ellipses", + "Problem 405: A rectangular tiling", + "Problem 406: Guessing Game", + "Problem 407: Idempotents", + "Problem 408: Admissible paths through a grid", + "Problem 409: Nim Extreme", + "Problem 410: Circle and tangent line", + "Problem 411: Uphill paths", + "Problem 412: Gnomon numbering", + "Problem 413: One-child Numbers", + "Problem 414: Kaprekar constant", + "Problem 415: Titanic sets", + "Problem 416: A frog's trip", + "Problem 417: Reciprocal cycles II", + "Problem 418: Factorisation triples", + "Problem 419: Look and say sequence", + "Problem 420: 2x2 positive integer matrix", + "Problem 421: Prime factors of n15+1", + "Problem 422: Sequence of points on a hyperbola", + "Problem 423: Consecutive die throws", + "Problem 424: Kakuro", + "Problem 425: Prime connection", + "Problem 426: Box-ball system", + "Problem 427: n-sequences", + "Problem 428: Necklace of Circles", + "Problem 429: Sum of squares of unitary divisors", + "Problem 430: Range flips", + "Problem 431: Square Space Silo", + "Problem 432: Totient sum", + "Problem 433: Steps in Euclid's algorithm", + "Problem 434: Rigid graphs", + "Problem 435: Polynomials of Fibonacci numbers", + "Problem 436: Unfair wager", + "Problem 437: Fibonacci primitive roots", + "Problem 438: Integer part of polynomial equation's solutions", + "Problem 439: Sum of sum of divisors", + "Problem 440: GCD and Tiling", + "Problem 441: The inverse summation of coprime couples", + "Problem 442: Eleven-free integers", + "Problem 443: GCD sequence", + "Problem 444: The Roundtable Lottery", + "Problem 445: Retractions A", + "Problem 446: Retractions B", + "Problem 447: Retractions C", + "Problem 448: Average least common multiple", + "Problem 449: Chocolate covered candy", + "Problem 450: Hypocycloid and Lattice points", + "Problem 451: Modular inverses", + "Problem 452: Long Products", + "Problem 453: Lattice Quadrilaterals", + "Problem 454: Diophantine reciprocals III", + "Problem 455: Powers With Trailing Digits", + "Problem 456: Triangles containing the origin II", + "Problem 457: A polynomial modulo the square of a prime", + "Problem 458: Permutations of Project", + "Problem 459: Flipping game", + "Problem 460: An ant on the move", + "Problem 461: Almost Pi", + "Problem 462: Permutation of 3-smooth numbers", + "Problem 463: A weird recurrence relation", + "Problem 464: Möbius function and intervals", + "Problem 465: Polar polygons", + "Problem 466: Distinct terms in a multiplication table", + "Problem 467: Superinteger", + "Problem 468: Smooth divisors of binomial coefficients", + "Problem 469: Empty chairs", + "Problem 470: Super Ramvok", + "Problem 471: Triangle inscribed in ellipse", + "Problem 472: Comfortable Distance II", + "Problem 473: Phigital number base", + "Problem 474: Last digits of divisors", + "Problem 475: Music festival", + "Problem 476: Circle Packing II", + "Problem 477: Number Sequence Game", + "Problem 478: Mixtures", + "Problem 479: Roots on the Rise", + "Problem 480: The Last Question" + ] + } +} \ No newline at end of file diff --git a/fcc_profiles.txt b/fcc_profiles.txt new file mode 100644 index 0000000..d145b02 --- /dev/null +++ b/fcc_profiles.txt @@ -0,0 +1,3 @@ +https://www.freecodecamp.org/magoun +https://www.freecodecamp.org/thirtythreeb +https://www.freecodecamp.org/jaaron0606 \ No newline at end of file diff --git a/get_bulk_fcc_progress.js b/get_bulk_fcc_progress.js new file mode 100644 index 0000000..13b584e --- /dev/null +++ b/get_bulk_fcc_progress.js @@ -0,0 +1,131 @@ +/** + * Scrapes multiple user profile from fcc_profiles.txt and + * compares completed challenges against the Greenville Codes curriculum. + * + * Track Greenville Codes progress with gvl_codes_path.json. + * + * Usage: node bulk_fcc_progress.js + * + */ + +const outputFile = 'student_progress.csv', + urlFile = 'fcc_profiles.txt', + trackFile = 'gvl_codes_path.json'; + +const fs = require('fs'); +const puppeteer = require('puppeteer'); +const URL = require('url'); + +// Function for filtering bad inputs from urlFile +let validateURL = (str) => { + const url = URL.parse(str); + + if (url.hostname) { + return true; + } else { + console.log('Skipping ' + str + ' (invalid URL)'); + return false; + } +}; + +// Read list of profile urls +// Returns an array of profile URLs +let readURLs = (file) => { + let profiles = fs.readFileSync(file, 'utf8'); + console.log('Reading profile urls from ' + urlFile); + + // Splitting the return on new line yields an array + return profiles.split('\n').filter(validateURL); +}; + +// Fetch html using puppeteer +let getHTML = async (url) => { + console.log('Getting HTML from ' + url); + + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + + // Wait for React components to load + await page.goto(url, { waitUntil: 'networkidle0'}); + + const html = await page.content(); + await browser.close(); + + return html; +}; + +// Parse the HTML for completed challenges +// Returns an array of challenge names +let parseHTML = (html) => { + const $ = require('cheerio').load(html); + let challenges = []; + + $('tr', 'tbody').each(function (index, element) { + let challenge = $(this).find('a').text(); + challenges.push(challenge); + }); + + return challenges; +}; + +// Compare progress to overall curriculum +// Returns a comma delimited string +let getProgress = (completedChallenges) => { + const rawJSON = fs.readFileSync(trackFile); + let challengeMap = JSON.parse(rawJSON); + + // Inconsistent capitalization, thus map to lower case + completedChallenges = completedChallenges.map(x => x.toLowerCase()); + + let progress = ''; + + // Loop through curriculum JSON + for (let section of Object.keys(challengeMap)) { + for (let subsection of Object.keys(challengeMap[section])) { + let numChallenges = challengeMap[section][subsection].length; + + // Inconsistent capitalization, thus map to lower case + let subsectionArray = challengeMap[section][subsection].map(x => x.toLowerCase()); + + let numCompleted = subsectionArray.reduce((acc, val) => { + return completedChallenges.indexOf(val) != -1 ? ++acc : acc; + }, 0); + + progress += `${Math.round(100 * numCompleted / numChallenges)}%` + ','; + } + } + + return progress; +}; + +// Writes the url and progress strings to a file +let writeProgress = (writeArray, file) => { + const outputString = writeArray.join('\n'); + + fs.writeFile(file, outputString, (err) => { + // Report success / failure + const successMessage = 'Greenville Codes progress was written to ' + file; + + err ? console.log(err) : console.log(successMessage); + }); +}; + +// Runs the program +let run = async () => { + // Get urls from urlFile + const urlArray = readURLs(urlFile); + let writeArray = []; + + // Assemble student progress into array + for (let url of urlArray) { + let progress = await getHTML(url) + .then(parseHTML) + .then(getProgress); + + writeArray[urlArray.indexOf(url)] = url + ',' + progress; + } + + writeProgress(writeArray, outputFile); +}; + +run(); \ No newline at end of file diff --git a/get_fcc_progress.js b/get_fcc_progress.js new file mode 100644 index 0000000..395f3ba --- /dev/null +++ b/get_fcc_progress.js @@ -0,0 +1,95 @@ +/** + * Scrapes a user profile from https://www.freecodecamp.org/{username} and + * compares completed challenges against the FCC curriculum. The results are + * output in JSON format to outputFile. + * + * Track Greenville Codes progress with gvl_codes_path.json. + * Track overall FreeCodeCamp progress with fcc_path.json. + * + * Usage: node fcc_progress.js + * + */ + +const outputFile = 'fcc_progress.json', + url = 'https://www.freecodecamp.org/magoun', + trackFile = 'gvl_codes_path.json'; + +const fs = require('fs'); +const puppeteer = require('puppeteer'); + +// Fetch html using puppeteer +let getHTML = async (url) => { + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + + // Wait for React components to load + await page.goto(url, { waitUntil: 'networkidle0'}); + + const html = await page.content(); + await browser.close(); + return html; +}; + +// Parse the HTML for completed challenges +// Returns an array of challenge names +let parseHTML = (html) => { + const $ = require('cheerio').load(html); + let challenges = []; + + $('tr', 'tbody').each(function (index, element) { + let challenge = $(this).find('a').text(); + challenges.push(challenge); + }); + + return challenges; +}; + +// Compare progress to overall curriculum +// Returns a JSON object +let getProgress = (completedChallenges) => { + const rawJSON = fs.readFileSync(trackFile); + let challengeMap = JSON.parse(rawJSON); + + // Inconsistent capitalization, thus map to lower case + completedChallenges = completedChallenges.map(x => x.toLowerCase()); + + // Loop through curriculum JSON + for (let section of Object.keys(challengeMap)) { + for (let subsection of Object.keys(challengeMap[section])) { + let numChallenges = challengeMap[section][subsection].length; + + // Inconsistent capitalization, thus map to lower case + let subsectionArray = challengeMap[section][subsection].map(x => x.toLowerCase()); + + let numCompleted = subsectionArray.reduce((acc, val) => { + return completedChallenges.indexOf(val) != -1 ? ++acc : acc; + }, 0); + + challengeMap[section][subsection] = `${Math.round(100 * numCompleted / numChallenges)}%`; + } + } + + return challengeMap; +}; + +// Writes the JSON object to a file +let writeJSON = (json) => { + // Format the json for readability + const jsonString = JSON.stringify(json, null, 2); + + fs.writeFile(outputFile, jsonString, (err) => { + // Report success / failure + const successMessage = 'Greenville Codes progress was written to ' + outputFile; + err ? console.log(err) : console.log(successMessage); + }); +}; + +// Runs the program +let run = () => { + getHTML(url) + .then(parseHTML) + .then(getProgress) + .then(writeJSON); +}; + +run(); \ No newline at end of file diff --git a/gvl_codes_path.json b/gvl_codes_path.json new file mode 100644 index 0000000..9c8d507 --- /dev/null +++ b/gvl_codes_path.json @@ -0,0 +1,564 @@ +{ + "Pre-Work Part 1": { + "Basic HTML and HTML5": [ + "Say Hello to HTML Elements", + "Headline with the h2 Element", + "Inform with the Paragraph Element", + "Fill in the Blank with Placeholder Text", + "Uncomment HTML", + "Comment out HTML", + "Delete HTML Elements", + "Introduction to HTML5 Elements", + "Add Images to Your Website", + "Link to External Pages with Anchor Elements", + "Link to Internal Sections of a Page with Anchor Elements", + "Nest an Anchor Element within a Paragraph", + "Make Dead Links Using the Hash Symbol", + "Turn an Image into a Link", + "Create a Bulleted Unordered List", + "Create an Ordered List", + "Create a Text Field", + "Add Placeholder Text to a Text Field", + "Create a Form Element", + "Add a Submit Button to a Form", + "Use HTML5 to Require a Field", + "Create a Set of Radio Buttons", + "Create a Set of Checkboxes", + "Check Radio Buttons and Checkboxes by Default", + "Nest Many Elements within a Single div Element", + "Declare the Doctype of an HTML Document", + "Define the Head and Body of an HTML Document" + ], + "Basic CSS": [ + "Change the Color of Text", + "Use CSS Selectors to Style Elements", + "Use a CSS Class to Style an Element", + "Style Multiple Elements with a CSS Class", + "Change the Font Size of an Element", + "Set the Font Family of an Element", + "Import a Google Font", + "Specify How Fonts Should Degrade", + "Size Your Images", + "Add Borders Around Your Elements", + "Add Rounded Corners with border-radius", + "Make Circular Images with a border-radius", + "Give a Background Color to a div Element", + "Set the id of an Element", + "Use an id Attribute to Style an Element", + "Adjust the Padding of an Element", + "Adjust the Margin of an Element", + "Add a Negative Margin to an Element", + "Add Different Padding to Each Side of an Element", + "Add Different Margins to Each Side of an Element", + "Use Clockwise Notation to Specify the Padding of an Element", + "Use Clockwise Notation to Specify the Margin of an Element", + "Use Attribute Selectors to Style Elements", + "Understand Absolute versus Relative Units", + "Style the HTML Body Element", + "Inherit Styles from the Body Element", + "Prioritize One Style Over Another", + "Override Styles in Subsequent CSS", + "Override Class Declarations by Styling ID Attributes", + "Override Class Declarations with Inline Styles", + "Override All Other Styles by using Important", + "Use Hex Code for Specific Colors", + "Use Hex Code to Mix Colors", + "Use Abbreviated Hex Code", + "Use RGB values to Color Elements", + "Use RGB to Mix Colors", + "Use CSS Variables to change several elements at once", + "Create a custom CSS Variable", + "Use a custom CSS Variable", + "Attach a Fallback value to a CSS Variable", + "Improve Compatibility with Browser Fallbacks", + "Cascading CSS variables", + "Change a variable for a specific area", + "Use a media query to change a variable" + ], + "Tribute Page Project": [ + "Build a Tribute Page" + ], + "Basic JavaScript": [ + "Comment Your JavaScript Code", + "Declare JavaScript Variables", + "Storing Values with the Assignment Operator", + "Initializing Variables with the Assignment Operator", + "Understanding Uninitialized Variables", + "Understanding Case Sensitivity in Variables", + "Add Two Numbers with JavaScript", + "Subtract One Number from Another with JavaScript", + "Multiply Two Numbers with JavaScript", + "Divide One Number by Another with JavaScript", + "Increment a Number with JavaScript", + "Decrement a Number with JavaScript", + "Create Decimal Numbers with JavaScript", + "Multiply Two Decimals with JavaScript", + "Divide One Decimal by Another with JavaScript", + "Finding a Remainder in JavaScript", + "Compound Assignment With Augmented Addition", + "Compound Assignment With Augmented Subtraction", + "Compound Assignment With Augmented Multiplication", + "Compound Assignment With Augmented Division", + "Declare String Variables", + "Escaping Literal Quotes in Strings", + "Quoting Strings with Single Quotes", + "Escape Sequences in Strings", + "Concatenating Strings with Plus Operator", + "Concatenating Strings with the Plus Equals Operator", + "Constructing Strings with Variables", + "Appending Variables to Strings", + "Find the Length of a String", + "Use Bracket Notation to Find the First Character in a String", + "Understand String Immutability", + "Use Bracket Notation to Find the Nth Character in a String", + "Use Bracket Notation to Find the Last Character in a String", + "Use Bracket Notation to Find the Nth-to-Last Character in a String", + "Word Blanks", + "Store Multiple Values in one Variable using JavaScript Arrays", + "Nest one Array within Another Array", + "Access Array Data with Indexes", + "Modify Array Data With Indexes", + "Access Multi-Dimensional Arrays With Indexes", + "Manipulate Arrays With push()", + "Manipulate Arrays With pop()", + "Manipulate Arrays With shift()", + "Manipulate Arrays With unshift()", + "Shopping List", + "Write Reusable JavaScript with Functions", + "Passing Values to Functions with Arguments", + "Global Scope and Functions", + "Local Scope and Functions", + "Global vs. Local Scope in Functions", + "Return a Value from a Function with Return", + "Understanding Undefined Value returned from a Function", + "Assignment with a Returned Value", + "Stand in Line", + "Understanding Boolean Values", + "Use Conditional Logic with If Statements", + "Comparison with the Equality Operator", + "Comparison with the Strict Equality Operator", + "Practice comparing different values", + "Comparison with the Inequality Operator", + "Comparison with the Strict Inequality Operator", + "Comparison with the Greater Than Operator", + "Comparison with the Greater Than Or Equal To Operator", + "Comparison with the Less Than Operator", + "Comparison with the Less Than Or Equal To Operator", + "Comparisons with the Logical And Operator", + "Comparisons with the Logical Or Operator", + "Introducing Else Statements", + "Introducing Else If Statements", + "Logical Order in If Else Statements", + "Chaining If Else Statements", + "Golf Code", + "Selecting from Many Options with Switch Statements", + "Adding a Default Option in Switch Statements", + "Multiple Identical Options in Switch Statements", + "Replacing If Else Chains with Switch", + "Returning Boolean Values from Functions", + "Return Early Pattern for Functions", + "Counting Cards", + "Build JavaScript Objects", + "Accessing Object Properties with Dot Notation", + "Accessing Object Properties with Bracket Notation", + "Accessing Object Properties with Variables", + "Updating Object Properties", + "Add New Properties to a JavaScript Object", + "Delete Properties from a JavaScript Object", + "Using Objects for Lookups", + "Testing Objects for Properties", + "Manipulating Complex Objects", + "Accessing Nested Objects", + "Accessing Nested Arrays", + "Record Collection", + "Iterate with JavaScript While Loops", + "Iterate with JavaScript For Loops", + "Iterate Odd Numbers With a For Loop", + "Count Backwards With a For Loop", + "Iterate Through an Array with a For Loop", + "Nesting For Loops", + "Iterate with JavaScript Do...While Loops", + "Profile Lookup", + "Generate Random Fractions with JavaScript", + "Generate Random Whole Numbers with JavaScript", + "Generate Random Whole Numbers within a Range", + "Use the parseInt Function", + "Use the parseInt Function with a Radix", + "Use the Conditional (Ternary) Operator", + "Use Multiple Conditional (Ternary) Operators" + ] + }, + "Pre-Work Part 2": { + "ES6": [ + "Explore Differences Between the var and let Keywords", + "Compare Scopes of the var and let Keywords", + "Declare a Read-Only Variable with the const Keyword", + "Mutate an Array Declared with const", + "Prevent Object Mutation", + "Use Arrow Functions to Write Concise Anonymous Functions", + "Write Arrow Functions with Parameters", + "Write Higher Order Arrow Functions", + "Set Default Parameters for Your Functions", + "Use the Rest Operator with Function Parameters", + "Use the Spread Operator to Evaluate Arrays In-Place", + "Use Destructuring Assignment to Assign Variables from Objects", + "Use Destructuring Assignment to Assign Variables from Nested Objects", + "Use Destructuring Assignment to Assign Variables from Arrays", + "Use Destructuring Assignment with the Rest Operator to Reassign Array Elements", + "Use Destructuring Assignment to Pass an Object as a Function's Parameters", + "Create Strings using Template Literals", + "Write Concise Object Literal Declarations Using Simple Fields", + "Write Concise Declarative Functions with ES6", + "Use class Syntax to Define a Constructor Function", + "Use getters and setters to Control Access to an Object", + "Understand the Differences Between import and require", + "Use export to Reuse a Code Block", + "Use * to Import Everything from a File", + "Create an Export Fallback with export default", + "Import a Default Export" + ], + "Regular Expressions": [ + "Using the Test Method", + "Match Literal Strings", + "Match a Literal String with Different Possibilities", + "Ignore Case While Matching", + "Extract Matches", + "Find More Than the First Match", + "Match Anything with Wildcard Period", + "Match Single Character with Multiple Possibilities", + "Match Letters of the Alphabet", + "Match Numbers and Letters of the Alphabet", + "Match Single Characters Not Specified", + "Match Characters that Occur One or More Times", + "Match Characters that Occur Zero or More Times", + "Find Characters with Lazy Matching", + "Find One or More Criminals in a Hunt", + "Match Beginning String Patterns", + "Match Ending String Patterns", + "Match All Letters and Numbers", + "Match Everything But Letters and Numbers", + "Match All Numbers", + "Match All Non-Numbers", + "Restrict Possible Usernames", + "Match Whitespace", + "Match Non-Whitespace Characters", + "Specify Upper and Lower Number of Matches", + "Specify Only the Lower Number of Matches", + "Specify Exact Number of Matches", + "Check for All or None", + "Positive and Negative Lookahead", + "Reuse Patterns Using Capture Groups", + "Use Capture Groups to Search and Replace", + "Remove Whitespace from Start and End" + ], + "Debugging": [ + "Use the JavaScript Console to Check the Value of a Variable", + "Understanding the Differences between the freeCodeCamp and Browser Console", + "Use typeof to Check the Type of a Variable", + "Catch Misspelled Variable and Function Names", + "Catch Unclosed Parentheses, Brackets, Braces and Quotes", + "Catch Mixed Usage of Single and Double Quotes", + "Catch Use of Assignment Operator Instead of Equality Operator", + "Catch Missing Open and Closing Parenthesis After a Function Call", + "Catch Arguments Passed in the Wrong Order When Calling a Function", + "Catch Off By One Errors When Using Indexing", + "Use Caution When Reinitializing Variables Inside a Loop", + "Prevent Infinite Loops with a Valid Terminal Condition" + ], + "Basic Data Structures": [ + "Use an Array to Store a Collection of Data", + "Access an Array's Contents Using Bracket Notation", + "Add Items to an Array with push() and unshift()", + "Remove Items from an Array with pop() and shift()", + "Remove Items Using splice()", + "Add Items Using splice()", + "Copy Array Items Using slice()", + "Copy an Array with the Spread Operator", + "Combine Arrays with the Spread Operator", + "Check For The Presence of an Element With indexOf()", + "Iterate Through All an Array's Items Using For Loops", + "Create complex multi-dimensional arrays", + "Add Key-Value Pairs to JavaScript Objects", + "Modify an Object Nested Within an Object", + "Access Property Names with Bracket Notation", + "Use the delete Keyword to Remove Object Properties", + "Check if an Object has a Property", + " Iterate Through the Keys of an Object with a for...in Statement", + "Generate an Array of All Object Keys with Object.keys()", + "Modify an Array Stored in an Object" + ] + }, + "12 Week Session - Part 1": { + "Basic Algorithm Scripting": [ + "Convert Celsius to Fahrenheit", + "Reverse a String", + "Factorialize a Number", + "Find the Longest Word in a String", + "Return Largest Numbers in Arrays", + "Confirm the Ending", + "Repeat a String Repeat a String", + "Truncate a String", + "Finders Keepers", + "Boo who", + "Title Case a Sentence", + "Slice and Splice", + "Falsy Bouncer", + "Where do I Belong", + "Mutations", + "Chunky Monkey" + ], + "Object Oriented Programming": [ + "Create a Basic JavaScript Object", + "Use Dot Notation to Access the Properties of an Object", + "Create a Method on an Object", + "Make Code More Reusable with the this Keyword", + "Define a Constructor Function", + "Use a Constructor to Create Objects", + "Extend Constructors to Receive Arguments", + "Verify an Object's Constructor with instanceof", + "Understand Own Properties", + "Use Prototype Properties to Reduce Duplicate Code", + "Iterate Over All Properties", + "Understand the Constructor Property", + "Change the Prototype to a New Object", + "Remember to Set the Constructor Property when Changing the Prototype", + "Understand Where an Object’s Prototype Comes From", + "Understand the Prototype Chain", + "Use Inheritance So You Don't Repeat Yourself", + "Inherit Behaviors from a Supertype", + "Set the Child's Prototype to an Instance of the Parent", + "Reset an Inherited Constructor Property", + "Add Methods After Inheritance", + "Override Inherited Methods", + "Use a Mixin to Add Common Behavior Between Unrelated Objects", + "Use Closure to Protect Properties Within an Object from Being Modified Externally", + "Understand the Immediately Invoked Function Expression (IIFE)", + "Use an IIFE to Create a Module" + ], + "Functional Programming": [ + "Learn About Functional Programming", + "Understand Functional Programming Terminology", + "Understand the Hazards of Using Imperative Code", + "Avoid Mutations and Side Effects Using Functional Programming", + "Pass Arguments to Avoid External Dependence in a Function", + "Refactor Global Variables Out of Functions", + "Use the map Method to Extract Data from an Array", + "Implement map on a Prototype", + "Use the filter Method to Extract Data from an Array", + "Implement the filter Method on a Prototype", + "Return Part of an Array Using the slice Method", + "Remove Elements from an Array Using slice Instead of splice", + "Combine Two Arrays Using the concat Method", + "Add Elements to the End of an Array Using concat Instead of push", + "Use the reduce Method to Analyze Data", + "Sort an Array Alphabetically using the sort Method", + "Return a Sorted Array Without Changing the Original Array", + "Split a String into an Array Using the split Method", + "Combine an Array into a String Using the join Method", + "Apply Functional Programming to Convert Strings to URL Slugs", + "Use the every Method to Check that Every Element in an Array Meets a Criteria", + "Use the some Method to Check that Any Elements in an Array Meet a Criteria", + "Introduction to Currying and Partial Application" + ], + "Intermediate Algorithm Scripting": [ + "Sum All Numbers in a Range", + "Diff Two Arrays", + "Seek and Destroy", + "Wherefore art thou", + "Spinal Tap Case", + "Pig Latin", + "Search and Replace", + "DNA Pairing", + "Missing letters", + "Sorted Union", + "Convert HTML Entities", + "Sum All Odd Fibonacci Numbers", + "Sum All Primes", + "Smallest Common Multiple", + "Drop it", + "Steamroller", + "Binary Agents", + "Everything Be True", + "Arguments Optional", + "Make a Person", + "Map the Debris" + ], + "Palindrome Checker Project": [ + "Palindrome Checker" + ], + "Roman Numeral Converter Project": [ + "Roman Numeral Converter" + ], + "Caesars Cipher Project": [ + "Caesars Cipher" + ], + "Telephone Number Validator Project": [ + "Telephone Number Validator" + ], + "Cash Register Project": [ + "Cash Register" + ] + }, + "12 Week Session - Part 2 (Advanced Track)": { + "Bootstrap": [ + "Use Responsive Design with Bootstrap Fluid Containers", + "Make Images Mobile Responsive", + "Center Text with Bootstrap", + "Create a Bootstrap Button", + "Create a Block Element Bootstrap Button", + "Taste the Bootstrap Button Color Rainbow", + "Call out Optional Actions with btn-info", + "Warn Your Users of a Dangerous Action with btn-danger", + "Use the Bootstrap Grid to Put Elements Side By Side", + "Ditch Custom CSS for Bootstrap", + "Use a span to Target Inline Elements", + "Create a Custom Heading", + "Add Font Awesome Icons to our Buttons", + "Add Font Awesome Icons to all of our Buttons", + "Responsively Style Radio Buttons", + "Responsively Style Checkboxes", + "Style Text Inputs as Form Controls", + "Line up Form Elements Responsively with Bootstrap", + "Create a Bootstrap Headline", + "House our page within a Bootstrap container-fluid div", + "Create a Bootstrap Row", + "Split Your Bootstrap Row", + "Create Bootstrap Wells", + "Add Elements within Your Bootstrap Wells", + "Apply the Default Bootstrap Button Style", + "Create a Class to Target with jQuery Selectors", + "Add id Attributes to Bootstrap Elements", + "Label Bootstrap Wells", + "Give Each Element a Unique id", + "Label Bootstrap Buttons", + "Use Comments to Clarify Code" + ], + "jQuery": [ + "Learn How Script Tags and Document Ready Work", + "Target HTML Elements with Selectors Using jQuery", + "Target Elements by Class Using jQuery", + "Target Elements by id Using jQuery", + "Delete Your jQuery Functions", + "Target the Same Element with Multiple jQuery Selectors", + "Remove Classes from an Element with jQuery", + "Change the CSS of an Element Using jQuery", + "Disable an Element Using jQuery", + "Change Text Inside an Element Using jQuery", + "Remove an Element Using jQuery", + "Use appendTo to Move Elements with jQuery", + "Clone an Element Using jQuery", + "Target the Parent of an Element Using jQuery", + "Target the Children of an Element Using jQuery", + "Target a Specific Child of an Element Using jQuery", + "Target Even Elements Using jQuery", + "Use jQuery to Modify the Entire Page" + ], + "Sass": [ + "Store Data with Sass Variables", + "Nest CSS with Sass", + "Create Reusable CSS with Mixins", + "Use @if and @else to Add Logic To Your Styles", + "Use @for to Create a Sass Loop", + "Use @each to Map Over Items in a List", + "Apply a Style Until a Condition is Met with @while", + "Split Your Styles into Smaller Chunks with Partials", + "Extend One Set of CSS Styles to Another Element" + ], + "React": [ + "Create a Simple JSX Element", + "Create a Complex JSX Element", + "Add Comments in JSX", + "Render HTML Elements to the DOM", + "Define an HTML Class in JSX", + "Learn About Self-Closing JSX Tags", + "Create a Stateless Functional Component", + "Create a React Component", + "Create a Component with Composition", + "Use React to Render Nested Components", + "Compose React Components", + "Render a Class Component to the DOM", + "Write a React Component from Scratch", + "Pass Props to a Stateless Functional Component", + "Pass an Array as Props", + "Use Default Props", + "Override Default Props", + "Use PropTypes to Define the Props You Expect", + "Access Props Using this.props", + "Review Using Props with Stateless Functional Components", + "Create a Stateful Component", + "Render State in the User Interface", + "Render State in the User Interface Another Way", + "Set State with this.setState", + "Bind 'this' to a Class Method", + "Use State to Toggle an Element", + "Write a Simple Counter", + "Create a Controlled Input", + "Create a Controlled Form", + "Pass State as Props to Child Components", + "Pass a Callback as Props", + "Use the Lifecycle Method componentWillMount", + "Use the Lifecycle Method componentDidMount", + "Add Event Listeners", + "Manage Updates with Lifecycle Methods", + "Optimize Re-Renders with shouldComponentUpdate", + "Introducing Inline Styles", + "Add Inline Styles in React", + "Use Advanced JavaScript in React Render Method", + "Render with an If/Else Condition", + "Use && for a More Concise Conditional", + "Use a Ternary Expression for Conditional Rendering", + "Render Conditionally from Props", + "Change Inline CSS Conditionally Based on Component State", + "Use Array.map() to Dynamically Render Elements", + "Give Sibling Elements a Unique Key Attribute", + "Use Array.filter() to Dynamically Filter an Array", + "Render React on the Server with renderToString" + ], + "Redux": [ + "Create a Redux Store", + "Get State from the Redux Store", + "Define a Redux Action", + "Define an Action Creator", + "Dispatch an Action Event", + "Handle an Action in the Store", + "Use a Switch Statement to Handle Multiple Actions", + "Use const for Action Types", + "Register a Store Listener", + "Combine Multiple Reducers", + "Send Action Data to the Store", + "Use Middleware to Handle Asynchronous Actions", + "Write a Counter with Redux", + "Never Mutate State", + "Use the Spread Operator on Arrays", + "Remove an Item from an Array", + "Copy an Object with Object.assign" + ], + "React and Redux": [ + "Getting Started with React Redux", + "Manage State Locally First", + "Extract State Logic to Redux", + "Use Provider to Connect Redux to React", + "Map State to Props", + "Map Dispatch to Props", + "Connect Redux to React", + "Connect Redux to the Messages App", + "Extract Local State into Redux", + "Moving Forward From Here" + ], + "Random Quote Machine Project": [ + "Build a Random Quote Machine" + ], + "Markdown Previewer Project": [ + "Build a Markdown Previewer" + ], + "Drum Machine Project": [ + "Build a Drum Machine" + ], + "Javascript Calculator Project": [ + "Build a JavaScript Calculator" + ], + "Pomodoro Clock Project": [ + "Build a Pomodoro Clock" + ] + } +} \ No newline at end of file diff --git a/lesson_map.js b/lesson_map.js deleted file mode 100644 index 9e9fc62..0000000 --- a/lesson_map.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * scrapes the lesson list from https://www.freecodecamp.org/map into - * a 3d array and copies a json string of the result, e.g. - * - * - * [ - * [ - * "section1", ["asg1", "asg2", ...], - * "section2", ["asg1", "asg2", ...], - ... - * ] - * ] -*/ - -const certification = document.querySelector('#collapseFront-End-Development-Certification>#nested') - -const sections = Array.from(certification.querySelectorAll('h3>a')) - -const sectionData = sections.map(el => [el.innerText, Array.from(certification.querySelectorAll(`${el.href.slice(el.href.indexOf('#'))}>p>a`)).map(x => x.innerText.replace(/ (in)?complete$/gi, ''))]); - -copy(JSON.stringify(sectionData)) - diff --git a/map_fcc_path.js b/map_fcc_path.js new file mode 100644 index 0000000..14a4a53 --- /dev/null +++ b/map_fcc_path.js @@ -0,0 +1,117 @@ +/** + * Scrapes the lesson list from https://learn.freecodecamp.org into + * a JSON Object, then writes that object to fcc_curriculum.json + * + * Usage: node lesson_map.js + * + * { 'section1': { + * 'subsection1': [ + * 'exercise1', + * 'exercise2', + * ... + * ], + * 'subsection2': [...], + * ... + * }, + * 'section2': {...}, + * ... + * } + * + */ + +// Fetch html using puppeteer +let getHTML = async (url) => { + const puppeteer = require('puppeteer'); + + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + // Wait for React components to load + await page.goto(url, { waitUntil: 'networkidle0'}); + + // Need to click links to fully expand map + // Outer level uses superblock class + let superblock = await page.$("li[class='superblock ']"); + while (superblock) { + await superblock.click(); + superblock = await page.$("li[class='superblock ']"); + } + + // Inner level uses block class + let block = await page.$("li[class='block ']"); + while (block) { + await block.click(); + block = await page.$("li[class='block ']"); + } + + // Map is now fully expanded. Get content and close browser. + const html = await page.content(); + await browser.close(); + return html; +}; + +// Create map of curriculum as JSON object +let createMap = (html) => { + const $ = require('cheerio').load(html); + let jsonObj = {}; + + // Assemble the data to convert to JSON + // Crawl the major sections in .superblock + $('.superblock').each(function (index, element) { + // Get the major section titles + let section = $(this).find('h4').text(); + + let sectionObj = {}; + // Crawl the subsections in .block + $(this).find('.block') + .each(function (index, element) { + // Get the minor section titles + let subsection = $(this).find('h5').text(); + + let exercises = []; + // Get the individual exercises for the subsection + $(this).find('a') + .each(function (index, element) { + // First item is an 'Intro item', which we can ignore + if (index != 0) { + let exercise = $(this).text(); + exercises.push(exercise); + } + }); + + // Store the exercise array as 'subsection' => exercises + sectionObj[subsection] = exercises; + }); + + // Store the section object as + // 'section' => {subsection1 => exercises, + // subsection2 => exercise, ...} + jsonObj[section] = sectionObj; + }); + + return jsonObj; +} + +// Writes the JSON object to a file +let writeJSON = (json) => { + const fs = require('fs'); + const outputFile = 'fcc_curriculum.json'; + // Format the json for readability + const jsonString = JSON.stringify(json, null, 2); + + fs.writeFile(outputFile, jsonString, (err) => { + // Report success / failure + const successMessage = 'FCC curriculum was written to ' + outputFile; + err ? console.log(err) : console.log(successMessage); + }); +}; + +// Runs the program +let run = () => { + const url = 'https://learn.freecodecamp.org'; + + getHTML(url) + .then(createMap) + .then(writeJSON); +}; + +run(); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..52811c7 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "fcc-progress", + "version": "1.0.0", + "description": "Scrape FreeCodeCamp.org to track student progress through the Greenville Codes program.", + "main": "fcc-progress.js", + "dependencies": { + "cheerio": "^1.0.0-rc.2", + "puppeteer": "^1.9.0" + }, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/magoun/fcc-progress.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/magoun/fcc-progress/issues" + }, + "homepage": "https://github.com/magoun/fcc-progress#readme" +}