This commit is contained in:
Leon
2025-07-15 22:54:35 +02:00
commit f7eda17284
89 changed files with 18535 additions and 0 deletions

View File

@@ -0,0 +1 @@
"""CRUD operations for database models."""

View File

@@ -0,0 +1,36 @@
from sqlalchemy.orm import Session
from app.core.logging import get_logger
from app.models.entries import Entry
from app.schemas.entries import EntryCreate
logger = get_logger(__name__)
def get_entries_by_newsletter(
db: Session, newsletter_id: int, skip: int = 0, limit: int = 100
):
"""Retrieve entries for a specific newsletter."""
logger.debug(
f"Querying entries for newsletter_id={newsletter_id}, skip={skip}, limit={limit}"
)
return (
db.query(Entry)
.filter(Entry.newsletter_id == newsletter_id)
.offset(skip)
.limit(limit)
.all()
)
def create_entry(db: Session, entry: EntryCreate, newsletter_id: int):
"""Create a new entry for a newsletter."""
logger.info(
f"Creating new entry for newsletter_id={newsletter_id} with subject '{entry.subject}'"
)
db_entry = Entry(**entry.model_dump(), newsletter_id=newsletter_id)
db.add(db_entry)
db.commit()
db.refresh(db_entry)
logger.info(f"Successfully created entry with id={db_entry.id}")
return db_entry

View File

@@ -0,0 +1,106 @@
from sqlalchemy import func
from sqlalchemy.orm import Session
from app.core.logging import get_logger
from app.models.entries import Entry
from app.models.newsletters import Newsletter, Sender
from app.schemas.newsletters import NewsletterCreate, NewsletterUpdate
logger = get_logger(__name__)
def get_newsletter(db: Session, newsletter_id: int):
"""Retrieve a single newsletter by its ID."""
logger.debug(f"Querying for newsletter with id={newsletter_id}")
result = (
db.query(Newsletter, func.count(Entry.id))
.outerjoin(Entry, Newsletter.id == Entry.newsletter_id)
.filter(Newsletter.id == newsletter_id)
.group_by(Newsletter.id)
.first()
)
if result:
newsletter, count = result
newsletter.entries_count = count
return newsletter
return None
def get_newsletters(db: Session, skip: int = 0, limit: int = 100):
"""Retrieve a list of newsletters."""
logger.debug(f"Querying for newsletters with skip={skip}, limit={limit}")
results = (
db.query(Newsletter, func.count(Entry.id))
.outerjoin(Entry, Newsletter.id == Entry.newsletter_id)
.group_by(Newsletter.id)
.order_by(Newsletter.id)
.offset(skip)
.limit(limit)
.all()
)
newsletters_with_count = []
for newsletter, count in results:
newsletter.entries_count = count
newsletters_with_count.append(newsletter)
return newsletters_with_count
def create_newsletter(db: Session, newsletter: NewsletterCreate):
"""Create a new newsletter."""
logger.info(f"Creating new newsletter with name '{newsletter.name}'")
db_newsletter = Newsletter(name=newsletter.name)
db.add(db_newsletter)
db.commit()
db.refresh(db_newsletter)
for email in newsletter.sender_emails:
db_sender = Sender(email=email, newsletter_id=db_newsletter.id)
db.add(db_sender)
db.commit()
db.refresh(db_newsletter)
logger.info(f"Successfully created newsletter with id={db_newsletter.id}")
db_newsletter.entries_count = 0
return db_newsletter
def update_newsletter(
db: Session, newsletter_id: int, newsletter_update: NewsletterUpdate
):
"""Update an existing newsletter."""
logger.info(f"Updating newsletter with id={newsletter_id}")
db_newsletter = db.query(Newsletter).filter(Newsletter.id == newsletter_id).first()
if not db_newsletter:
return None
db_newsletter.name = newsletter_update.name
# Simple approach: delete existing senders and add new ones
for sender in db_newsletter.senders:
db.delete(sender)
db.commit()
for email in newsletter_update.sender_emails:
db_sender = Sender(email=email, newsletter_id=db_newsletter.id)
db.add(db_sender)
db.commit()
logger.info(f"Successfully updated newsletter with id={db_newsletter.id}")
return get_newsletter(db, newsletter_id)
def delete_newsletter(db: Session, newsletter_id: int):
"""Delete a newsletter by its ID."""
logger.info(f"Deleting newsletter with id={newsletter_id}")
db_newsletter = get_newsletter(db, newsletter_id)
if not db_newsletter:
return None
db.delete(db_newsletter)
db.commit()
logger.info(f"Successfully deleted newsletter with id={newsletter_id}")
return db_newsletter

