My Codeforces Journal is a Chrome extension that helps Codeforces users track the problems they've solved. With just a click, the extension stores the details of the current problem to a Google Spreadsheet that the user has connected. This makes it easy to maintain a personal log of problems solved over time.
Want to contribute to this chrom-extension? Go to this repo :My Codeforces Journal Development
One-click Storage: Save the current Codeforces problem you’ve solved with a single click.
Spreadsheet Integration: Link your own Google Spreadsheet to store and organize problems in a format that suits you.
Problem Details Captured: The extension stores key information such as problem URL, problem name, and problem rating.
Watch this youtube video: How to get started with My Codeforces Journal
Before you can use the extension, make sure you have the following:
A Google Spreadsheet where the problems will be stored.
The Codeforces Journal Chrome extension installed and authorized to access your spreadsheet.
Make sure to read COMMON ERRORS SECTION at the end of this.
Download the dist
folder from the following link: My Codeforces Journal
Open Chrome and go to chrome://extensions/
.
Enable Developer mode in the top right corner.
Click on Load unpacked and select the folder where the dist
folder is saved.
The extension should now be visible on your extensions page.
Pin the extension on the toolbar for easy use.
Create a new Google Spreadsheet.
Name the Spreadsheet as you prefer.
Click on the "Extensions" menu, and then click on "App Script."
Click on "Untitled" and rename this App Script file to match the name of your Spreadsheet.
Copy the code below and paste it into the App Script editor, and save by keypressing cmd + S
/ctrl + S
Click on the "Deploy" button in the top right and choose "New Deployment."
Add a description of your choice ( could be same as your SpreadSheet's name ).
Set "Who has access" to "Anyone."
Click "Deploy" and authorize the App Script,
Upon clicking authorize, select the Google Account you used to create the SpreadSheet, and then you'll be shown a "Google hasn't verified this app" message
Click on Advanced at the bottom and,
Click on the name of the AppScript project shown, then click Allow
Redeploy the AppScript project just to make sure your project has been authorized properly
Copy the script URL, as this is your AppScript URL, and save it for later use in the extension.
Again click on "Deploy" button
Select "Manage deployments", and then a modal will show up
Click on the "Edit" button ( the Pen icon ), and now the "deploy" button at the bottom will become active
Click on deploy
If there exists some error with authorization, then you will be shown the authorize button again, or else you'll be shown to copy the script URL
function doPost(e) { try { var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]; // Parse the incoming request data var data; try { data = JSON.parse(e.postData.contents); } catch (parseError) { return ContentService.createTextOutput('{"status":"error","message":"Invalid JSON format."}').setMimeType(ContentService.MimeType.TEXT); } // Check if the request is to check for an existing problem if (data.action === "checkProblem") { const problemNameColumn = sheet .getRange("B2:B" + sheet.getLastRow()) .getValues() .flat(); const problemExists = problemNameColumn.includes(data.problemName); return ContentService.createTextOutput(JSON.stringify({ status: "success", exists: problemExists })).setMimeType(ContentService.MimeType.JSON); } // (Your existing 'initialize' and 'addProblem' logic here) if (data.action === "initialize") { var headers = ["Rating", "Problem", "Status", "Remarks", "Date", "Takeaway", "Topics"]; if (sheet.getLastRow() === 0 || sheet.getRange("A1").getValue() === "") { // Initialization logic remains the same sheet.getRange("A1:G1").merge(); sheet.getRange("A1").setValue("Codeforces Sheet"); sheet.getRange("A1").setFontSize(13).setHorizontalAlignment("center").setBackground("#ffd966"); sheet.getRange("A2:G2").setValues([headers]); sheet.getRange("A2:G2").setFontSize(12).setHorizontalAlignment("center").setBackground("#93c47d"); sheet.setColumnWidth(1, 75); sheet.setColumnWidth(2, 185); sheet.setColumnWidth(3, 155); sheet.setColumnWidth(4, 290); sheet.setColumnWidth(5, 80); sheet.setColumnWidth(6, 530); sheet.setColumnWidth(7, 190); sheet.getRange("A2:G1000").setVerticalAlignment("middle"); sheet.getRange("C2:C1000").setWrap(true); sheet.getRange("D2:D1000").setWrap(true); sheet.getRange("F2:F1000").setWrap(true); sheet.getRange("G2:G1000").setWrap(true); sheet.getRange("A2:A1000").setHorizontalAlignment("center"); sheet.getRange("B2:B1000").setHorizontalAlignment("center"); sheet.getRange("E2:E1000").setHorizontalAlignment("center"); sheet.getRange("G2:G1000").setHorizontalAlignment("center"); var newDate = new Date(); sheet.getRange("E3:E1000").setNumberFormat("dd-mmm-yyyy"); sheet.insertRowBefore(3); sheet .getRange("A3:G3") .setValues([ [9999, "Problem9999Z", "Solved", "Could not implement my intuition", newDate, "I understood how recursion works", "Recursion, DP, Graph"], ]); return ContentService.createTextOutput( '{"status":"success","message":"Headers initialized with formatting, custom column widths, and row added."}' ).setMimeType(ContentService.MimeType.TEXT); } else { return ContentService.createTextOutput('{"status":"success","message":"Headers already exist."}').setMimeType(ContentService.MimeType.TEXT); } } else if (data.action === "addProblem") { try { const newRow = [data.problemRating, data.problemName, data.problemStatus, data.remarks, data.dateSolved, data.takeaways, data.problemTopics]; const lastRow = sheet.getLastRow() + 1; sheet.appendRow(newRow); const problemNameCell = sheet.getRange(lastRow, 2); if (data.problemUrl) { problemNameCell.setValue(data.problemName).setFormula(`=HYPERLINK("${data.problemUrl}", "${data.problemName}")`); } return ContentService.createTextOutput('{"status":"success","message":"Problem data added with hyperlink."}').setMimeType( ContentService.MimeType.JSON ); } catch (addError) { return ContentService.createTextOutput('{"status":"error","message":"Failed to add problem data."}').setMimeType( ContentService.MimeType.TEXT ); } } } catch (error) { return ContentService.createTextOutput('{"status":"error","message":"' + error.message + '"}').setMimeType(ContentService.MimeType.TEXT); }}
Open any Codeforces problem page.
Once you’ve solved the problem, click on the My Codeforces Journal extension icon, and then click on "Add Problem."
Fill in the form, and press "Submit."
The problem details will be automatically saved to the connected spreadsheet.
This extension requires the following permissions:
Active Tab: To access the URL of the current tab when saving the problem.
Storage: To store your connected spreadsheet details.
Scripting: To run JavaScript
Cause: This typically occurs if a non-existing Codeforces ID was submitted.
Solution: Double-check the ID and ensure it's correct.
Cause: This error generally means the AppScript URL provided is incorrect.
Solution: Verify the URL and use the correct AppScript URL.
Additional Tip: This error may also appear if your AppScript Project hasn’t been properly authorized. To ensure proper authorization:
During the spreadsheet setup, deploy your AppScript Project twice; the second deployment will help confirm the AppScript Project is fully authorized.
Cause: This occurs if the problem you’re trying to add has not been solved within your last 40 submissions on Codeforces.
Solution: Ensure you’ve solved the problem recently. If not, attempt it on Codeforces before adding it to the Spreadsheet.