From c2f5bf51c8545c8ac5ae3ff51be9c55b16d725e0 Mon Sep 17 00:00:00 2001 From: K-dash Date: Wed, 29 May 2024 18:26:33 +0900 Subject: [PATCH] Refactor environment variable loading using pydantic-settings --- .env.example | 13 +++++++------ requirements.txt | 3 +++ src/cli.py | 4 ++-- src/send_email.py | 34 +++++++++++----------------------- src/server.py | 15 +++++++++------ src/settings.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 77 insertions(+), 37 deletions(-) create mode 100644 src/settings.py diff --git a/.env.example b/.env.example index 913f159..290025d 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,9 @@ PORT=8000 IP_ADDRESS="localhost" -SMTP_SERVER ='smtp.gmail.com' -EMAIL=your-email -EMAIL_PW=email-password -EMAIL_RECEIVER=email-to-receive-surf-report -COMMAND=localhost:8000 -SUBJECT='Surf Report' +SMTP_SERVER="smtp.gmail.com" +SMTP_PORT=587 +EMAIL="your-email" +EMAIL_PW="email-password" +EMAIL_RECEIVER="email-to-receive-surf-report" +COMMAND="localhost:8000" +SUBJECT="Surf Report" diff --git a/requirements.txt b/requirements.txt index dac7bc4..37c872a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,6 @@ python-dotenv==1.0.1 Requests==2.32.2 requests_cache==1.2.0 retry_requests==2.0.0 +pydantic==2.7.1 +pydantic-settings==2.2.1 +email-validator==2.1.1 diff --git a/src/cli.py b/src/cli.py index 121456e..aabd7b2 100644 --- a/src/cli.py +++ b/src/cli.py @@ -2,10 +2,10 @@ Main module """ -import os import sys +from pathlib import Path -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +sys.path.append(str(Path(__file__).parent.parent)) from src import api, helper diff --git a/src/send_email.py b/src/send_email.py index 56bc221..4100fa4 100644 --- a/src/send_email.py +++ b/src/send_email.py @@ -2,33 +2,21 @@ Module to send surf report emails """ -import os import smtplib import subprocess from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText -from dotenv import load_dotenv +from settings import EmailSettings # Load environment variables from .env file -load_dotenv(override=True) - -# Email server configuration -SMTP_SERVER = os.getenv("SMTP_SERVER") -PORT = 587 # Port for TLS connection - -# Sender's email credentials -SENDER_EMAIL = os.getenv("EMAIL") -PASSWORD = os.getenv("EMAIL_PW") - -# Receiver's email -RECEIVER_EMAIL = os.getenv("EMAIL_RECEIVER") +env = EmailSettings() # Create a multipart message and set headers message = MIMEMultipart() -message["From"] = SENDER_EMAIL -message["To"] = RECEIVER_EMAIL -message["Subject"] = os.getenv("SUBJECT") +message["From"] = env.EMAIL +message["To"] = env.EMAIL_RECEIVER +message["Subject"] = env.SUBJECT def send_user_email(): @@ -36,7 +24,7 @@ def send_user_email(): Sends user an email """ SURF = subprocess.run( - ["curl", os.getenv("COMMAND")], + ["curl", env.COMMAND], capture_output=True, text=True, check=True, @@ -48,12 +36,12 @@ def send_user_email(): message.attach(MIMEText(BODY, "plain")) # Connect to the SMTP server - with smtplib.SMTP(SMTP_SERVER, PORT) as server: + with smtplib.SMTP(env.SMTP_SERVER, env.SMTP_PORT) as server: server.starttls() # Secure the connection - server.login(SENDER_EMAIL, PASSWORD) + server.login(env.EMAIL, env.EMAIL_PW) text = message.as_string() - server.sendmail(SENDER_EMAIL, RECEIVER_EMAIL, text) + server.sendmail(env.EMAIL, env.EMAIL_RECEIVER, text) print("Email sent successfully.") - -send_user_email() +if __name__ == "__main__": + send_user_email() diff --git a/src/server.py b/src/server.py index ce4d91d..d3b3390 100644 --- a/src/server.py +++ b/src/server.py @@ -3,11 +3,10 @@ """ import asyncio -import os import subprocess import urllib.parse -from dotenv import dotenv_values, load_dotenv +from pathlib import Path from flask import ( Flask, render_template, @@ -17,8 +16,10 @@ ) from flask_cors import CORS +from settings import ServerSettings + # Load environment variables from .env file -load_dotenv(override=True) +env = ServerSettings() app = Flask(__name__) CORS(app) @@ -26,12 +27,14 @@ @app.route("/help") def serve_help(): - return send_from_directory("../", "help.txt") + return send_from_directory( + f"{str(Path(__file__).parent)}/", "help.txt" + ) @app.route("/home") def serve_index(): - return render_template("index.html", env_vars=dict(dotenv_values())) + return render_template("index.html", env_vars=env.model_dump()) @app.route("/script.js") @@ -78,4 +81,4 @@ async def run_subprocess(): if __name__ == "__main__": - app.run(host="0.0.0.0", port=os.getenv("PORT")) + app.run(host="0.0.0.0", port=env.PORT) diff --git a/src/settings.py b/src/settings.py new file mode 100644 index 0000000..6acfdae --- /dev/null +++ b/src/settings.py @@ -0,0 +1,45 @@ +from pathlib import Path +from typing import Literal, Union + +from pydantic import EmailStr, Field, IPvAnyAddress +from pydantic_settings import BaseSettings, SettingsConfigDict + + +class CommonSettings(BaseSettings): + """ + Base class for defining common settings. + model_config (SettingsConfigDict)Configuration dictionary + for specifying the settings file and encoding. + """ + model_config = SettingsConfigDict( + env_file=f"{Path(__file__).parent.parent}/.env", + env_file_encoding="utf-8", + extra="ignore", + ) + + +class ServerSettings(CommonSettings): + """ + Class for defining server env settings. + """ + + PORT: int = Field(default=8000) + IP_ADDRESS: Union[IPvAnyAddress | Literal["localhost"]] = Field( + default="localhost" + ) + + +class EmailSettings(ServerSettings): + """ + Class for defining email env settings. + """ + + SMTP_SERVER: str = Field(default="smtp.gmail.com") # default gmail server + SMTP_PORT: int = Field(default=587) # default 587 + EMAIL: EmailStr # required + EMAIL_PW: str # required + EMAIL_RECEIVER: EmailStr # required + COMMAND: str = Field( + default_factory=lambda cls: f"{cls.IP_ADDRESS}:{cls.PORT}" + ) + SUBJECT: str = Field(default="Surf Report")