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.
The new update adds the following feature:
- Feature to update the problem details of problems already added to your spreadsheet
Requirements for this new update:
- Update AppScript
- New Version of chrome-extension
Screen.Recording.2024-12-20.at.9.51.01.PM.mov
Screen.Recording.2024-12-20.at.9.56.33.PM.mov
Want to contribute to this Chrome extension? Check out the repository here: My Codeforces Journal Development.
-
Clone the Repository
Clone this repo to your local machine:git clone https://github.com/Dev-Code24/My-Codeforces-Journal-Development.git
-
Navigate to the Repository Directory
Open the directory where the repo is cloned and ensure you are at the root:cd My-Codeforces-Journal-Development -
Create a Development Environment File
Run the following command to create an.env.developmentfile:touch .env.development
-
Add Environment Variables
Open the.env.developmentfile and add the following configuration:VITE_DEV_OR_PROD="development" DEV_CODEFORCES_ID="your codeforces profile URL" DEV_APPSCRIPT_URL="your appscript URL"
- Replace
your codeforces profile URLwith the URL of your Codeforces profile. - Replace
your appscript URLwith your Apps Script URL.
- Replace
-
Run the Development Server
Start the development server:npm run dev:chrome
-
Load the Extension in Chrome/Brave
- Open Chrome or Brave and go to
chrome://extensions. - Turn on Developer Mode (toggle in the top-right corner).
- Click on Load unpacked and select the
distfolder located in the root of the repo you cloned.
- Open Chrome or Brave and go to
- 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
distfolder 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
distfolder 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
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]; function doPost(e) { try { // 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); } // 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":"Verified !."}').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 ); } } // New feature: Update problem data if (data.action === "updateProblem") { const rows = sheet.getDataRange().getValues(); const problemName = data.problemName; // Find the row with the matching problem name for (let i = 0; i < rows.length; i++) { if (rows[i][1] === problemName) { // Assuming column B has the problem name sheet.getRange(i + 1, 3).setValue(data.problemStatus); // Update "Status" in column C sheet.getRange(i + 1, 4).setValue(data.remarks); // Update "Remarks" in column D sheet.getRange(i + 1, 6).setValue(data.takeaways); // Update "Takeaway" in column F return ContentService.createTextOutput(JSON.stringify({ status: "success", message: "Problem data updated successfully." })).setMimeType( ContentService.MimeType.JSON ); } } return ContentService.createTextOutput(JSON.stringify({ status: "error", message: "Problem not found." })).setMimeType( ContentService.MimeType.JSON ); } } catch (error) { return ContentService.createTextOutput('{"status":"error","message":"' + error.message + '"}').setMimeType(ContentService.MimeType.TEXT); } } // New feature: Handle GET requests for fetching problem data function doGet(e) { const problemName = e.parameter.problemName; const data = sheet.getDataRange().getValues(); // Find the row with the matching problem name const row = data.find((row) => row[1] === problemName); // Assuming column B has the problem name if (row) { return ContentService.createTextOutput( JSON.stringify({ status: "success", problem: { status: row[2], // Assuming "Status" is column C remarks: row[3], // Assuming "Remarks" is column D takeaways: row[5], // Assuming "Takeaway" is column F }, }) ).setMimeType(ContentService.MimeType.JSON); } else { return ContentService.createTextOutput(JSON.stringify({ status: "error", message: "Problem not found." })).setMimeType( ContentService.MimeType.JSON ); } }- 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 250 submissions on Codeforces.
- Solution: Ensure you’ve solved the problem recently. If not, attempt it on Codeforces before adding it to the Spreadsheet.
