refactor: code quality improvements across the backend
All checks were successful
Deploy to K8s / deploy (push) Successful in 13s
Build and Push / build (push) Successful in 32s

- Remove insecure SSL verification bypass in attendance.py
- Add gunicorn as production WSGI server (Dockerfile + entrypoint)
- Fix silent data loss in reconciliation (log + surface unmatched members)
- Add required column validation in payment sheet parsing
- Add input validation on /qr route (account format, amount bounds, SPD injection)
- Centralize configuration into scripts/config.py
- Extract credentials path to env-configurable constant
- Hide unmatched transactions from reconcile-juniors page
- Fix test mocks to bypass cache layer (all 8 tests now pass reliably)
- Add pytest + pytest-cov dev dependencies
- Fix typo "Inffering" in infer_payments.py
- Update CLAUDE.md to reflect current project state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 11:40:32 +01:00
parent 0d0c2af778
commit 033349cafa
13 changed files with 293 additions and 88 deletions

View File

@@ -1,29 +1,17 @@
import json
import os
import socket
import logging
from datetime import datetime
from pathlib import Path
from google.oauth2 import service_account
from googleapiclient.discovery import build
from config import (
CACHE_DIR, CREDENTIALS_PATH as CREDS_PATH, DRIVE_TIMEOUT,
CACHE_TTL_SECONDS, CACHE_API_CHECK_TTL_SECONDS, CACHE_SHEET_MAP,
)
logger = logging.getLogger(__name__)
# Constants
CACHE_DIR = Path(__file__).parent.parent / "tmp"
CREDS_PATH = Path(__file__).parent.parent / ".secret" / "fuj-management-bot-credentials.json"
DRIVE_TIMEOUT = 10 # seconds
CACHE_TTL_SECONDS = int(os.environ.get("CACHE_TTL_SECONDS", 300)) # 30 min default for max cache age
CACHE_API_CHECK_TTL_SECONDS = int(os.environ.get("CACHE_API_CHECK_TTL_SECONDS", 300)) # 5 min default
# Known mappings mapping "cache name" to Google Sheet ID
CACHE_SHEET_MAP = {
"attendance_regular": "1E2e_gT_K5AwSRCDLDTa2UetZTkHmBOcz0kFbBUNUNBA",
"attendance_juniors": "1E2e_gT_K5AwSRCDLDTa2UetZTkHmBOcz0kFbBUNUNBA",
"exceptions_dict": "1Om0YPoDVCH5cV8BrNz5LG5eR5MMU05ypQC7UMN1xn_Y",
"payments_transactions": "1Om0YPoDVCH5cV8BrNz5LG5eR5MMU05ypQC7UMN1xn_Y"
}
# Global state to track last Drive API check time per sheet
_LAST_CHECKED = {}
_DRIVE_SERVICE = None
@@ -87,7 +75,7 @@ def get_sheet_modified_time(cache_key: str) -> str | None:
# 2. Check if the cache file is simply too new (legacy check)
if CACHE_TTL_SECONDS > 0 and cache_file.exists():
try:
file_mtime = os.path.getmtime(cache_file)
file_mtime = cache_file.stat().st_mtime
if time.time() - file_mtime < CACHE_TTL_SECONDS:
with open(cache_file, "r", encoding="utf-8") as f:
cache_data = json.load(f)