fix: remove patient names from coding template and restrict to admin

- Remove Nachname/Vorname columns from ICD coding template (DSGVO)
- Restrict /cases/coding-template endpoint to admin-only
- ICD import reads last column for backwards compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
CCS Admin 2026-02-26 17:20:28 +00:00
parent 90c121d58d
commit 56c0c6e662
3 changed files with 20 additions and 21 deletions

View file

@ -126,7 +126,7 @@ def download_coding_template(
fallgruppe: Optional[str] = Query(None),
db: Session = Depends(get_db),
):
"""Download an Excel template for ICD coding.
"""Download an Excel template for ICD coding. Admin only.
Supports both ``Authorization: Bearer`` header and ``?token=`` query
parameter for direct browser downloads.
@ -149,6 +149,8 @@ def download_coding_template(
user = db.query(User).filter(User.id == user_id, User.is_active == True).first() # noqa: E712
if not user:
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "User not found")
if user.role != "admin":
raise HTTPException(status.HTTP_403_FORBIDDEN, "Admin access required")
xlsx_bytes = generate_coding_template(db, jahr=jahr, fallgruppe=fallgruppe)
from io import BytesIO

View file

@ -107,7 +107,9 @@ def generate_coding_template(
"""Generate an Excel template for ICD coding.
Returns .xlsx bytes with columns:
Case_ID, Fall_ID, Nachname, Vorname, Fallgruppe, Datum, ICD (empty)
Case_ID, Fall_ID, Fallgruppe, Datum, ICD (empty)
Patient names are excluded for data privacy (DSGVO).
"""
cases, _ = get_pending_icd_cases(
db, jahr=jahr, fallgruppe=fallgruppe, page=1, per_page=10000
@ -118,7 +120,7 @@ def generate_coding_template(
ws.title = "ICD Coding"
# Header
headers = ["Case_ID", "Fall_ID", "Nachname", "Vorname", "Fallgruppe", "Datum", "ICD"]
headers = ["Case_ID", "Fall_ID", "Fallgruppe", "Datum", "ICD"]
for col, header in enumerate(headers, start=1):
ws.cell(row=1, column=col, value=header)
@ -126,11 +128,9 @@ def generate_coding_template(
for i, case in enumerate(cases, start=2):
ws.cell(row=i, column=1, value=case.id)
ws.cell(row=i, column=2, value=case.fall_id)
ws.cell(row=i, column=3, value=case.nachname)
ws.cell(row=i, column=4, value=case.vorname)
ws.cell(row=i, column=5, value=case.fallgruppe)
ws.cell(row=i, column=6, value=case.datum.isoformat() if case.datum else "")
# Column 7 (ICD) left empty for DAK to fill in
ws.cell(row=i, column=3, value=case.fallgruppe)
ws.cell(row=i, column=4, value=case.datum.isoformat() if case.datum else "")
# Column 5 (ICD) left empty for admin to fill in
# Auto-width
for col in ws.columns:
@ -145,7 +145,7 @@ def generate_coding_template(
def import_icd_from_xlsx(db: Session, content: bytes, user_id: int) -> dict:
"""Import ICD codes from a filled-in coding template Excel file.
Expects columns: Case_ID (col 1), ICD (col 7 or last col)
Expects columns: Case_ID (col 1), ICD (last col col 5 or col 7)
Returns: {"updated": int, "errors": list[str]}
"""
wb = load_workbook(BytesIO(content), read_only=True)
@ -164,10 +164,11 @@ def import_icd_from_xlsx(db: Session, content: bytes, user_id: int) -> dict:
except (ValueError, TypeError):
continue
# Find ICD column (column 7)
# Find ICD column: last column (col 5 in new template, col 7 in legacy)
icd_value = None
if len(row) >= 7 and row[6].value:
icd_value = str(row[6].value).strip()
last_idx = len(row) - 1
if last_idx >= 0 and row[last_idx].value:
icd_value = str(row[last_idx].value).strip()
if not icd_value:
continue

View file

@ -73,24 +73,20 @@ class TestGenerateCodingTemplate:
ws = wb.active
assert ws.title == "ICD Coding"
# Verify header row
# Verify header row (no patient names for DSGVO)
headers = [cell.value for cell in ws[1]]
assert headers == [
"Case_ID",
"Fall_ID",
"Nachname",
"Vorname",
"Fallgruppe",
"Datum",
"ICD",
]
# Verify data row
# Verify data row (no nachname/vorname)
row2 = [cell.value for cell in ws[2]]
assert row2[0] == 1
assert row2[1] == "FALL-001"
assert row2[2] == "Mustermann"
assert row2[3] == "Max"
assert row2[4] == "onko"
assert row2[5] == "2026-02-24"
assert row2[6] is None # ICD column should be empty
assert row2[2] == "onko"
assert row2[3] == "2026-02-24"
assert row2[4] is None # ICD column should be empty