cteward-ng/cteward_ng/database.py
2026-06-06 12:04:59 +02:00

205 lines
5.2 KiB
Python

"""Database connectivity and query execution.
Replaces database.js:
- pyodbc connection pool (via DBUtils)
- Health check (checkBackendOkay)
- Parameterized query execution (runquery)
- All SQL statement definitions
- Stats query aggregations
"""
import logging
logger = logging.getLogger(__name__)
# TODO Phase 2: import pyodbc, DBUtils.PooledDB
# import pyodbc
# from DBUtils.PooledDB import PooledDB
# Module-level connection pool
_pool = None
def init(config=None):
"""Initialize the MSSQL connection pool.
Replaces database.init() from database.js.
Uses the same config keys: user, password, server, port, database.
"""
global _pool
# TODO Phase 2: create PooledDB
raise NotImplementedError("Database not yet initialized")
def connected():
"""Check if the connection pool is alive."""
# TODO Phase 2
raise NotImplementedError
def check_backend_okay():
"""Health check: verify DB is reachable and has expected data.
Replaces checkBackendOkay() from database.js.
"""
# TODO Phase 2
raise NotImplementedError
def run_query(query_def, params):
"""Execute a parameterized query and return rows as list of dicts.
Replaces runquery() from database.js.
Args:
query_def: dict with 'statement' (str) and 'params' (dict of names)
or 'special' (str) for stats queries
params: dict of parameter values
Returns:
list of dict rows
"""
# TODO Phase 2
raise NotImplementedError
def run_query_stats_members():
"""Special aggregation query for member count over time."""
# TODO Phase 2
raise NotImplementedError
def run_query_stats_contracts():
"""Special aggregation query for contract statistics."""
# TODO Phase 2
raise NotImplementedError
def run_query_stats_genders():
"""Special aggregation query for gender demographics."""
# TODO Phase 2
raise NotImplementedError
def run_query_stats_ages():
"""Special aggregation query for age demographics."""
# TODO Phase 2
raise NotImplementedError
def member_lookup(crewname):
"""Look up a single member by crewname.
Replaces memberlookup() from database.js.
Returns a single dict row or raises.
"""
# TODO Phase 2
raise NotImplementedError
# ─── SQL Statement Definitions ───────────────────────────────────────────────
# Replacing the QUERY_* constants from database.js
# Parameter placeholders use pyodbc '?' style instead of T-SQL '@name'
QUERY_CONTRACTLIST_BY_CREWNAME = {
'statement': (
'SELECT MgVert.* FROM Adresse, MgVert '
'WHERE Adresse.Kurzname = ? AND MgVert.AdrNr = Adresse.AdrNr'
),
'params': ['crewname'],
}
QUERY_CONTRACT_BY_CREWNAME_AND_CONTRACT = {
'statement': (
'SELECT MgVert.* FROM Adresse, MgVert '
'WHERE Adresse.Kurzname = ? AND MgVert.AdrNr = Adresse.AdrNr '
"AND (MgVert.VertragNr = ? OR MgVert.VertragNr = ' ' + ?)"
),
'params': ['crewname', 'contract'],
}
QUERY_DEBITLIST_BY_CREWNAME = {
'statement': (
'SELECT MgSolln.* FROM Adresse, MgSolln '
'WHERE Adresse.Kurzname = ? AND MgSolln.AdrNr = Adresse.AdrNr '
'ORDER BY MgSolln.Jahr, MgSolln.Zeitraum'
),
'params': ['crewname'],
}
QUERY_DEBIT_BY_CREWNAME_AND_GUID = {
'statement': (
'SELECT MgSolln.* FROM Adresse, MgSolln '
'WHERE Adresse.Kurzname = ? AND MgSolln.AdrNr = Adresse.AdrNr '
'AND MgSolln.GUID = ?'
),
'params': ['crewname', 'guid'],
}
QUERY_MEMBERLIST = {
'statement': (
"SELECT AdrNr, Firma4, Nachname, Vorname, Kurzname, Kennung3, "
"Telefon3, Kontaktwoher, Eintritt, Austritt "
"FROM Adresse WHERE Kurzname != '' ORDER BY Nachname"
),
'params': [],
}
QUERY_STATS_MEMBERS = {
'special': 'QUERY_STATS_MEMBERS',
}
QUERY_STATS_CONTRACTS = {
'special': 'QUERY_STATS_CONTRACTS',
}
QUERY_STATS_GENDERS = {
'special': 'QUERY_STATS_GENDERS',
}
QUERY_STATS_AGES = {
'special': 'QUERY_STATS_AGES',
}
QUERY_MEMBERLIST_RAW = {
'statement': 'SELECT * FROM Adresse ORDER BY Nachname',
'params': [],
}
QUERY_MEMBER_BY_CREWNAME = {
'statement': 'SELECT * FROM Adresse WHERE Kurzname = ?',
'params': ['crewname'],
}
QUERY_MEMBER_MEMO_BY_CREWNAME = {
'statement': (
'SELECT Memof.* FROM Adresse, Memof '
'WHERE Adresse.Kurzname = ? AND Memof.AdrNr = Adresse.AdrNr'
),
'params': ['crewname'],
}
QUERY_WITHDRAWALLIST_BY_CREWNAME = {
'statement': (
'SELECT MgLast.* FROM Adresse, MgLast '
'WHERE Adresse.Kurzname = ? AND MgLast.Adr_Nummer = Adresse.AdrNr'
),
'params': ['crewname'],
}
QUERY_WITHDRAWAL_BY_CREWNAME_AND_GUID = {
'statement': (
'SELECT MgLast.* FROM Adresse, MgLast '
'WHERE Adresse.Kurzname = ? AND MgLast.Adr_Nummer = Adresse.AdrNr '
'AND MgLast.GUID = ?'
),
'params': ['crewname', 'guid'],
}
QUERY_PAYMENTLIST_BY_CREWNAME = {
'statement': (
'SELECT F5bew4.* FROM Adresse, F5bew4 '
'WHERE Adresse.Kurzname = ? AND F5bew4.AdrNr = Adresse.AdrNr'
),
'params': ['crewname'],
}