From 6bbb9f59441714fce26e8c1f1a637556fa4f4c16 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Tue, 15 Jul 2025 07:22:51 +0000 Subject: [PATCH] Upload files to "mailling" This is python scripts for storing mail body to mongoDB and script to send mail body from mongoDB --- mailling/config.json | 19 ++ mailling/send_project_report_email.py | 319 +++++++++++++++++++++++++ mailling/store_project_report_email.py | 106 ++++++++ 3 files changed, 444 insertions(+) create mode 100644 mailling/config.json create mode 100644 mailling/send_project_report_email.py create mode 100644 mailling/store_project_report_email.py diff --git a/mailling/config.json b/mailling/config.json new file mode 100644 index 0000000..fba66ac --- /dev/null +++ b/mailling/config.json @@ -0,0 +1,19 @@ +{ + "SMPT": { + "SMPTSERVER": "smtp.gmail.com", + "PORT": 587, + "SENDER_EMAIL": "marcoioitsoft@gmail.com", + "SENDER_PASSWORD": "qrtq wfuj hwpp fhqr" + }, + "API": { + "BASE_URL": "http://localhost:5032/api", + "USERNAME": "admin@marcoaiot.com", + "PASSWORD": "User@123" + }, + "MONGODB":{ + "MONGO_CONNECTION_STRING": "mongodb://localhost:27017", + "DATABASE_NAME": "MarcoBMS_Caches", + "COLLECTION_NAME": "ProjectReportMail" + } +} + diff --git a/mailling/send_project_report_email.py b/mailling/send_project_report_email.py new file mode 100644 index 0000000..d5543e1 --- /dev/null +++ b/mailling/send_project_report_email.py @@ -0,0 +1,319 @@ +import smtplib +import requests +import ssl +import json +from email.message import EmailMessage +import pymongo +from pymongo.errors import ConnectionFailure, OperationFailure, PyMongoError +from bson.objectid import ObjectId # Import ObjectId for querying by _id + +# --- Configuration Loading --- +def load_config_from_json(file_path="config.json"): + """Loads configuration from a JSON file.""" + try: + with open(file_path, "r", encoding="utf-8") as f: + config = json.load(f) + print(f"Configuration loaded from {file_path}") + return config + except FileNotFoundError: + print(f"Error: Configuration file '{file_path}' not found. Please create it.") + exit(1) + except json.JSONDecodeError: + print(f"Error: Could not decode JSON from '{file_path}'. Check file format for errors.") + exit(1) + except Exception as e: + print(f"An unexpected error occurred while loading config: {e}") + exit(1) + +# Load configuration at the start +CONFIG = load_config_from_json() + +# Access variables from the nested configuration +SMPT_CONFIG = CONFIG.get("SMPT", {}) +SMTP_SERVER = SMPT_CONFIG.get("SMPTSERVER") +SMTP_PORT = int(SMPT_CONFIG.get("PORT")) if SMPT_CONFIG.get("PORT") is not None else None +SENDER_EMAIL = SMPT_CONFIG.get("SENDER_EMAIL") +SENDER_PASSWORD = SMPT_CONFIG.get("SENDER_PASSWORD") + +API_CONFIG = CONFIG.get("API", {}) +BASE_URL = API_CONFIG.get("BASE_URL") +API_USERNAME = API_CONFIG.get("USERNAME") +API_PASSWORD = API_CONFIG.get("PASSWORD") + +MONGODB_CONFIG = CONFIG.get("MONGODB", {}) +MONGO_CONNECTION_STRING = MONGODB_CONFIG.get("MONGO_CONNECTION_STRING") +DATABASE_NAME = MONGODB_CONFIG.get("DATABASE_NAME") +COLLECTION_NAME = MONGODB_CONFIG.get("COLLECTION_NAME") + +# Validate essential configuration (more robust check) +if not (SMTP_SERVER and SMTP_PORT and SENDER_EMAIL and SENDER_PASSWORD and + BASE_URL and API_USERNAME and API_PASSWORD and + MONGO_CONNECTION_STRING and DATABASE_NAME and COLLECTION_NAME): + print("Error: One or more essential configuration variables are missing or invalid in config.json.") + # Print which specific parts are missing for easier debugging + missing_configs = [] + if not SMTP_SERVER: missing_configs.append("SMPT.SMPTSERVER") + if not SMTP_PORT: missing_configs.append("SMPT.PORT") + if not SENDER_EMAIL: missing_configs.append("SMPT.SENDER_EMAIL") + if not SENDER_PASSWORD: missing_configs.append("SMPT.SENDER_PASSWORD") + if not BASE_URL: missing_configs.append("API.BASE_URL") + if not API_USERNAME: missing_configs.append("API.USERNAME") + if not API_PASSWORD: missing_configs.append("API.PASSWORD") + if not MONGO_CONNECTION_STRING: missing_configs.append("MONGODB.MONGO_CONNECTION_STRING") + if not DATABASE_NAME: missing_configs.append("MONGODB.DATABASE_NAME") + if not COLLECTION_NAME: missing_configs.append("MONGODB.COLLECTION_NAME") + print(f"Missing/Invalid: {', '.join(missing_configs)}") + exit(1) + +# Create a default SSL context once for secure connections +SSL_CONTEXT = ssl.create_default_context() + +# --- API Functions --- +def login_api(): + """Logs into the API and returns the JWT token.""" + payload = {"username": API_USERNAME, "password": API_PASSWORD} + headers = {"Content-Type": "application/json"} + try: + response = requests.post(f"{BASE_URL}/auth/login", json=payload, headers=headers, timeout=10) # Add timeout + response.raise_for_status() + data = response.json().get("data") # Use .get() for safer access + jwt = data.get("token") if data else None # Safely get token + if jwt: + print("API login successful.") + return jwt + else: + print("API response missing 'data' or 'token' key.") + return None + except requests.exceptions.HTTPError as e: + print(f"HTTP Error during API login: {e} - Response: {getattr(e.response, 'text', 'N/A')}") + return None + except requests.exceptions.Timeout: + print("Timeout Error during API login: The server did not respond in time.") + return None + except requests.exceptions.ConnectionError: + print("Connection Error during API login: Could not connect to the API server. Check URL and network.") + return None + except requests.exceptions.RequestException as e: + print(f"An unexpected error occurred during API login: {e}") + return None + except json.JSONDecodeError: + print(f"API response is not valid JSON for login: {response.text}") + return None + +def get_email_bodies_from_api(jwt): + """Retrieves email body data from the API.""" + if not jwt: + print("No JWT token available to get email bodies.") + return [] + + headers = {"Authorization": f"Bearer {jwt}", "Content-Type": "application/json"} + try: + response = requests.get(f"{BASE_URL}/report/report-mail", headers=headers, timeout=10) # Add timeout + response.raise_for_status() + data = response.json().get("data") # Safely get data + if isinstance(data, list): + print(f"Successfully retrieved {len(data)} email bodies from API.") + return data + else: + print("API response for report-mail is missing 'data' key or it's not a list.") + return [] + except requests.exceptions.HTTPError as e: + print(f"HTTP Error when getting email bodies: {e} - Response: {getattr(e.response, 'text', 'N/A')}") + return [] + except requests.exceptions.Timeout: + print("Timeout Error when getting email bodies: The server did not respond in time.") + return [] + except requests.exceptions.ConnectionError: + print("Connection Error when getting email bodies: Could not connect to the API server. Check URL and network.") + return [] + except requests.exceptions.RequestException as e: + print(f"An error occurred when getting email bodies: {e}") + return [] + except json.JSONDecodeError: + print(f"API response for getting email bodies is not valid JSON: {response.text}") + return [] + +# --- SMTP Functions --- +def login_smtp(): + """Logs into the SMTP server and returns the server object.""" + server = None + try: + server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) + server.starttls(context=SSL_CONTEXT) + print("TLS connection established.") + + server.login(SENDER_EMAIL, SENDER_PASSWORD) + print("Logged in to SMTP server successfully.") + return server + except smtplib.SMTPAuthenticationError as e: + print(f"SMTP Authentication Error: {e}") + print("Please check your email address and password/app password.") + print("For Gmail, ensure 'Less secure app access' is OFF and you're using an App Password.") + print("For Outlook, you might need to generate an App Password.") + if server: server.quit() + return None + except smtplib.SMTPServerDisconnected as e: + print(f"SMTP Server Disconnected Error: {e}") + print("The SMTP server unexpectedly disconnected. Check network or server status.") + if server: server.quit() + return None + except smtplib.SMTPException as e: + print(f"SMTP Error: {e}") + print("An error occurred during the SMTP transaction.") + if server: server.quit() + return None + except Exception as e: + print(f"An unexpected error occurred during SMTP login: {e}") + if server: server.quit() + return None + +def send_email(smtp_server, receiver_email, subject, body): + """Sends an HTML email using a pre-logged-in SMTP server.""" + if not smtp_server: + print("SMTP server not initialized. Cannot send email.") + return False + + msg = EmailMessage() + msg["From"] = SENDER_EMAIL + msg["Subject"] = subject + + msg.add_alternative(body, subtype="html") + + if isinstance(receiver_email, str): + receiver_email = [receiver_email] + + msg["To"] = ", ".join(receiver_email) + recipients_for_log = ", ".join(receiver_email) + + print(f"Attempting to send email from {SENDER_EMAIL} to {recipients_for_log}...") + + try: + smtp_server.send_message(msg, from_addr=SENDER_EMAIL, to_addrs=receiver_email) + print(f"Email sent successfully to {recipients_for_log}!") + return True + except smtplib.SMTPRecipientsRefused as e: + print(f"Recipient Refused Error: {e.recipients}") + print(f"Email not sent to some recipients: {recipients_for_log}. Check recipient addresses.") + return False + except smtplib.SMTPException as e: + print(f"SMTP Error during sending: {e}") + print(f"Could not send email to {recipients_for_log}.") + return False + except Exception as e: + print(f"An unexpected error occurred while sending email: {e}") + print(f"Could not send email to {recipients_for_log}.") + return False + +# --- MongoDB Functions --- +def update_mongodb_document(query, new_values, upsert=False, multi=False): + """ + Connects to MongoDB and updates documents in a specified collection. + """ + client = None # Initialize client to None for finally block + try: + # Establish a connection to MongoDB + # The serverSelectionTimeoutMS helps prevent long waits if the server is unreachable + client = pymongo.MongoClient( + MONGO_CONNECTION_STRING, serverSelectionTimeoutMS=5000 + ) + + # The ping command is cheap and does not require auth. + # This will raise an exception if the connection fails. + client.admin.command("ping") + print("MongoDB connection successful!") + + # Access the specified database and collection + db = client[DATABASE_NAME] + collection = db[COLLECTION_NAME] + + print( + f"Attempting to update document(s) in '{DATABASE_NAME}.{COLLECTION_NAME}'..." + ) + print(f"Query: {query}") + print(f"New Values: {new_values}") + + if multi: + # Update multiple documents + result = collection.update_many(query, new_values, upsert=upsert) + print( + f"Matched {result.matched_count} document(s) and modified {result.modified_count} document(s)." + ) + if result.upserted_id: + print(f"Upserted a new document with _id: {result.upserted_id}") + else: + # Update a single document + result = collection.update_one(query, new_values, upsert=upsert) + print( + f"Matched {result.matched_count} document(s) and modified {result.modified_count} document(s)." + ) + if result.upserted_id: + print(f"Upserted a new document with _id: {result.upserted_id}") + + return result + + except ConnectionFailure as e: + print( + f"MongoDB Connection Error: Could not connect to server. Please check connection string and server status. Error: {e}" + ) + except OperationFailure as e: + print( + f"MongoDB Operation Error: An error occurred during a database operation. Error: {e}" + ) + except PyMongoError as e: + print(f"PyMongo Error: An unexpected PyMongo error occurred: {e}") + except Exception as e: + print(f"An unexpected error occurred: {e}") + finally: + # Ensure the client connection is closed + if client: + client.close() + print("MongoDB connection closed.") + return None + + +# --- Main execution logic --- +if __name__ == "__main__": + jwt_token = login_api() + + if jwt_token: + # Call add_email_body_to_api only if necessary, e.g., if you're populating the DB + # For a regular report sending script, you might only need to get_email_bodies_from_api + email_objects = get_email_bodies_from_api(jwt_token) + + if email_objects: + smtp_connection = login_smtp() + if smtp_connection: + for item in email_objects: + receivers = item.get("receivers") + subject = item.get("subject") + body = item.get("body") + + if receivers and subject and body: + send_success = send_email( + smtp_connection, receivers, subject, body + ) + if send_success: + try: + # Ensure you have an actual _id from your database for this to work + document_id_string = item.get("id") + query_by_id = {"_id": ObjectId(document_id_string)} + update_by_id_values = {"$set": {"IsSent": True}} + update_mongodb_document( + query_by_id, update_by_id_values + ) + except Exception as e: + print( + f"Error in Example 5: Make sure the _id string is valid and exists. {e}" + ) + else: + print( + f"Skipping email due to missing data in API response: {item}" + ) + smtp_connection.quit() # Ensure SMTP connection is closed after all emails are sent + print("SMTP connection closed.") + else: + print("Failed to establish SMTP connection. Cannot send emails.") + else: + print("No email bodies retrieved from API to send.") + else: + print("Failed to obtain JWT token. Cannot proceed with email operations.") diff --git a/mailling/store_project_report_email.py b/mailling/store_project_report_email.py new file mode 100644 index 0000000..78d57af --- /dev/null +++ b/mailling/store_project_report_email.py @@ -0,0 +1,106 @@ +import json +import requests + +# --- Configuration --- +def load_config_from_json(file_path="config.json"): + try: + with open(file_path, 'r', encoding='utf-8') as f: + config = json.load(f) + print(f"Configuration loaded from {file_path}") + return config + except FileNotFoundError: + print(f"Error: Configuration file '{file_path}' not found.") + exit(1) + except json.JSONDecodeError: + print(f"Error: Could not decode JSON from '{file_path}'. Check file format.") + exit(1) + except Exception as e: + print(f"An unexpected error occurred while loading config: {e}") + exit(1) + +# Load configuration at the start +CONFIG = load_config_from_json() +# Access variables from the nested configuration +# Accessing API section +API_CONFIG = CONFIG.get('API', {}) # Use .get() with a default empty dict to prevent KeyError if 'API' is missing +BASE_URL = API_CONFIG.get('BASE_URL') +API_USERNAME = API_CONFIG.get('USERNAME') +API_PASSWORD = API_CONFIG.get('PASSWORD') + +def login_api(): + """ + Logs into the API and returns the JWT token. + Handles potential request errors. + """ + payload = { + "username": API_USERNAME, + "password": API_PASSWORD + } + + headers = { + "Content-Type": "application/json" + } + + try: + response = requests.post(f"{BASE_URL}/auth/login", json=payload, headers=headers) + response.raise_for_status() # Raise an exception for HTTP errors (4xx or 5xx) + data = response.json()['data'] + jwt = data["token"] + print("API login successful.") + return jwt + except requests.exceptions.HTTPError as e: + print(f"HTTP Error during API login: {e}") + print(f"Response: {response.text}") + return None + except requests.exceptions.ConnectionError as e: + print(f"Connection Error during API login: {e}") + return None + except requests.exceptions.Timeout as e: + print(f"Timeout Error during API login: {e}") + return None + except requests.exceptions.RequestException as e: + print(f"An unexpected error occurred during API login: {e}") + return None + except KeyError: + print("API response missing 'data' or 'token' key.") + return None + +def add_email_body_to_api(jwt): + """ + Adds email body data to the API. + """ + if not jwt: + print("No JWT token available to add email body.") + return None + + headers = { + "Authorization": f"Bearer {jwt}", + "Content-Type": "application/json" + } + try: + # Assuming this POST request adds new mail records to the database. + # If it's idempotent (can be called multiple times without issues), it's fine. + # Otherwise, consider if this should only be called once or based on specific logic. + response = requests.post(f"{BASE_URL}/report/add-report-mail", headers=headers) + response.raise_for_status() + print("Email body successfully added to API.") + return response + except requests.exceptions.HTTPError as e: + print(f"HTTP Error when adding email body: {e}") + print(f"Response: {response.text}") + return None + except requests.exceptions.RequestException as e: + print(f"An error occurred when adding email body: {e}") + return None + + +# --- Main execution logic --- +if __name__ == "__main__": + jwt_token = login_api() + + if jwt_token: + # Call add_email_body_to_api only if necessary, e.g., if you're populating the DB + # For a regular report sending script, you might only need to get_email_bodies_from_api + add_response = add_email_body_to_api(jwt_token) + else: + print("Failed to obtain JWT token. Cannot proceed with email operations.") \ No newline at end of file