View File

@@ -0,0 +1,98 @@
from sqlalchemy.orm import Session
from app.core.config import settings as env_settings
from app.core.logging import get_logger
from app.models.settings import Settings as SettingsModel
from app.schemas.settings import Settings as SettingsSchema
from app.schemas.settings import SettingsCreate
logger = get_logger(__name__)
def get_settings(db: Session, with_password: bool = False) -> SettingsSchema:
"""Retrieve application settings, prioritizing environment variables over database."""
logger.debug("Querying for settings")
db_settings = db.query(SettingsModel).first()
if not db_settings:
logger.info(
"No settings found in the database, creating new default settings from environment variables."
)
# Get env settings, but only for fields that exist in the DB model
model_fields = {c.name for c in SettingsModel.__table__.columns}
env_data_for_db = {
k: v for k, v in env_settings.model_dump().items() if k in model_fields
}
db_settings = SettingsModel(**env_data_for_db)
db.add(db_settings)
db.commit()
db.refresh(db_settings)
logger.info("Default settings created from environment variables.")
# Build dictionary from DB model attributes, handling possible None values
db_data = {
"id": db_settings.id,
"imap_server": db_settings.imap_server or "",
"imap_username": db_settings.imap_username or "",
"search_folder": db_settings.search_folder,
"move_to_folder": db_settings.move_to_folder,
"mark_as_read": db_settings.mark_as_read,
"email_check_interval": db_settings.email_check_interval,
"auto_add_new_senders": db_settings.auto_add_new_senders,
}
# Get all environment settings that were explicitly set.
env_data = env_settings.model_dump(exclude_unset=True)
# Merge them. env_data takes precedence.
merged_data = {**db_data, **env_data}
# The fields that came from env are the "locked" ones.
locked_fields = list(env_data.keys())
logger.debug(f"Locked fields from environment variables: {locked_fields}")
# Handle password separately for security
if with_password:
logger.debug("Including IMAP password in settings data")
if "imap_password" in locked_fields:
merged_data["imap_password"] = env_settings.imap_password
else:
merged_data["imap_password"] = db_settings.imap_password
elif "imap_password" in merged_data:
# Ensure password is not in the data if not requested
del merged_data["imap_password"]
# Create the final schema object
settings_schema = SettingsSchema.model_validate(merged_data)
settings_schema.locked_fields = locked_fields
return settings_schema
def create_or_update_settings(db: Session, settings: SettingsCreate):
"""Create or update application settings."""
logger.info("Creating or updating settings")
db_settings = db.query(SettingsModel).first()
if not db_settings:
logger.info("No existing settings found, creating new ones.")
db_settings = SettingsModel()
db.add(db_settings)
update_data = settings.model_dump()
# Do not update fields that are set by environment variables
locked_fields = list(env_settings.model_dump(exclude_unset=True).keys())
logger.debug(f"Fields locked by environment variables: {locked_fields}")
for key, value in update_data.items():
if key not in locked_fields:
setattr(db_settings, key, value)
db.commit()
db.refresh(db_settings)
logger.info("Successfully updated settings.")
# Return the updated settings including locked fields for a complete view
return get_settings(db)