Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
AhmedMohamedAbdelaty committed Mar 14, 2024
0 parents commit 56749f1
Show file tree
Hide file tree
Showing 10 changed files with 540 additions and 0 deletions.
61 changes: 61 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Sanfoundry Scrapper

Sanfoundry Scrapper is a Chrome extension **in development** that scrapes multiple-choice questions (MCQs) from sanfoundry.com and converts them into a PDF file.

## Features

- Scrape MCQs from sanfoundry.com
- Format scraped data
- Convert formatted data into a PDF file

## Files

- [popup.js](#file:popup.js-context): Contains the main functionality of the extension, including scraping, formatting, and PDF creation.
- [manifest.json](#file:manifest.json-context): The manifest file for the Chrome extension, which provides important metadata for the extension.
- [popup.html](#file:popup.html-context): The HTML file for the popup interface of the extension.

## Usage

1. Click on the extension icon in the Chrome toolbar.
2. Click on the "Convert the MCQ to PDF" button when you are on a sanfoundry.com page with MCQs.

## Installation

Follow these steps to install the extension:

1. Clone the repository to your local machine using `git clone <repository-url>` or download the repository as a ZIP file and extract it.
2. Open Google Chrome or a Chromium-based browser and navigate to `chrome://extensions/`.
3. Enable Developer Mode by clicking the toggle switch at the top right.
4. Click the "Load unpacked" button and select the directory where you cloned the repository.
5. The extension should now be installed and visible in your Chrome toolbar.
6. Pin the extension to your Chrome toolbar for easy access.

Please note that since this extension is still in development, it may not function as expected. We appreciate your patience and contributions.

## Development Status

### To-Do List

- [ ] Add support for different question formats.
- [ ] Implement a user-friendly interface for the popup.
- [ ] Add an option to customize the output PDF.

### Known Bugs

- The extension does not function correctly when a question or answer contains a code block. We are currently working on a fix for this issue. If you have any suggestions or solutions, please feel free to contribute.

## Contribution Guide

We welcome contributions from the community. Here are the steps to contribute:

1. Fork the repository.
2. Create a new branch for your changes.
3. Make your changes in your branch.
4. Submit a pull request with your changes.
5. Make sure your pull request describes what you changed, why you changed it, and how you tested these changes.

Please note that this project is still in development, and we appreciate your patience and contributions.

## Disclaimer

This extension is not affiliated with or endorsed by sanfoundry.com. It is developed for educational purposes only. Please use responsibly.
Binary file added icons/result128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/result16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added icons/result32.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
164 changes: 164 additions & 0 deletions jspdf.min.js

Large diffs are not rendered by default.

23 changes: 23 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"manifest_version": 3,
"name": "Sanfoundry Scrapper",
"description": "Scrapes the Sanfoundry website and saves the questions and answers in a PDF file",
"version": "1.0",
"action": {
"default_popup": "popup.html"
},
"permissions": [
"activeTab","scripting"
],
"web_accessible_resources": [
{
"resources": ["jspdf.min.js"],
"matches": ["<all_urls>"]
}
],
"icons": {
"16": "/icons/result16.png",
"32": "/icons/result32.png",
"128": "/icons/result128.png"
}
}
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "sanfoundry-scrapper",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
61 changes: 61 additions & 0 deletions popup.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* import font */
@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700');

* {
box-sizing: border-box;
font-family: 'Roboto', sans-serif;
}

body {
width: 300px;
height: 200px;
background-color: #f0f0f0;
padding: 0;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
}

.center {
position: relative;
text-align: center;
}

#btn {
background-color: #4CAF50;
/* Green */
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 4px;
}

#btn:hover {
background-color: #45a049;
}

.social-media {
margin-top: 20px;
}

.social-media a {
color: #333;
text-decoration: none;
margin-right: 10px;
font-size: 17px;
}

.social-media a i {
font-size: 20px;
}

.social-media a i.fa-github:hover {
color: #4078c0;
}
21 changes: 21 additions & 0 deletions popup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="popup.css" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" />
</head>
<body>
<div class="center">
<button id="btn">Convert the MCQ to PDF</button>
<div class="social-media">
<a href="https://github.com/AhmedMohamedAbdelaty">
<i class="fa fa-github"></i> My GitHub
</a>
</div>
<script src="./jspdf.min.js"></script>
<script type="module" src="popup.js"></script>
</div>
</body>
</html>
199 changes: 199 additions & 0 deletions popup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
let btn = document.getElementById("btn");

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
let { questions, title } = message;
for (let i = 0; i < questions.length; i++) {
let question = questions[i];
question.question = question.question.replace("View Answer", "");
question.question = i + 1 + ". " + question.question;
question.answer = "Answer: " + question.answer;
}
format(title, questions);
});

