- Introduced a new configuration file `.cursorrules` to define coding standards, error handling, testing requirements, and project-specific guidelines. - Refactored `config.py` to implement a `Settings` dataclass for better management of environment variables, improving testability and maintainability. - Updated the import duty schedule handler to utilize session management with `session_scope`, ensuring proper database session handling. - Enhanced the import service to streamline the duty schedule import process, improving code organization and readability. - Added new service layer functions to encapsulate business logic related to group duty pinning and duty schedule imports. - Updated README documentation to reflect the new configuration structure and improved import functionality.
58 lines
1.7 KiB
Python
58 lines
1.7 KiB
Python
"""SQLAlchemy engine and session factory.
|
|
|
|
Engine and session factory are cached globally per process. Only one DATABASE_URL
|
|
is effectively used for the process lifetime. Using a different URL later (e.g. in
|
|
tests with in-memory SQLite) would still use the first engine. To use a different
|
|
URL in tests, set env (e.g. DATABASE_URL) before the first import of this module, or
|
|
clear _engine and _SessionLocal in test fixtures. Prefer session_scope() for all
|
|
callers so sessions are always closed and rolled back on error.
|
|
"""
|
|
|
|
from contextlib import contextmanager
|
|
from typing import Generator
|
|
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import Session, sessionmaker
|
|
|
|
|
|
_engine = None
|
|
_SessionLocal = None
|
|
|
|
|
|
@contextmanager
|
|
def session_scope(database_url: str) -> Generator[Session, None, None]:
|
|
"""Context manager: yields a session, rolls back on exception, closes on exit."""
|
|
session = get_session(database_url)
|
|
try:
|
|
yield session
|
|
except Exception:
|
|
session.rollback()
|
|
raise
|
|
finally:
|
|
session.close()
|
|
|
|
|
|
def get_engine(database_url: str):
|
|
global _engine
|
|
if _engine is None:
|
|
_engine = create_engine(
|
|
database_url,
|
|
connect_args={"check_same_thread": False}
|
|
if "sqlite" in database_url
|
|
else {},
|
|
echo=False,
|
|
)
|
|
return _engine
|
|
|
|
|
|
def get_session_factory(database_url: str) -> sessionmaker[Session]:
|
|
global _SessionLocal
|
|
if _SessionLocal is None:
|
|
engine = get_engine(database_url)
|
|
_SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
return _SessionLocal
|
|
|
|
|
|
def get_session(database_url: str) -> Session:
|
|
return get_session_factory(database_url)()
|