Set up the project skeleton (MIL-001)
pyproject.toml (Python >=3.13, no runtime dependencies, pytest as dev extra), Python .gitignore, .env.example, constants.py, assignment data files, Doxyfile and a smoke test. Task: MIL-001#1 Task: MIL-001#2 Task: MIL-001#3 Task: MIL-001#4 Refs #2 Refs #3 Refs #4 Refs #5
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
GMAIL_ADDRESS=
|
||||
GMAIL_TOKEN=
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
# Secrets: personal use only, never committed
|
||||
.env
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.egg-info/
|
||||
build/
|
||||
dist/
|
||||
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
|
||||
# Test and tool caches
|
||||
.pytest_cache/
|
||||
.ruff_cache/
|
||||
.mypy_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
# Generated documentation
|
||||
docs/doxygen/
|
||||
|
||||
# Editors and OS
|
||||
.idea/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
@@ -0,0 +1,23 @@
|
||||
# Doxygen configuration for the birthday wisher (Python).
|
||||
# Run from the repository root: doxygen Doxyfile
|
||||
# Output goes to docs/doxygen/ (git-ignored).
|
||||
|
||||
PROJECT_NAME = "Birthday Wisher"
|
||||
PROJECT_BRIEF = "Sends a random birthday letter by e-mail (Udemy 100 Days of Code, day 32)"
|
||||
OUTPUT_DIRECTORY = docs/doxygen
|
||||
INPUT = src README.md
|
||||
FILE_PATTERNS = *.py *.md
|
||||
RECURSIVE = YES
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
|
||||
OPTIMIZE_OUTPUT_JAVA = YES
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_PRIVATE = NO
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
AUTOLINK_SUPPORT = YES
|
||||
SOURCE_BROWSER = YES
|
||||
|
||||
GENERATE_HTML = YES
|
||||
GENERATE_LATEX = NO
|
||||
QUIET = YES
|
||||
WARN_IF_UNDOCUMENTED = YES
|
||||
@@ -0,0 +1,5 @@
|
||||
name,email,year,month,day
|
||||
Alice Example,alice@example.com,1990,3,14
|
||||
Bob Example,bob@example.com,1985,10,17
|
||||
Carol Example,carol@example.com,2001,10,17
|
||||
Dave Example,dave@example.com,1978,12,24
|
||||
|
@@ -0,0 +1,7 @@
|
||||
Dear [NAME],
|
||||
|
||||
Happy birthday!
|
||||
|
||||
All the best for the year!
|
||||
|
||||
Angela
|
||||
@@ -0,0 +1,7 @@
|
||||
Hey [NAME],
|
||||
|
||||
Happy birthday! Have a wonderful time today and eat lots of cake!
|
||||
|
||||
Lots of love,
|
||||
|
||||
Angela
|
||||
@@ -0,0 +1,7 @@
|
||||
Dear [NAME],
|
||||
|
||||
It's your birthday! Have a great day!
|
||||
|
||||
All my love,
|
||||
|
||||
Angela
|
||||
@@ -0,0 +1,22 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=68"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "birthday-wisher"
|
||||
version = "0.1.0"
|
||||
description = "Sends a random birthday letter by e-mail to everyone whose birthday is today (Udemy 100 Days of Code, day 32)."
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
license = { file = "LICENSE" }
|
||||
dependencies = []
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["pytest>=8"]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
pythonpath = ["src"]
|
||||
testpaths = ["tests"]
|
||||
@@ -0,0 +1,4 @@
|
||||
"""!
|
||||
@file __init__.py
|
||||
@brief Birthday wisher: sends a random birthday letter to everyone whose birthday is today.
|
||||
"""
|
||||
@@ -0,0 +1,37 @@
|
||||
"""!
|
||||
@file constants.py
|
||||
@brief Constants of the birthday wisher: file names, placeholder, SMTP settings.
|
||||
"""
|
||||
|
||||
## Name of the CSV file with the people and their birth dates.
|
||||
BIRTHDAYS_FILE: str = "birthdays.csv"
|
||||
|
||||
## Folder with the letter templates.
|
||||
LETTER_TEMPLATES_DIR: str = "letter_templates"
|
||||
|
||||
## File name pattern of a letter template (the number is 1 to LETTER_COUNT).
|
||||
LETTER_FILE_PATTERN: str = "letter_{number}.txt"
|
||||
|
||||
## Number of letter templates the program chooses from.
|
||||
LETTER_COUNT: int = 3
|
||||
|
||||
## Placeholder in a letter that is replaced by the person's name.
|
||||
NAME_PLACEHOLDER: str = "[NAME]"
|
||||
|
||||
## Columns of the CSV file.
|
||||
CSV_COLUMNS: tuple[str, ...] = ("name", "email", "year", "month", "day")
|
||||
|
||||
## SMTP server of Gmail.
|
||||
SMTP_HOST: str = "smtp.gmail.com"
|
||||
|
||||
## SMTP port that expects STARTTLS.
|
||||
SMTP_PORT: int = 587
|
||||
|
||||
## Subject of the e-mail.
|
||||
EMAIL_SUBJECT: str = "Happy Birthday"
|
||||
|
||||
## Environment variable with the sender's e-mail address.
|
||||
ENV_SENDER_ADDRESS: str = "GMAIL_ADDRESS"
|
||||
|
||||
## Environment variable with the sender's app password (token).
|
||||
ENV_SENDER_TOKEN: str = "GMAIL_TOKEN"
|
||||
@@ -0,0 +1,15 @@
|
||||
"""Smoke test: the package imports and the constants are consistent."""
|
||||
|
||||
from birthday_wisher import constants
|
||||
|
||||
|
||||
def test_letter_file_pattern_has_number_placeholder() -> None:
|
||||
assert "{number}" in constants.LETTER_FILE_PATTERN
|
||||
|
||||
|
||||
def test_csv_columns_start_with_name_and_email() -> None:
|
||||
assert constants.CSV_COLUMNS[:2] == ("name", "email")
|
||||
|
||||
|
||||
def test_smtp_port_expects_starttls() -> None:
|
||||
assert constants.SMTP_PORT == 587
|
||||
Reference in New Issue
Block a user