Use masterdata project scope column for assignment disambiguation

This commit is contained in:
Rob committed 2026-04-21 16:41:59 +02:00
1 parent bd2c0d512a
commit 4dd320878d
5 files changed
+36 -16

No files matched your search

+4 -3
View File
@@ -41,9 +41,10 @@
"antragsnummer": "B",
"name": "C",
"qualifikation": "D",
"stellenanteil": "E",
"funktion": "F",
"taetigkeit": "G"
"project_scope": "E",
"stellenanteil": "F",
"funktion": "G",
"taetigkeit": "H"
},
"decimal": {
"decimal_separator": ",",
+19 -8
View File
@@ -46,12 +46,23 @@ def _name_keys(name: str) -> set[str]:
def _disambiguate_by_project_key(
candidates: list,
candidates: list[MasterDataRecord],
project_key: str,
) -> object:
"""Return the single candidate whose Funktion contains the project key, or None."""
key = project_key.strip().casefold()
matched = [c for c in candidates if key in c.funktion.casefold()]
) -> MasterDataRecord | None:
"""Return the single candidate matching allocation project key via masterdata project_scope."""
parts = [
token.strip().casefold()
for token in re.split(r"[/,;|]", project_key)
if token.strip()
]
project_tokens = set(parts or [project_key.strip().casefold()])
matched = []
for candidate in candidates:
candidate_scope = (candidate.project_scope or "PS").strip().casefold()
if candidate_scope in project_tokens:
matched.append(candidate)
return matched[0] if len(matched) == 1 else None
@@ -105,7 +116,7 @@ def match_assignments(
if len(candidates) == 1:
selected = candidates[0]
else:
# Multiple candidates: disambiguate by project key in Funktion
# Multiple candidates: disambiguate by explicit project scope column.
selected = _disambiguate_by_project_key(candidates, allocation.project_key)
if selected is None:
issues.append(
@@ -114,8 +125,8 @@ def match_assignments(
message=(
f"Ambiguous master-data for '{allocation.employee_name}' / "
f"'{allocation.project_key}': {len(candidates)} rows, "
f"none or multiple match Funktion. "
f"Funktionen: {[c.funktion for c in candidates]}"
f"none or multiple match project_scope. "
f"project_scope values: {[c.project_scope for c in candidates]}"
),
severity="error",
context=allocation.employee_name,
+1
View File
@@ -19,6 +19,7 @@ class MasterDataRecord:
antragsnummer: str
name: str
qualifikation: str
project_scope: str
stellenanteil_percent: float
funktion: str
taetigkeit: str
+11 -5
View File
@@ -243,6 +243,7 @@ def parse_masterdata_records(
antragsnummer_col = column_index_from_string(columns_cfg["antragsnummer"])
name_col = column_index_from_string(columns_cfg["name"])
qualifikation_col = column_index_from_string(columns_cfg["qualifikation"])
project_scope_col = column_index_from_string(columns_cfg["project_scope"])
stellenanteil_col = column_index_from_string(columns_cfg["stellenanteil"])
funktion_col = column_index_from_string(columns_cfg["funktion"])
taetigkeit_col = column_index_from_string(columns_cfg["taetigkeit"])
@@ -254,12 +255,16 @@ def parse_masterdata_records(
if not name:
continue
stellenanteil = _parse_german_decimal(
worksheet.cell(row=row_index, column=stellenanteil_col).value,
field_name=f"stellenanteil row {row_index}",
decimal_separator=decimal_cfg.get("decimal_separator", ","),
thousands_separator=decimal_cfg.get("thousands_separator", "."),
stellenanteil_raw = worksheet.cell(row=row_index, column=stellenanteil_col).value
stellenanteil = _to_float_or_none(stellenanteil_raw)
if stellenanteil is None:
stellenanteil = 0.0
project_scope = _to_text(
worksheet.cell(row=row_index, column=project_scope_col).value
)
if not project_scope:
project_scope = "PS"
records.append(
MasterDataRecord(
@@ -271,6 +276,7 @@ def parse_masterdata_records(
qualifikation=_to_text(
worksheet.cell(row=row_index, column=qualifikation_col).value
),
project_scope=project_scope,
stellenanteil_percent=stellenanteil,
funktion=_to_text(worksheet.cell(row=row_index, column=funktion_col).value),
taetigkeit=_to_text(
+1
View File
@@ -132,6 +132,7 @@ def validate_sources(
master_cols["antragsnummer"],
master_cols["name"],
master_cols["qualifikation"],
master_cols["project_scope"],
master_cols["stellenanteil"],
master_cols["funktion"],
master_cols["taetigkeit"],