Add script, readme and requirements.
This commit is contained in:
@@ -168,3 +168,5 @@ cython_debug/
|
|||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
#.idea/
|
#.idea/
|
||||||
|
|
||||||
|
pdfs/
|
||||||
|
export/
|
||||||
@@ -1,3 +1,39 @@
|
|||||||
# moodle_quiz_pdf_export
|
# moodle_quiz_pdf_export
|
||||||
|
|
||||||
Kleines Script, um Moddle Quizzes/Tests von Studierenden automatisiert als PDF zu exportieren.
|
Kleines Script, um Moddle Quizzes/Tests von Studierenden automatisiert als PDF zu exportieren.
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
Requirements installieren:
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
# Konfiguration
|
||||||
|
Eine Textdatei `.env` im Hauptverzeichnis anlegen mit folgenden Inhalten und entsprechend anpassen:
|
||||||
|
```
|
||||||
|
MOODLE_URL="https://elearning.ohmportal.de"
|
||||||
|
QUIZ_ATTEMPTS_URL="URL der Ergebnisübersicht des Tests"
|
||||||
|
USERNAME="Moodle Username"
|
||||||
|
PASSWORD="Moodle Passwort"
|
||||||
|
PDF_OUTPUT_DIR="Gewünschtes Ausgabeverzeichnis"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hinweis:** `QUIZ_ATTEMPTS_URL` sollte die vollständige URL der Ergebnisübersicht des Moodle Tests sein, ggf. inklusive der gewünschten Filter (z.B. Gruppe) sowie einer Seitengröße, die alle Versuche anzeigt. Das Skript extrahiert die Versuche der Studierenden aus dieser Seite, d.h. es werden genau diejenigen Versuche exportiert, die bei Aufruf der URL in der Tabelle sichtbar sind.
|
||||||
|
|
||||||
|
# Ausführen
|
||||||
|
Das Skript ausführen.
|
||||||
|
```
|
||||||
|
python moodle_quiz_pdf_export.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Es sollte eine Ausgabe ähnlich wie unten erscheinen und die PDF-Dateien im gewünschten Ordner gespeichert werden.
|
||||||
|
|
||||||
|
```
|
||||||
|
Found 53 attempt URLs.
|
||||||
|
[1/53] Downloading: https://elearning.ohmportal.de/mod/quiz/review.php?attempt=241863
|
||||||
|
[2/53] Downloading: https://elearning.ohmportal.de/mod/quiz/review.php?attempt=241865
|
||||||
|
[3/53] Downloading: https://elearning.ohmportal.de/mod/quiz/review.php?attempt=241866
|
||||||
|
...
|
||||||
|
[53/53] Downloading: https://elearning.ohmportal.de/mod/quiz/review.php?attempt=241948
|
||||||
|
✅ All PDFs downloaded.
|
||||||
|
```
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
from selenium import webdriver
|
||||||
|
from selenium.common.exceptions import NoSuchElementException
|
||||||
|
from selenium.webdriver.chrome.options import Options
|
||||||
|
from selenium.webdriver.common.by import By
|
||||||
|
import time
|
||||||
|
import os
|
||||||
|
import base64
|
||||||
|
import dotenv
|
||||||
|
|
||||||
|
# Load configuration variables from .env File
|
||||||
|
dotenv.load_dotenv()
|
||||||
|
MOODLE_URL = os.getenv('MOODLE_URL')
|
||||||
|
QUIZ_ATTEMPTS_URL = os.getenv('QUIZ_ATTEMPTS_URL')
|
||||||
|
USERNAME = os.getenv('USERNAME')
|
||||||
|
PASSWORD = os.getenv('PASSWORD')
|
||||||
|
PDF_OUTPUT_DIR = os.getenv('PDF_OUTPUT_DIR')
|
||||||
|
|
||||||
|
# Ensure output directory exists
|
||||||
|
os.makedirs(PDF_OUTPUT_DIR, exist_ok=True)
|
||||||
|
|
||||||
|
# Chrome options for headless PDF printing
|
||||||
|
chrome_options = Options()
|
||||||
|
chrome_options.add_argument('--headless=new') # use --headless=new for Chrome v112+
|
||||||
|
chrome_options.add_argument('--disable-gpu')
|
||||||
|
chrome_options.add_argument('--kiosk-printing')
|
||||||
|
chrome_prefs = {
|
||||||
|
"printing.print_preview_sticky_settings.appState": '{"recentDestinations":[{"id":"Save as PDF","origin":"local"}],"selectedDestinationId":"Save as PDF","version":2}',
|
||||||
|
"savefile.default_directory": PDF_OUTPUT_DIR
|
||||||
|
}
|
||||||
|
chrome_options.add_experimental_option("prefs", chrome_prefs)
|
||||||
|
chrome_options.add_argument('--disable-popup-blocking')
|
||||||
|
chrome_options.add_argument('--no-sandbox')
|
||||||
|
chrome_options.add_argument('--print-to-pdf-no-header')
|
||||||
|
|
||||||
|
# Start driver
|
||||||
|
driver = webdriver.Chrome(options=chrome_options)
|
||||||
|
|
||||||
|
# --- LOGIN ---
|
||||||
|
driver.get(MOODLE_URL)
|
||||||
|
driver.find_element(By.ID, "username").send_keys(USERNAME)
|
||||||
|
driver.find_element(By.ID, "password").send_keys(PASSWORD)
|
||||||
|
driver.find_element(By.ID, "loginbtn").click()
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
# --- GO TO QUIZ RESULTS PAGE ---
|
||||||
|
driver.get(QUIZ_ATTEMPTS_URL)
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
# --- SCRAPE ATTEMPT LINKS ---
|
||||||
|
attempt_links = []
|
||||||
|
elements = driver.find_elements(By.CSS_SELECTOR, "a[href*='review.php?attempt=']")
|
||||||
|
for elem in elements:
|
||||||
|
url = elem.get_attribute("href")
|
||||||
|
if url and url not in attempt_links:
|
||||||
|
attempt_links.append(url)
|
||||||
|
|
||||||
|
print(f"Found {len(attempt_links)} attempt URLs.")
|
||||||
|
|
||||||
|
# --- HELPER: Save PDF from current page ---
|
||||||
|
def save_pdf_from_current_page(filename):
|
||||||
|
# Call DevTools Protocol directly
|
||||||
|
result = driver.execute_cdp_cmd("Page.printToPDF", {
|
||||||
|
"format": "A4",
|
||||||
|
"printBackground": True
|
||||||
|
})
|
||||||
|
pdf_data = base64.b64decode(result['data'])
|
||||||
|
with open(filename, "wb") as f:
|
||||||
|
f.write(pdf_data)
|
||||||
|
|
||||||
|
# --- DOWNLOAD EACH AS PDF ---
|
||||||
|
for idx, url in enumerate(attempt_links, start=1):
|
||||||
|
print(f"[{idx}/{len(attempt_links)}] Downloading: {url}")
|
||||||
|
driver.get(url)
|
||||||
|
time.sleep(2)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Try to get the student's name near the profile picture
|
||||||
|
student_name_elem = driver.find_element(By.CSS_SELECTOR, "td.cell a[href*='/user/view.php']")
|
||||||
|
student_name = student_name_elem.text.strip()
|
||||||
|
except NoSuchElementException:
|
||||||
|
# Fallback if name is not found
|
||||||
|
student_name = f"student_{idx}"
|
||||||
|
|
||||||
|
# Clean the name for filename use
|
||||||
|
safe_name = "".join(c for c in student_name if c.isalnum() or c in ('_', '-'))
|
||||||
|
|
||||||
|
filename = os.path.join(PDF_OUTPUT_DIR, f"{safe_name}.pdf")
|
||||||
|
|
||||||
|
save_pdf_from_current_page(filename)
|
||||||
|
|
||||||
|
print("✅ All PDFs downloaded.")
|
||||||
|
|
||||||
|
driver.quit()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
selenium
|
||||||
|
dotenv
|
||||||
Reference in New Issue
Block a user