-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 56749f1
Showing
10 changed files
with
540 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |