Use masterdata project scope column for assignment disambiguation
This commit is contained in:
1 parent
bd2c0d512a
commit
4dd320878d
5 files changed
+36
-16
No files matched your search
+4
-3
@@ -41,9 +41,10 @@
|
|||||||
"antragsnummer": "B",
|
"antragsnummer": "B",
|
||||||
"name": "C",
|
"name": "C",
|
||||||
"qualifikation": "D",
|
"qualifikation": "D",
|
||||||
"stellenanteil": "E",
|
"project_scope": "E",
|
||||||
"funktion": "F",
|
"stellenanteil": "F",
|
||||||
"taetigkeit": "G"
|
"funktion": "G",
|
||||||
|
"taetigkeit": "H"
|
||||||
},
|
},
|
||||||
"decimal": {
|
"decimal": {
|
||||||
"decimal_separator": ",",
|
"decimal_separator": ",",
|
||||||
|
|||||||
@@ -46,12 +46,23 @@ def _name_keys(name: str) -> set[str]:
|
|||||||
|
|
||||||
|
|
||||||
def _disambiguate_by_project_key(
|
def _disambiguate_by_project_key(
|
||||||
candidates: list,
|
candidates: list[MasterDataRecord],
|
||||||
project_key: str,
|
project_key: str,
|
||||||
) -> object:
|
) -> MasterDataRecord | None:
|
||||||
"""Return the single candidate whose Funktion contains the project key, or None."""
|
"""Return the single candidate matching allocation project key via masterdata project_scope."""
|
||||||
key = project_key.strip().casefold()
|
parts = [
|
||||||
matched = [c for c in candidates if key in c.funktion.casefold()]
|
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
|
return matched[0] if len(matched) == 1 else None
|
||||||
|
|
||||||
|
|
||||||
@@ -105,7 +116,7 @@ def match_assignments(
|
|||||||
if len(candidates) == 1:
|
if len(candidates) == 1:
|
||||||
selected = candidates[0]
|
selected = candidates[0]
|
||||||
else:
|
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)
|
selected = _disambiguate_by_project_key(candidates, allocation.project_key)
|
||||||
if selected is None:
|
if selected is None:
|
||||||
issues.append(
|
issues.append(
|
||||||
@@ -114,8 +125,8 @@ def match_assignments(
|
|||||||
message=(
|
message=(
|
||||||
f"Ambiguous master-data for '{allocation.employee_name}' / "
|
f"Ambiguous master-data for '{allocation.employee_name}' / "
|
||||||
f"'{allocation.project_key}': {len(candidates)} rows, "
|
f"'{allocation.project_key}': {len(candidates)} rows, "
|
||||||
f"none or multiple match Funktion. "
|
f"none or multiple match project_scope. "
|
||||||
f"Funktionen: {[c.funktion for c in candidates]}"
|
f"project_scope values: {[c.project_scope for c in candidates]}"
|
||||||
),
|
),
|
||||||
severity="error",
|
severity="error",
|
||||||
context=allocation.employee_name,
|
context=allocation.employee_name,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ class MasterDataRecord:
|
|||||||
antragsnummer: str
|
antragsnummer: str
|
||||||
name: str
|
name: str
|
||||||
qualifikation: str
|
qualifikation: str
|
||||||
|
project_scope: str
|
||||||
stellenanteil_percent: float
|
stellenanteil_percent: float
|
||||||
funktion: str
|
funktion: str
|
||||||
taetigkeit: str
|
taetigkeit: str
|
||||||
|
|||||||
@@ -243,6 +243,7 @@ def parse_masterdata_records(
|
|||||||
antragsnummer_col = column_index_from_string(columns_cfg["antragsnummer"])
|
antragsnummer_col = column_index_from_string(columns_cfg["antragsnummer"])
|
||||||
name_col = column_index_from_string(columns_cfg["name"])
|
name_col = column_index_from_string(columns_cfg["name"])
|
||||||
qualifikation_col = column_index_from_string(columns_cfg["qualifikation"])
|
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"])
|
stellenanteil_col = column_index_from_string(columns_cfg["stellenanteil"])
|
||||||
funktion_col = column_index_from_string(columns_cfg["funktion"])
|
funktion_col = column_index_from_string(columns_cfg["funktion"])
|
||||||
taetigkeit_col = column_index_from_string(columns_cfg["taetigkeit"])
|
taetigkeit_col = column_index_from_string(columns_cfg["taetigkeit"])
|
||||||
@@ -254,12 +255,16 @@ def parse_masterdata_records(
|
|||||||
if not name:
|
if not name:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
stellenanteil = _parse_german_decimal(
|
stellenanteil_raw = worksheet.cell(row=row_index, column=stellenanteil_col).value
|
||||||
worksheet.cell(row=row_index, column=stellenanteil_col).value,
|
stellenanteil = _to_float_or_none(stellenanteil_raw)
|
||||||
field_name=f"stellenanteil row {row_index}",
|
if stellenanteil is None:
|
||||||
decimal_separator=decimal_cfg.get("decimal_separator", ","),
|
stellenanteil = 0.0
|
||||||
thousands_separator=decimal_cfg.get("thousands_separator", "."),
|
|
||||||
|
project_scope = _to_text(
|
||||||
|
worksheet.cell(row=row_index, column=project_scope_col).value
|
||||||
)
|
)
|
||||||
|
if not project_scope:
|
||||||
|
project_scope = "PS"
|
||||||
|
|
||||||
records.append(
|
records.append(
|
||||||
MasterDataRecord(
|
MasterDataRecord(
|
||||||
@@ -271,6 +276,7 @@ def parse_masterdata_records(
|
|||||||
qualifikation=_to_text(
|
qualifikation=_to_text(
|
||||||
worksheet.cell(row=row_index, column=qualifikation_col).value
|
worksheet.cell(row=row_index, column=qualifikation_col).value
|
||||||
),
|
),
|
||||||
|
project_scope=project_scope,
|
||||||
stellenanteil_percent=stellenanteil,
|
stellenanteil_percent=stellenanteil,
|
||||||
funktion=_to_text(worksheet.cell(row=row_index, column=funktion_col).value),
|
funktion=_to_text(worksheet.cell(row=row_index, column=funktion_col).value),
|
||||||
taetigkeit=_to_text(
|
taetigkeit=_to_text(
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ def validate_sources(
|
|||||||
master_cols["antragsnummer"],
|
master_cols["antragsnummer"],
|
||||||
master_cols["name"],
|
master_cols["name"],
|
||||||
master_cols["qualifikation"],
|
master_cols["qualifikation"],
|
||||||
|
master_cols["project_scope"],
|
||||||
master_cols["stellenanteil"],
|
master_cols["stellenanteil"],
|
||||||
master_cols["funktion"],
|
master_cols["funktion"],
|
||||||
master_cols["taetigkeit"],
|
master_cols["taetigkeit"],
|
||||||
|
|||||||
Reference in new issue
Block a user