diff --git a/scripts/other/createemail/config-template.py b/scripts/other/createemail/config-template.py new file mode 100644 index 0000000..30dbd10 --- /dev/null +++ b/scripts/other/createemail/config-template.py @@ -0,0 +1,18 @@ +""" +examples for +MYTOKEN="3fe723423dsd459266cec5e9ce81420c4fc2771", +MYURL "http://paperless:8000" +SEARCHPATH="?tags__id__all=2&custom_field_query=%5B%22OR%22,%5B%5B2,%22exists%22,%22false%22%5D%5D%5D&query=created:%5B-3%20month%20to%20now%5D,added:%5B-1%20week%20to%20now%5D&sort=added&reverse=1&page=1", +""" +settings = dict( + MYTOKEN="3fe723423dsd459266cec5e9ce81420c4fc2771", + MYURL="http://paperless.example.com:8000", + SEARCHPATH="?tags__id__all=2&custom_field_query=%5B%22OR%22,%5B%5B2,%22exists%22,%22false%22%5D%5D%5D&query=created:%5B-3%20month%20to%20now%5D,added:%5B-1%20week%20to%20now%5D&sort=added&reverse=1&page=1", + TO=["joe.sixpack@example.com"], + FROM="joe.sixpack@example.com+paperless@gmail.com", + SUBJECT="Paperless files in need of attendance.", + SMTP_HOST="localhost", + SMTP_PORT=25, + SMTP_USER="USER", + SMTP_PASS="" +) \ No newline at end of file diff --git a/scripts/other/createemail/main.py b/scripts/other/createemail/main.py new file mode 100644 index 0000000..9f100ed --- /dev/null +++ b/scripts/other/createemail/main.py @@ -0,0 +1,186 @@ +""" +Author: Your Name +Date: 2024-12-12 +Synopsis: This script retrieves documents, correspondents, and tags from a Paperless instance, + formats their information into an HTML email, and sends it via a local Postfix server. + +License (MIT): See the previous example. +""" + +import requests +import smtplib +from config import settings +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +from datetime import datetime, date + +TOKEN = settings["MYTOKEN"] +MYURL = settings["MYURL"] # e.g. "http://paperless:8000" (no /api here) +SEARCHPATH = settings["SEARCHPATH"] # Configurable search path for documents +TO = settings["TO"] # Can be a string or a list +FROM = settings["FROM"] +SUBJECT = settings["SUBJECT"] + +headers = { + "Authorization": f"Token {TOKEN}", + "Accept": "application/json" +} + +def safe_request(url): + """Make a request and handle exceptions.""" + try: + response = requests.get(url, headers=headers) + response.raise_for_status() + return response.json() + except requests.exceptions.RequestException as e: + print(f"Error fetching data from {url}: {e}") + return None + +def get_correspondents(): + url = f"{MYURL}/api/correspondents/" + data = safe_request(url) + if not data: + return [] + return [ + {"id": c["id"], "correspondent": c["name"]} + for c in data.get("results", []) + if "id" in c and "name" in c + ] + +def get_tags_map(): + url = f"{MYURL}/api/tags/" + data = safe_request(url) + if not data: + return {} + return {t["id"]: t["name"] for t in data.get("results", []) if "id" in t and "name" in t} + +def get_documents(): + """Fetch documents using the configurable SEARCHPATH.""" + url = f"{MYURL}/api/documents/{SEARCHPATH}" + data = safe_request(url) + if not data: + return [] + return data.get("results", []) + +def get_document_details(doc_id): + url = f"{MYURL}/api/documents/{doc_id}/" + data = safe_request(url) + if not data: + return "N/A", "N/A", None + return ( + data.get("original_file_name", "N/A"), + data.get("archived_file_name", "N/A"), + data.get("created_date", None), + ) + +def calculate_age(created_date_str): + if not created_date_str: + return "N/A" + try: + created_date = datetime.strptime(created_date_str, "%Y-%m-%d").date() + today = date.today() + age_days = (today - created_date).days + return age_days + except ValueError: + return "N/A" + +def create_email(docs, correspondents, tag_map): + corr_map = {c["id"]: c["correspondent"] for c in correspondents} + + # Sort documents by Age (descending) + sorted_docs = sorted( + docs, + key=lambda d: calculate_age(d.get("created_date")), + reverse=True, + ) + + lines = [] + for doc in sorted_docs: + try: + doc_id = doc.get("id") + corr_id = doc.get("correspondent") + doc_tags = doc.get("tags", []) + + if not doc_id: + continue + + corr_name = corr_map.get(corr_id, "No Correspondent") + + original_file_name, archived_file_name, created_date_str = get_document_details(doc_id) + age = calculate_age(created_date_str) + + # Format age + if isinstance(age, int) and age > 31: + age_str = f"{age}" + else: + age_str = str(age) + + # Map tag IDs to names + tag_names = [tag_map.get(tid, f"Tag-{tid}") for tid in doc_tags] + tags_str = ", ".join(tag_names) if tag_names else "None" + + # Make the correspondent's name the link to the file + detail_url = f"{MYURL}/documents/{doc_id}/" + corr_link = f"{corr_name}" + + # Add blank line before each bullet + lines.append( + f"
  • " + f"{corr_link}
    " + f"Age (days): {age_str}
    " + f"Tags: {tags_str}
    " + f"Original File: {original_file_name}
    " + f"Archived File: {archived_file_name}
    " + f"
  • " + ) + except Exception as e: + print(f"Error processing document ID {doc.get('id', 'unknown')}: {e}") + continue + + if not lines: + lines.append("
  • No documents found.
  • ") + + html_body = f""" + + +

    Here are your documents:

    + +

    Your trusted PaperlessNGX Runner

    + + +""" + + subject_with_count = f"{SUBJECT} ({len(docs)})" + + msg = MIMEMultipart("alternative") + msg["Subject"] = subject_with_count + msg["From"] = FROM + if isinstance(TO, list): + msg["To"] = ", ".join(TO) + else: + msg["To"] = TO + + part = MIMEText(html_body, "html") + msg.attach(part) + + return msg + +def send_email(msg): + try: + with smtplib.SMTP("localhost", 25) as server: + server.send_message(msg) + except Exception as e: + print(f"Error sending email: {e}") + +if __name__ == "__main__": + try: + correspondents = get_correspondents() + tag_map = get_tags_map() + docs = get_documents() + email_msg = create_email(docs, correspondents, tag_map) + send_email(email_msg) + print("Email sent.") + except Exception as e: + print(f"Error during execution: {e}")