Compare commits
No commits in common. 'master' and 'main' have entirely different histories.
6 changed files with 164 additions and 156 deletions
@ -1,154 +0,0 @@ |
|||||||
# ---> Python |
|
||||||
# Byte-compiled / optimized / DLL files |
|
||||||
__pycache__/ |
|
||||||
*.py[cod] |
|
||||||
*$py.class |
|
||||||
|
|
||||||
# C extensions |
|
||||||
*.so |
|
||||||
|
|
||||||
# Distribution / packaging |
|
||||||
.Python |
|
||||||
build/ |
|
||||||
develop-eggs/ |
|
||||||
dist/ |
|
||||||
downloads/ |
|
||||||
eggs/ |
|
||||||
.eggs/ |
|
||||||
lib/ |
|
||||||
lib64/ |
|
||||||
parts/ |
|
||||||
sdist/ |
|
||||||
var/ |
|
||||||
wheels/ |
|
||||||
share/python-wheels/ |
|
||||||
*.egg-info/ |
|
||||||
.installed.cfg |
|
||||||
*.egg |
|
||||||
MANIFEST |
|
||||||
|
|
||||||
# PyInstaller |
|
||||||
# Usually these files are written by a python script from a template |
|
||||||
# before PyInstaller builds the exe, so as to inject date/other infos into it. |
|
||||||
*.manifest |
|
||||||
*.spec |
|
||||||
|
|
||||||
# Installer logs |
|
||||||
pip-log.txt |
|
||||||
pip-delete-this-directory.txt |
|
||||||
|
|
||||||
# Unit test / coverage reports |
|
||||||
htmlcov/ |
|
||||||
.tox/ |
|
||||||
.nox/ |
|
||||||
.coverage |
|
||||||
.coverage.* |
|
||||||
.cache |
|
||||||
nosetests.xml |
|
||||||
coverage.xml |
|
||||||
*.cover |
|
||||||
*.py,cover |
|
||||||
.hypothesis/ |
|
||||||
.pytest_cache/ |
|
||||||
cover/ |
|
||||||
|
|
||||||
# Translations |
|
||||||
*.mo |
|
||||||
*.pot |
|
||||||
|
|
||||||
# Django stuff: |
|
||||||
*.log |
|
||||||
local_settings.py |
|
||||||
db.sqlite3 |
|
||||||
db.sqlite3-journal |
|
||||||
|
|
||||||
# Flask stuff: |
|
||||||
instance/ |
|
||||||
.webassets-cache |
|
||||||
|
|
||||||
# Scrapy stuff: |
|
||||||
.scrapy |
|
||||||
|
|
||||||
# Sphinx documentation |
|
||||||
docs/_build/ |
|
||||||
|
|
||||||
# PyBuilder |
|
||||||
.pybuilder/ |
|
||||||
target/ |
|
||||||
|
|
||||||
# Jupyter Notebook |
|
||||||
.ipynb_checkpoints |
|
||||||
|
|
||||||
# IPython |
|
||||||
profile_default/ |
|
||||||
ipython_config.py |
|
||||||
|
|
||||||
# pyenv |
|
||||||
# For a library or package, you might want to ignore these files since the code is |
|
||||||
# intended to run in multiple environments; otherwise, check them in: |
|
||||||
# .python-version |
|
||||||
|
|
||||||
# pipenv |
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. |
|
||||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies |
|
||||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not |
|
||||||
# install all needed dependencies. |
|
||||||
#Pipfile.lock |
|
||||||
|
|
||||||
# poetry |
|
||||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. |
|
||||||
# This is especially recommended for binary packages to ensure reproducibility, and is more |
|
||||||
# commonly ignored for libraries. |
|
||||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control |
|
||||||
#poetry.lock |
|
||||||
|
|
||||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow |
|
||||||
__pypackages__/ |
|
||||||
|
|
||||||
# Celery stuff |
|
||||||
celerybeat-schedule |
|
||||||
celerybeat.pid |
|
||||||
|
|
||||||
# SageMath parsed files |
|
||||||
*.sage.py |
|
||||||
|
|
||||||
# Environments |
|
||||||
.env |
|
||||||
.venv |
|
||||||
env/ |
|
||||||
venv/ |
|
||||||
ENV/ |
|
||||||
env.bak/ |
|
||||||
venv.bak/ |
|
||||||
|
|
||||||
# Spyder project settings |
|
||||||
.spyderproject |
|
||||||
.spyproject |
|
||||||
|
|
||||||
# Rope project settings |
|
||||||
.ropeproject |
|
||||||
|
|
||||||
# mkdocs documentation |
|
||||||
/site |
|
||||||
|
|
||||||
# mypy |
|
||||||
.mypy_cache/ |
|
||||||
.dmypy.json |
|
||||||
dmypy.json |
|
||||||
|
|
||||||
# Pyre type checker |
|
||||||
.pyre/ |
|
||||||
|
|
||||||
# pytype static type analyzer |
|
||||||
.pytype/ |
|
||||||
|
|
||||||
# Cython debug symbols |
|
||||||
cython_debug/ |
|
||||||
|
|
||||||
# PyCharm |
|
||||||
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can |
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore |
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear |
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder. |
|
||||||
#.idea/ |
|
||||||
|
|
@ -0,0 +1,118 @@ |
|||||||
|
import tempfile |
||||||
|
|
||||||
|
from selenium import webdriver |
||||||
|
from selenium.webdriver.chrome.options import Options |
||||||
|
from selenium.webdriver.common.by import By |
||||||
|
from selenium.webdriver.support.ui import WebDriverWait |
||||||
|
from selenium.webdriver.support import expected_conditions as EC |
||||||
|
from datetime import datetime, timedelta |
||||||
|
from email.mime.text import MIMEText |
||||||
|
from email.mime.multipart import MIMEMultipart |
||||||
|
import smtplib |
||||||
|
import time |
||||||
|
from dotenv import load_dotenv |
||||||
|
import os |
||||||
|
import json |
||||||
|
|
||||||
|
|
||||||
|
# Load environment variables from .env file |
||||||
|
load_dotenv() |
||||||
|
|
||||||
|
# Email Configuration from environment variables |
||||||
|
SMTP_SERVER = os.getenv("SMTP_SERVER") |
||||||
|
SMTP_PORT = int(os.getenv("SMTP_PORT")) |
||||||
|
EMAIL_ADDRESS = os.getenv("EMAIL_ADDRESS") |
||||||
|
EMAIL_PASSWORD = os.getenv("EMAIL_PASSWORD") |
||||||
|
|
||||||
|
DAYS_BEFORE = int(os.getenv("DAYS_BEFORE")) |
||||||
|
|
||||||
|
def check_date(due_date, recipient_email): |
||||||
|
today = datetime.now() |
||||||
|
if today <= due_date <= today + timedelta(days=DAYS_BEFORE): |
||||||
|
print(f"Reminder: Book is due on {due_date}") |
||||||
|
|
||||||
|
# Send an email reminder |
||||||
|
subject = "Stadtbibliothek Radebeul - Buch bald fällig" |
||||||
|
body = f"Ein Buch muss am {due_date.strftime('%d.%m.%Y')} zurückgegeben werden. Bitte zurückgeben um Gebühren zu vermeiden." |
||||||
|
|
||||||
|
# Set up email |
||||||
|
msg = MIMEMultipart() |
||||||
|
msg['From'] = EMAIL_ADDRESS |
||||||
|
msg['To'] = recipient_email |
||||||
|
msg['Subject'] = subject |
||||||
|
msg.attach(MIMEText(body, 'plain')) |
||||||
|
|
||||||
|
# Connect to SMTP server and send email |
||||||
|
with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server: |
||||||
|
server.starttls() # Secure the connection |
||||||
|
server.login(EMAIL_ADDRESS, EMAIL_PASSWORD) |
||||||
|
server.send_message(msg) |
||||||
|
print(f"Email sent to {recipient_email}") |
||||||
|
|
||||||
|
def check_books(user, pw, recipient_email): |
||||||
|
options = Options() |
||||||
|
options.add_argument("--headless") # Run Chrome in headless mode |
||||||
|
options.add_argument("--disable-gpu") # Disable GPU acceleration |
||||||
|
options.add_argument("--no-sandbox") # Required for some environments |
||||||
|
options.add_argument("--incognito") |
||||||
|
# Use a unique temporary user-data-dir |
||||||
|
temp_dir = tempfile.mkdtemp() |
||||||
|
options.add_argument(f"--user-data-dir={temp_dir}") |
||||||
|
|
||||||
|
driver = webdriver.Chrome(options=options) # Use ChromeDriver |
||||||
|
|
||||||
|
try: |
||||||
|
# Navigate to the login page |
||||||
|
driver.get("https://webserver.sv-radebeul.de/Mediensuche-Konto/Mein-Konto") |
||||||
|
|
||||||
|
# Wait for the page to load |
||||||
|
wait = WebDriverWait(driver, 10) |
||||||
|
|
||||||
|
# Locate username and password fields |
||||||
|
username_field = driver.find_element(By.ID, "dnn_ctr355_Login_Login_COP_txtUsername") |
||||||
|
password_field = driver.find_element(By.ID, "dnn_ctr355_Login_Login_COP_txtPassword") |
||||||
|
|
||||||
|
# Enter login credentials |
||||||
|
username_field.send_keys(user) |
||||||
|
password_field.send_keys(pw) |
||||||
|
|
||||||
|
# Click login button |
||||||
|
login_button = driver.find_element(By.ID, "dnn_ctr355_Login_Login_COP_cmdLogin") |
||||||
|
login_button.click() |
||||||
|
|
||||||
|
# Wait for login to complete |
||||||
|
time.sleep(5) |
||||||
|
|
||||||
|
# Check if login was successful |
||||||
|
if "Mein Konto" in driver.page_source: |
||||||
|
print(f"Login successful for {user}!") |
||||||
|
# Locate the table containing the rows |
||||||
|
table = driver.find_element(By.ID, "dnn_ctr423_MainView_tpnlLoans_ucLoansView_grdViewLoans") |
||||||
|
table_rows = table.find_elements(By.XPATH, "./tbody/tr") |
||||||
|
|
||||||
|
# Loop through each row and extract the "Aktuelle Frist" date |
||||||
|
for row in table_rows: |
||||||
|
try: |
||||||
|
# Locate the "Aktuelle Frist" column in the current row |
||||||
|
due_date = row.find_element(By.XPATH, ".//td[span[contains(text(), 'Aktuelle Frist:')]]/span[2]") |
||||||
|
print(f"Aktuelle Frist for {user}: {due_date.text}") |
||||||
|
check_date(datetime.strptime(due_date.text, "%d.%m.%Y"), recipient_email) |
||||||
|
except Exception as e: |
||||||
|
print(f"Could not extract 'Aktuelle Frist' date for a row: {e}") |
||||||
|
else: |
||||||
|
print(f"Login failed for {user}. Please check the credentials or the page structure.") |
||||||
|
finally: |
||||||
|
# Close the browser |
||||||
|
driver.quit() |
||||||
|
|
||||||
|
if __name__ == '__main__': |
||||||
|
# Load user credentials from JSON file |
||||||
|
with open("./users.json", "r") as file: |
||||||
|
users = json.load(file) |
||||||
|
|
||||||
|
# Iterate over all users |
||||||
|
for user_data in users: |
||||||
|
username = user_data["username"] |
||||||
|
password = user_data["password"] |
||||||
|
recipient_email = user_data["recipient_email"] |
||||||
|
check_books(username, password, recipient_email) |
@ -0,0 +1,37 @@ |
|||||||
|
# Lend Expire Notification |
||||||
|
...for Stadtbibliothek Radebeul |
||||||
|
|
||||||
|
## About |
||||||
|
This script checks the current lends of the user. If a lend is about to expire it sends an e-mail notification. |
||||||
|
|
||||||
|
To get the Information the script simulates a user. It does a login to the OPAC System of the Stadtbibliothek Radebeul and extracts the information from the website. |
||||||
|
To archive this, it utiles using selenium. |
||||||
|
|
||||||
|
Each run will trigger one e-mail, so trigger or run in the desired frequency. |
||||||
|
|
||||||
|
## Getting Stared |
||||||
|
|
||||||
|
1. Check out the repo |
||||||
|
2. Add username, password and email address in the users.json file |
||||||
|
3. create a ".env" file or past the following configurations varabiles into the environment varabiles |
||||||
|
|
||||||
|
## Enviornment Varables |
||||||
|
|
||||||
|
``` |
||||||
|
SMTP_SERVER=mail.server.com |
||||||
|
SMTP_PORT=587 |
||||||
|
EMAIL_ADDRESS=sender-mail |
||||||
|
EMAIL_PASSWORD=password |
||||||
|
DAYS_BEFORE=3 |
||||||
|
``` |
||||||
|
|
||||||
|
## users.json |
||||||
|
``` |
||||||
|
[ |
||||||
|
{ |
||||||
|
"username": "username", |
||||||
|
"password": "password", |
||||||
|
"recipient_email": "notification_mail" |
||||||
|
} |
||||||
|
] |
||||||
|
``` |
Loading…
Reference in new issue