btn.addEventListener("click", async () => {
let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (tab.url.match(/sanfoundry.com/)) {
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: scrapeData,
});
} else {
alert("This extension only works on sanfoundry.com");
}
});
function scrapeData() {
let title = document.querySelector("h1.entry-title");
let content = document.querySelector("div.entry-content");
let questions = [];
for (let i = 0; i < content.children.length; i++) {
let child = content.children[i];
if (child.tagName === "P") {
if (child.innerText.match(/^\d+\./)) {
// get the question
let question = child.innerText.replace(/^\d+\.\s*/, "");
// get the answer and explanation
let answer = content.children[i + 1].innerText;
let questionObj = {
question,
answer: answer.replace("Answer: ", ""),
explanation: answer.split("Explanation: ")[1],
};
// push the object to the questions array
questions.push(questionObj);
}
}
}
chrome.runtime.sendMessage({ questions, title: title.textContent });
}

function format(title, questions) {
const formattedQuestions = [];

questions.forEach((question, index) => {
const text = question.question.split("\n")[0].trim();
const options = question.question.split("\n").slice(1);

const formattedOptions = options.map((option) => {
const letter = option.slice(0, 1); // Extracting option letter
const text = option.slice(2).trim(); // Extracting option text
return { letter, text };
});

// Extracting answer and explanation
const answer = question.answer
.split("\n")[0]
.replace("Answer: ", "")
.trim();
const explanation = question.answer
.split("\n")
.slice(1)
.join("\n")
.replace("Explanation: ", "")
.trim();

const formattedQuestion = {
id: index + 1, // Question number
text, // Question text
options: formattedOptions, // Formatted options
answer, // Answer
explanation, // Explanation
};
formattedQuestions.push(formattedQuestion);
});
createPDF(title, formattedQuestions);
}

function createPDF(title, questions) {
let doc = new jsPDF("p", "mm", [250, 360]);
let x = 10,
y = 10,
lineHeight = 7,
fontStyle = ["bold", "normal"],
fontSize = 12;
doc.setFontSize(fontSize);
doc.setFont("Times", fontStyle[0]);
doc.setTextColor(0, 0, 0);
let text = title;
let textWidth =
(doc.getStringUnitWidth(title) * fontSize) / doc.internal.scaleFactor;
x = (doc.internal.pageSize.width - textWidth) / 2;
let textLines = doc.splitTextToSize(text, 250);
textLines.forEach((line) => {
doc.text(line, x, y);
y += lineHeight;
});
y += lineHeight;
for (let i = 0; i < questions.length; i++) {
doc.setFont("Times", fontStyle[0]);
let question = questions[i];
let remainingHeight = doc.internal.pageSize.height - y;
if (
checkQuestionHeight(
doc,
y,
lineHeight,
remainingHeight,
question,
fontSize
) == false
) {
doc.addPage();
y = 10;
remainingHeight = doc.internal.pageSize.height - y;
}
let options = question.options;
let questionText = question.text;
let answer = question.answer;
let explanation = question.explanation;
let textLines = doc.splitTextToSize(questionText, 230);
textLines.forEach((line) => {
doc.text(line, 10, y);
y += lineHeight;
});
for (let i = 0; i < options.length - 1; i++) {
doc.setFont("Times", fontStyle[1]);
let option = options[i];
let letter = option.letter;
let text = option.text;
let textLines = doc.splitTextToSize(letter + ". " + text, 230);
textLines.forEach((line) => {
if (letter === answer) {
doc.setTextColor(0, 128, 0); // Set text color to green
} else {
doc.setTextColor(0, 0, 0);
}
doc.text(line, 10, y);
y += lineHeight;
});
}
doc.setTextColor(0, 0, 0);
let exp = "Explanation: " + explanation;
let expText = doc.splitTextToSize(exp, 230);
expText.forEach((line) => {
doc.text(line, 10, y);
y += lineHeight;
});
y += lineHeight;
}
doc.save(`${title}.pdf`);
}

function checkQuestionHeight(
doc,
y,
lineHeight,
remainingHeight,
question,
fontSize
) {
let options = question.options;
let questionText = question.text;
let answer = question.answer;
let explanation = question.explanation;
let textLines = doc.splitTextToSize(questionText, 230);
textLines.forEach((line) => {
// doc.text(line, 10, y);
y += lineHeight;
});
for (let i = 0; i < options.length - 1; i++) {
let option = options[i];
let letter = option.letter;
let text = option.text;
let textLines = doc.splitTextToSize(letter + ". " + text, 230);
textLines.forEach((line) => {
// doc.text(line, 10, y);
y += lineHeight;
});
}
let exp = "Explanation: " + explanation;
let expText = doc.splitTextToSize(exp, 230);
expText.forEach((line) => {
// doc.text(line, 10, y);
y += lineHeight;
});
if (y > remainingHeight + 300) {
return false;
}
return true;
}

0 comments on commit 56749f1

Please sign in to comment.