206 lines
5.2 KiB
Python
206 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'],
|
||
|
|
}
|